diff options
author | Brett Cannon <brett@python.org> | 2016-06-25 17:58:17 (GMT) |
---|---|---|
committer | Brett Cannon <brett@python.org> | 2016-06-25 17:58:17 (GMT) |
commit | 696c35e86bffea1f2bc6179a29e46416899e3de6 (patch) | |
tree | 774b8cec3342b4ca9a2c6d9c193f59b17299f615 /Lib/importlib | |
parent | da037616b10fc12a213e67711065f8123fb98cef (diff) | |
download | cpython-696c35e86bffea1f2bc6179a29e46416899e3de6.zip cpython-696c35e86bffea1f2bc6179a29e46416899e3de6.tar.gz cpython-696c35e86bffea1f2bc6179a29e46416899e3de6.tar.bz2 |
Issue #26186: Remove the restriction that built-in and extension
modules can't be lazily loaded.
Thanks to Python 3.6 allowing for types.ModuleType to have its
__class__ mutated, the restriction can be lifted by calling
create_module() on the wrapped loader.
Diffstat (limited to 'Lib/importlib')
-rw-r--r-- | Lib/importlib/util.py | 24 |
1 files changed, 11 insertions, 13 deletions
diff --git a/Lib/importlib/util.py b/Lib/importlib/util.py index a9d0f1e..6bdf0d4 100644 --- a/Lib/importlib/util.py +++ b/Lib/importlib/util.py @@ -204,11 +204,6 @@ def module_for_loader(fxn): return module_for_loader_wrapper -class _Module(types.ModuleType): - - """A subclass of the module type to allow __class__ manipulation.""" - - class _LazyModule(types.ModuleType): """A subclass of the module type which triggers loading upon attribute access.""" @@ -218,13 +213,14 @@ class _LazyModule(types.ModuleType): # All module metadata must be garnered from __spec__ in order to avoid # using mutated values. # Stop triggering this method. - self.__class__ = _Module + self.__class__ = types.ModuleType # Get the original name to make sure no object substitution occurred # in sys.modules. original_name = self.__spec__.name # Figure out exactly what attributes were mutated between the creation # of the module and now. - attrs_then = self.__spec__.loader_state + attrs_then = self.__spec__.loader_state['__dict__'] + original_type = self.__spec__.loader_state['__class__'] attrs_now = self.__dict__ attrs_updated = {} for key, value in attrs_now.items(): @@ -239,9 +235,9 @@ class _LazyModule(types.ModuleType): # object was put into sys.modules. if original_name in sys.modules: if id(self) != id(sys.modules[original_name]): - msg = ('module object for {!r} substituted in sys.modules ' - 'during a lazy load') - raise ValueError(msg.format(original_name)) + raise ValueError(f"module object for {original_name!r} " + "substituted in sys.modules during a lazy " + "load") # Update after loading since that's what would happen in an eager # loading situation. self.__dict__.update(attrs_updated) @@ -275,8 +271,7 @@ class LazyLoader(abc.Loader): self.loader = loader def create_module(self, spec): - """Create a module which can have its __class__ manipulated.""" - return _Module(spec.name) + return self.loader.create_module(spec) def exec_module(self, module): """Make the module load lazily.""" @@ -286,5 +281,8 @@ class LazyLoader(abc.Loader): # on an object would have triggered the load, # e.g. ``module.__spec__.loader = None`` would trigger a load from # trying to access module.__spec__. - module.__spec__.loader_state = module.__dict__.copy() + loader_state = {} + loader_state['__dict__'] = module.__dict__.copy() + loader_state['__class__'] = module.__class__ + module.__spec__.loader_state = loader_state module.__class__ = _LazyModule |