@@ -2692,6 +2692,135 @@ def test_dotted_as_replays_lookups_on_dotted_placeholder(self):
26922692 assert_python_ok ("-c" , code )
26932693
26942694
2695+ class SubmoduleLazinessTests (unittest .TestCase ):
2696+ """Tests that module-level lazy imports remain lazy until accessed."""
2697+
2698+ def tearDown (self ):
2699+ for key in list (sys .modules .keys ()):
2700+ if key .startswith ('test.test_lazy_import.data' ):
2701+ del sys .modules [key ]
2702+ sys .set_lazy_imports_filter (None )
2703+ sys .set_lazy_imports ("normal" )
2704+
2705+ def test_unaccessed_imports_stay_lazy (self ):
2706+ """Imports in 'all' mode should stay lazy until accessed."""
2707+ sys .set_lazy_imports ("all" )
2708+ from test .test_lazy_import .data .metasyntactic import names
2709+ self .assertIsInstance (names .__dict__ ["Foo" ], types .LazyImportType )
2710+ self .assertNotIn (
2711+ "test.test_lazy_import.data.metasyntactic.foo" , sys .modules
2712+ )
2713+ _ = names .Foo
2714+ self .assertEqual (names .Foo , "Foo" )
2715+ self .assertIn (
2716+ "test.test_lazy_import.data.metasyntactic.foo" , sys .modules
2717+ )
2718+ self .assertIsInstance (names .__dict__ ["Ack" ], types .LazyImportType )
2719+ self .assertNotIn (
2720+ "test.test_lazy_import.data.metasyntactic.foo.ack" , sys .modules
2721+ )
2722+
2723+
2724+ class AttributeSideEffectTests (unittest .TestCase ):
2725+ """Tests that submodule imports don't overwrite parent attributes."""
2726+
2727+ def tearDown (self ):
2728+ for key in list (sys .modules .keys ()):
2729+ if key .startswith ('test.test_lazy_import.data' ):
2730+ del sys .modules [key ]
2731+ sys .set_lazy_imports_filter (None )
2732+ sys .set_lazy_imports ("normal" )
2733+
2734+ def test_version_submodule_does_not_overwrite (self ):
2735+ """A __version__ submodule should not overwrite the parent's
2736+ __version__ attribute imported in __init__.py."""
2737+ import test .test_lazy_import .data .versioned as versioned
2738+ self .assertEqual (versioned .__version__ , "1.0" )
2739+ self .assertEqual (
2740+ versioned .__copyright__ ,
2741+ "Copyright (c) 2001-2022 Python Software Foundation." ,
2742+ )
2743+
2744+
2745+ class ModuleVariableNameCollisionTests (unittest .TestCase ):
2746+ """Tests for name collision between a submodule and a variable."""
2747+
2748+ def tearDown (self ):
2749+ for key in list (sys .modules .keys ()):
2750+ if key .startswith ('test.test_lazy_import.data' ):
2751+ del sys .modules [key ]
2752+ sys .set_lazy_imports_filter (None )
2753+ sys .set_lazy_imports ("normal" )
2754+
2755+ def test_variable_after_import_wins (self ):
2756+ """Variable assigned after import should overwrite the submodule."""
2757+ from test .test_lazy_import .data import module_same_name_var_order1
2758+ self .assertEqual (module_same_name_var_order1 .bar , "Blah" )
2759+
2760+ def test_import_after_variable_wins (self ):
2761+ """Import after variable assignment should overwrite the variable."""
2762+ from test .test_lazy_import .data import module_same_name_var_order2
2763+ bar_mod = sys .modules [
2764+ "test.test_lazy_import.data.module_same_name_var_order2.bar"
2765+ ]
2766+ self .assertIs (module_same_name_var_order2 .bar , bar_mod )
2767+
2768+
2769+ class DeletedModuleReimportTests (unittest .TestCase ):
2770+ """Tests for reimporting after module deletion from sys.modules."""
2771+
2772+ def tearDown (self ):
2773+ for key in list (sys .modules .keys ()):
2774+ if key .startswith ('test.test_lazy_import.data' ):
2775+ del sys .modules [key ]
2776+ sys .set_lazy_imports_filter (None )
2777+ sys .set_lazy_imports ("normal" )
2778+
2779+ def test_reimport_creates_new_module (self ):
2780+ """Deleting and reimporting should create a new module object."""
2781+ import test .test_lazy_import .data .metasyntactic .foo
2782+ import test .test_lazy_import .data .metasyntactic .foo .bar .baz
2783+
2784+ first_bar = test .test_lazy_import .data .metasyntactic .foo .bar
2785+
2786+ del sys .modules [
2787+ "test.test_lazy_import.data.metasyntactic.foo.bar"
2788+ ]
2789+
2790+ import test .test_lazy_import .data .metasyntactic .foo .bar .thud
2791+
2792+ second_bar = test .test_lazy_import .data .metasyntactic .foo .bar
2793+
2794+ self .assertIsNot (first_bar , second_bar )
2795+ self .assertIn ("baz" , dir (first_bar ))
2796+ self .assertNotIn ("thud" , dir (first_bar ))
2797+ self .assertIn ("thud" , dir (second_bar ))
2798+ self .assertNotIn ("baz" , dir (second_bar ))
2799+
2800+
2801+ @support .requires_subprocess ()
2802+ class CircularImportLazyTests (unittest .TestCase ):
2803+ """Tests that lazy imports can break circular import patterns."""
2804+
2805+ def test_succeeds_with_lazy (self ):
2806+ """Same-level circular imports should succeed with lazy mode."""
2807+ proc = assert_python_ok (
2808+ "-X" , "lazy_imports=all" , "-c" ,
2809+ "import test.test_lazy_import.data.circular_import_pkg.main;"
2810+ "print('OK')" ,
2811+ )
2812+ self .assertIn (b"OK" , proc .out )
2813+
2814+ def test_fails_without_lazy (self ):
2815+ """Same-level circular imports should fail without lazy mode."""
2816+ result = subprocess .run (
2817+ [sys .executable , "-c" ,
2818+ "import test.test_lazy_import.data.circular_import_pkg.main" ],
2819+ capture_output = True , text = True ,
2820+ )
2821+ self .assertNotEqual (result .returncode , 0 )
2822+ self .assertIn ("ImportError" , result .stderr )
2823+
26952824
26962825if __name__ == '__main__' :
26972826 unittest .main ()
0 commit comments