mirror of
https://github.com/python/cpython.git
synced 2025-07-08 03:45:36 +00:00
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.
This commit is contained in:
parent
da037616b1
commit
696c35e86b
4 changed files with 50 additions and 27 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue