|
15 | 15 | import token |
16 | 16 | from types import ModuleType |
17 | 17 |
|
| 18 | +import IPython.core |
18 | 19 | from IPython.core import magic |
19 | 20 | from IPython.core.error import UsageError |
20 | | -from IPython.core.magic import register_line_magic |
21 | 21 | from IPython.core.magic_arguments import ( |
22 | 22 | argument, magic_arguments, parse_argstring) |
23 | 23 | from IPython.core.magics.execution import ExecutionMagics |
@@ -93,53 +93,54 @@ def _report(ipython, msg): |
93 | 93 | print("{}Autoimport:{} {}".format(cs[token.NUMBER], cs["normal"], msg)) |
94 | 94 |
|
95 | 95 |
|
96 | | -def _make_submodule_autoimporter_module(ipython, module): |
97 | | - """ |
98 | | - Return a module sub-instance that automatically imports submodules. |
| 96 | +class _SubmoduleAutoImporterModule(ModuleType): |
| 97 | + # __module and __ipython are set externally to not modify the constructor. |
99 | 98 |
|
100 | | - Implemented as a factory function to close over the real module. |
101 | | - """ |
| 99 | + @property |
| 100 | + def __dict__(self): |
| 101 | + return self.__module.__dict__ |
102 | 102 |
|
103 | | - if not hasattr(module, "__path__"): # We only need to wrap packages. |
104 | | - return module |
| 103 | + # Overriding __setattr__ is needed even when __dict__ is overridden. |
| 104 | + def __setattr__(self, name, value): |
| 105 | + setattr(self.__module, name, value) |
105 | 106 |
|
106 | | - class SubmoduleAutoImporterModule(ModuleType): |
107 | | - @property |
108 | | - def __dict__(self): |
109 | | - return module.__dict__ |
| 107 | + def __getattr__(self, name): |
| 108 | + try: |
| 109 | + value = getattr(self.__module, name) |
| 110 | + if isinstance(value, ModuleType): |
| 111 | + value = _make_submodule_autoimporter_module( |
| 112 | + self.__ipython, value) |
| 113 | + return value |
| 114 | + except AttributeError: |
| 115 | + import_target = "{}.{}".format(self.__name__, name) |
| 116 | + try: |
| 117 | + submodule = importlib.import_module(import_target) |
| 118 | + except getattr(builtins, "ModuleNotFoundError", ImportError): |
| 119 | + pass # Py<3.6. |
| 120 | + else: |
| 121 | + _report(self.__ipython, "import {}".format(import_target)) |
| 122 | + return _make_submodule_autoimporter_module( |
| 123 | + self.__ipython, submodule) |
| 124 | + raise # Raise AttributeError without chaining ImportError. |
110 | 125 |
|
111 | | - # Overriding __setattr__ is needed even when __dict__ is overridden. |
112 | | - def __setattr__(self, name, value): |
113 | | - setattr(module, name, value) |
114 | 126 |
|
115 | | - def __getattr__(self, name): |
116 | | - try: |
117 | | - value = getattr(module, name) |
118 | | - if isinstance(value, ModuleType): |
119 | | - value = _make_submodule_autoimporter_module(ipython, |
120 | | - value) |
121 | | - return value |
122 | | - except AttributeError: |
123 | | - import_target = "{}.{}".format(self.__name__, name) |
124 | | - try: |
125 | | - submodule = importlib.import_module(import_target) |
126 | | - except getattr(builtins, "ModuleNotFoundError", |
127 | | - ImportError): # Py<3.6. |
128 | | - pass |
129 | | - else: |
130 | | - _report(ipython, "import {}".format(import_target)) |
131 | | - return _make_submodule_autoimporter_module( |
132 | | - ipython, submodule) |
133 | | - raise # Raise AttributeError without chaining ImportError. |
134 | | - |
135 | | - sai_module = SubmoduleAutoImporterModule(module.__name__) |
136 | | - # Apparently, `module?` does not trigger descriptors, so we need to |
137 | | - # set the docstring explicitly (on the instance, not on the class). |
138 | | - # Then then only difference in the output of `module?` becomes the type |
139 | | - # (`SubmoduleAutoImportModule` instead of `module`), which we should keep |
140 | | - # for clarity. |
141 | | - ModuleType.__setattr__(sai_module, "__doc__", module.__doc__) |
142 | | - return sai_module |
| 127 | +def _make_submodule_autoimporter_module(ipython, module): |
| 128 | + """Return a module sub-instance that automatically imports submodules.""" |
| 129 | + if not hasattr(module, "__path__"): # We only need to wrap packages. |
| 130 | + return module |
| 131 | + saim = _SubmoduleAutoImporterModule(module.__name__) |
| 132 | + for k, v in [ |
| 133 | + ("_SubmoduleAutoImporterModule__module", module), |
| 134 | + ("_SubmoduleAutoImporterModule__ipython", ipython), |
| 135 | + # Apparently, `module?` does not trigger descriptors, so we need to |
| 136 | + # set the docstring explicitly (on the instance, not on the class). |
| 137 | + # Then then only difference in the output of `module?` becomes the |
| 138 | + # type (`SubmoduleAutoImportModule` instead of `module`), which we |
| 139 | + # should keep for clarity. |
| 140 | + ("__doc__", module.__doc__), |
| 141 | + ]: |
| 142 | + ModuleType.__setattr__(saim, k, v) |
| 143 | + return saim |
143 | 144 |
|
144 | 145 |
|
145 | 146 | class _AutoImporterMap(dict): |
@@ -231,6 +232,9 @@ def _install_namespace(ipython): |
231 | 232 | # (both with and without Jedi). |
232 | 233 | ipython.user_ns = ipython.Completer.namespace = ( |
233 | 234 | _AutoImporterMap(ipython)) |
| 235 | + if hasattr(IPython.core, "guarded_eval"): |
| 236 | + (IPython.core.guarded_eval.EVALUATION_POLICIES["limited"] |
| 237 | + .allowed_getattr.add(_SubmoduleAutoImporterModule)) |
234 | 238 |
|
235 | 239 |
|
236 | 240 | def _uninstall_namespace(ipython): |
@@ -266,7 +270,7 @@ def autoimport(arg): |
266 | 270 | def load_ipython_extension(ipython): |
267 | 271 | _install_namespace(ipython) |
268 | 272 | ipython.register_magics(_PatchedMagics) # Add warning to timing magics. |
269 | | - register_line_magic(autoimport) |
| 273 | + magic.register_line_magic(autoimport) |
270 | 274 |
|
271 | 275 |
|
272 | 276 | def unload_ipython_extension(ipython): |
|
0 commit comments