@@ -574,6 +574,166 @@ def custom_import(name, globals=None, locals=None, fromlist=None,
574574 """ )
575575 assert_python_ok ("-c" , code )
576576
577+ @support .requires_subprocess ()
578+ def test_module_attr_dynamic_equal_name_clears_owner (self ):
579+ code = textwrap .dedent ("""
580+ import builtins
581+ import sys
582+ import types
583+
584+ real_import = builtins.__import__
585+ module = sys.modules[__name__]
586+
587+ lazy import target_module as target
588+ proxy = globals()["target"]
589+ stored_name = next(name for name in globals() if name == "target")
590+ dynamic_name = bytearray(b"target").decode()
591+ assert dynamic_name == stored_name
592+ assert dynamic_name is not stored_name
593+
594+ def custom_import(name, globals=None, locals=None, fromlist=None,
595+ level=0):
596+ if name == "target_module":
597+ resolved = types.ModuleType(name)
598+ resolved.VALUE = "resolved"
599+ return resolved
600+ return real_import(name, globals, locals, fromlist, level)
601+
602+ builtins.__import__ = custom_import
603+ try:
604+ resolved = getattr(module, dynamic_name)
605+ assert resolved.VALUE == "resolved"
606+ assert module.__dict__["target"] is resolved
607+
608+ module.__dict__["target"] = proxy
609+ again = proxy.resolve()
610+ assert again is resolved
611+ assert module.__dict__["target"] is proxy
612+ finally:
613+ builtins.__import__ = real_import
614+ """ )
615+ assert_python_ok ("-c" , code )
616+
617+ @support .requires_subprocess ()
618+ def test_rebound_back_to_same_proxy_after_stale_resolve_does_not_replace (self ):
619+ code = textwrap .dedent ("""
620+ import builtins
621+ import types
622+
623+ real_import = builtins.__import__
624+ calls = []
625+
626+ lazy import target_module as target
627+ proxy = globals()["target"]
628+
629+ def custom_import(name, globals=None, locals=None, fromlist=None,
630+ level=0):
631+ if name == "target_module":
632+ calls.append(name)
633+ resolved = types.ModuleType(name)
634+ resolved.VALUE = "resolved"
635+ return resolved
636+ return real_import(name, globals, locals, fromlist, level)
637+
638+ builtins.__import__ = custom_import
639+ try:
640+ sentinel = object()
641+ globals()["target"] = sentinel
642+ resolved = proxy.resolve()
643+ assert globals()["target"] is sentinel
644+
645+ globals()["target"] = proxy
646+ again = proxy.resolve()
647+ assert again is resolved
648+ assert globals()["target"] is proxy
649+ assert len(calls) == 1, calls
650+ finally:
651+ builtins.__import__ = real_import
652+ """ )
653+ assert_python_ok ("-c" , code )
654+
655+ @support .requires_subprocess ()
656+ def test_resolve_replaces_unaliased_import_global (self ):
657+ code = textwrap .dedent ("""
658+ import builtins
659+ import types
660+
661+ real_import = builtins.__import__
662+
663+ lazy import target_module
664+ proxy = globals()["target_module"]
665+
666+ def custom_import(name, globals=None, locals=None, fromlist=None,
667+ level=0):
668+ if name == "target_module":
669+ resolved = types.ModuleType(name)
670+ resolved.VALUE = "resolved"
671+ return resolved
672+ return real_import(name, globals, locals, fromlist, level)
673+
674+ builtins.__import__ = custom_import
675+ try:
676+ resolved = proxy.resolve()
677+ assert resolved.VALUE == "resolved"
678+ assert globals()["target_module"] is resolved
679+ finally:
680+ builtins.__import__ = real_import
681+ """ )
682+ assert_python_ok ("-c" , code )
683+
684+ @support .requires_subprocess ()
685+ def test_resolve_replaces_unaliased_dotted_import_binding (self ):
686+ code = textwrap .dedent ("""
687+ import types
688+
689+ lazy import test.test_lazy_import.data.pkg.bar
690+ proxy = globals()["test"]
691+
692+ resolved = proxy.resolve()
693+ assert isinstance(resolved, types.ModuleType)
694+ assert resolved.__name__ == "test"
695+ assert globals()["test"] is resolved
696+ assert "test.test_lazy_import.data.pkg" not in globals()
697+ """ )
698+ assert_python_ok ("-c" , code )
699+
700+ @support .requires_subprocess ()
701+ def test_copying_lazy_proxy_to_second_global_keeps_first_owner (self ):
702+ code = textwrap .dedent ("""
703+ import builtins
704+ import types
705+
706+ real_import = builtins.__import__
707+ calls = []
708+
709+ lazy import target_module as primary
710+ secondary = globals()["primary"]
711+ proxy = globals()["primary"]
712+
713+ def custom_import(name, globals=None, locals=None, fromlist=None,
714+ level=0):
715+ if name == "target_module":
716+ calls.append(name)
717+ resolved = types.ModuleType(name)
718+ resolved.VALUE = "resolved"
719+ return resolved
720+ return real_import(name, globals, locals, fromlist, level)
721+
722+ builtins.__import__ = custom_import
723+ try:
724+ resolved = proxy.resolve()
725+ assert globals()["primary"] is resolved
726+ assert globals()["secondary"] is proxy
727+
728+ again = globals()["secondary"].resolve()
729+ assert again is resolved
730+ assert globals()["secondary"] is proxy
731+ assert len(calls) == 1, calls
732+ finally:
733+ builtins.__import__ = real_import
734+ """ )
735+ assert_python_ok ("-c" , code )
736+
577737
578738class SyntaxRestrictionTests (LazyImportTestCase ):
579739 """Tests for syntax restrictions on lazy imports."""
0 commit comments