mirror of
https://github.com/python/cpython.git
synced 2025-08-16 23:01:34 +00:00
[3.12] gh-117178: Recover lazy loading of self-referential modules (GH-117179) (#117319)
Co-authored-by: Chris Markiewicz <effigies@gmail.com>
This commit is contained in:
parent
8d42c57789
commit
552b2646b7
3 changed files with 25 additions and 6 deletions
|
@ -178,12 +178,11 @@ class _LazyModule(types.ModuleType):
|
||||||
# Only the first thread to get the lock should trigger the load
|
# Only the first thread to get the lock should trigger the load
|
||||||
# and reset the module's class. The rest can now getattr().
|
# and reset the module's class. The rest can now getattr().
|
||||||
if object.__getattribute__(self, '__class__') is _LazyModule:
|
if object.__getattribute__(self, '__class__') is _LazyModule:
|
||||||
# The first thread comes here multiple times as it descends the
|
# Reentrant calls from the same thread must be allowed to proceed without
|
||||||
# call stack. The first time, it sets is_loading and triggers
|
# triggering the load again.
|
||||||
# exec_module(), which will access module.__dict__, module.__name__,
|
# exec_module() and self-referential imports are the primary ways this can
|
||||||
# and/or module.__spec__, reentering this method. These accesses
|
# happen, but in any case we must return something to avoid deadlock.
|
||||||
# need to be allowed to proceed without triggering the load again.
|
if loader_state['is_loading']:
|
||||||
if loader_state['is_loading'] and attr.startswith('__') and attr.endswith('__'):
|
|
||||||
return object.__getattribute__(self, attr)
|
return object.__getattribute__(self, attr)
|
||||||
loader_state['is_loading'] = True
|
loader_state['is_loading'] = True
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,24 @@ class LazyLoaderTests(unittest.TestCase):
|
||||||
# Or multiple load attempts
|
# Or multiple load attempts
|
||||||
self.assertEqual(loader.load_count, 1)
|
self.assertEqual(loader.load_count, 1)
|
||||||
|
|
||||||
|
def test_lazy_self_referential_modules(self):
|
||||||
|
# Directory modules with submodules that reference the parent can attempt to access
|
||||||
|
# the parent module during a load. Verify that this common pattern works with lazy loading.
|
||||||
|
# json is a good example in the stdlib.
|
||||||
|
json_modules = [name for name in sys.modules if name.startswith('json')]
|
||||||
|
with test_util.uncache(*json_modules):
|
||||||
|
# Standard lazy loading, unwrapped
|
||||||
|
spec = util.find_spec('json')
|
||||||
|
loader = util.LazyLoader(spec.loader)
|
||||||
|
spec.loader = loader
|
||||||
|
module = util.module_from_spec(spec)
|
||||||
|
sys.modules['json'] = module
|
||||||
|
loader.exec_module(module)
|
||||||
|
|
||||||
|
# Trigger load with attribute lookup, ensure expected behavior
|
||||||
|
test_load = module.loads('{}')
|
||||||
|
self.assertEqual(test_load, {})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix regression in lazy loading of self-referential modules, introduced in
|
||||||
|
gh-114781.
|
Loading…
Add table
Add a link
Reference in a new issue