Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2bf4603

Browse files
committed
Lift out _SubmoduleAutoImporterModule and register it to guarded_eval.
1 parent 81ac701 commit 2bf4603

2 files changed

Lines changed: 54 additions & 47 deletions

File tree

README.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ As usual, install using pip:
6464
6565
Then, append the output of ``python -m ipython_autoimport`` to the
6666
``ipython_config.py`` file in the directory printed by ``ipython profile
67-
locate`` (typically ``~/.ipython/profile_default/``).
67+
locate`` (typically ``~/.ipython/profile_default/``). If you don't have such a
68+
file at all, first create it with ``ipython profile create``.
6869

69-
If you don't have such a file at all, you can use ``ipython profile create``.
70+
Note that upon loading, ``ipython_autoimport`` will register its submodule
71+
auto-importer to IPython's "limited evalutation" completer policy (on IPython
72+
versions that support it).
7073

7174
Run tests with ``pytest``.
7275

@@ -87,6 +90,6 @@ the builtins dict instead, but that seems a bit too invasive...).
8790

8891
When using Jedi autocompletion (the default if Jedi is installed as of IPython
8992
7.2), trying to tab-complete not-yet-imported global names to trigger an import
90-
fails, because Jedi purposefully converts the global dict to a namespace
93+
failure, because Jedi purposefully converts the global dict to a namespace
9194
object and looks up attributes using ``getattr_static``. Jedi can be disabled
9295
by adding ``c.Completer.use_jedi = False`` to the ``ipython_config.py`` file.

lib/ipython_autoimport.py

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
import token
1616
from types import ModuleType
1717

18+
import IPython.core
1819
from IPython.core import magic
1920
from IPython.core.error import UsageError
20-
from IPython.core.magic import register_line_magic
2121
from IPython.core.magic_arguments import (
2222
argument, magic_arguments, parse_argstring)
2323
from IPython.core.magics.execution import ExecutionMagics
@@ -93,53 +93,54 @@ def _report(ipython, msg):
9393
print("{}Autoimport:{} {}".format(cs[token.NUMBER], cs["normal"], msg))
9494

9595

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.
9998

100-
Implemented as a factory function to close over the real module.
101-
"""
99+
@property
100+
def __dict__(self):
101+
return self.__module.__dict__
102102

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)
105106

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.
110125

111-
# Overriding __setattr__ is needed even when __dict__ is overridden.
112-
def __setattr__(self, name, value):
113-
setattr(module, name, value)
114126

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
143144

144145

145146
class _AutoImporterMap(dict):
@@ -231,6 +232,9 @@ def _install_namespace(ipython):
231232
# (both with and without Jedi).
232233
ipython.user_ns = ipython.Completer.namespace = (
233234
_AutoImporterMap(ipython))
235+
if hasattr(IPython.core, "guarded_eval"):
236+
(IPython.core.guarded_eval.EVALUATION_POLICIES["limited"]
237+
.allowed_getattr.add(_SubmoduleAutoImporterModule))
234238

235239

236240
def _uninstall_namespace(ipython):
@@ -266,7 +270,7 @@ def autoimport(arg):
266270
def load_ipython_extension(ipython):
267271
_install_namespace(ipython)
268272
ipython.register_magics(_PatchedMagics) # Add warning to timing magics.
269-
register_line_magic(autoimport)
273+
magic.register_line_magic(autoimport)
270274

271275

272276
def unload_ipython_extension(ipython):

0 commit comments

Comments
 (0)