@@ -2577,6 +2577,135 @@ def test_dotted_as_replays_lookups_on_dotted_placeholder(self):
25772577 assert_python_ok ("-c" , code )
25782578
25792579
2580+ class SubmoduleLazinessTests (unittest .TestCase ):
2581+ """Tests that module-level lazy imports remain lazy until accessed."""
2582+
2583+ def tearDown (self ):
2584+ for key in list (sys .modules .keys ()):
2585+ if key .startswith ('test.test_lazy_import.data' ):
2586+ del sys .modules [key ]
2587+ sys .set_lazy_imports_filter (None )
2588+ sys .set_lazy_imports ("normal" )
2589+
2590+ def test_unaccessed_imports_stay_lazy (self ):
2591+ """Imports in 'all' mode should stay lazy until accessed."""
2592+ sys .set_lazy_imports ("all" )
2593+ from test .test_lazy_import .data .metasyntactic import names
2594+ self .assertIsInstance (names .__dict__ ["Foo" ], types .LazyImportType )
2595+ self .assertNotIn (
2596+ "test.test_lazy_import.data.metasyntactic.foo" , sys .modules
2597+ )
2598+ _ = names .Foo
2599+ self .assertEqual (names .Foo , "Foo" )
2600+ self .assertIn (
2601+ "test.test_lazy_import.data.metasyntactic.foo" , sys .modules
2602+ )
2603+ self .assertIsInstance (names .__dict__ ["Ack" ], types .LazyImportType )
2604+ self .assertNotIn (
2605+ "test.test_lazy_import.data.metasyntactic.foo.ack" , sys .modules
2606+ )
2607+
2608+
2609+ class AttributeSideEffectTests (unittest .TestCase ):
2610+ """Tests that submodule imports don't overwrite parent attributes."""
2611+
2612+ def tearDown (self ):
2613+ for key in list (sys .modules .keys ()):
2614+ if key .startswith ('test.test_lazy_import.data' ):
2615+ del sys .modules [key ]
2616+ sys .set_lazy_imports_filter (None )
2617+ sys .set_lazy_imports ("normal" )
2618+
2619+ def test_version_submodule_does_not_overwrite (self ):
2620+ """A __version__ submodule should not overwrite the parent's
2621+ __version__ attribute imported in __init__.py."""
2622+ import test .test_lazy_import .data .versioned as versioned
2623+ self .assertEqual (versioned .__version__ , "1.0" )
2624+ self .assertEqual (
2625+ versioned .__copyright__ ,
2626+ "Copyright (c) 2001-2022 Python Software Foundation." ,
2627+ )
2628+
2629+
2630+ class ModuleVariableNameCollisionTests (unittest .TestCase ):
2631+ """Tests for name collision between a submodule and a variable."""
2632+
2633+ def tearDown (self ):
2634+ for key in list (sys .modules .keys ()):
2635+ if key .startswith ('test.test_lazy_import.data' ):
2636+ del sys .modules [key ]
2637+ sys .set_lazy_imports_filter (None )
2638+ sys .set_lazy_imports ("normal" )
2639+
2640+ def test_variable_after_import_wins (self ):
2641+ """Variable assigned after import should overwrite the submodule."""
2642+ from test .test_lazy_import .data import module_same_name_var_order1
2643+ self .assertEqual (module_same_name_var_order1 .bar , "Blah" )
2644+
2645+ def test_import_after_variable_wins (self ):
2646+ """Import after variable assignment should overwrite the variable."""
2647+ from test .test_lazy_import .data import module_same_name_var_order2
2648+ bar_mod = sys .modules [
2649+ "test.test_lazy_import.data.module_same_name_var_order2.bar"
2650+ ]
2651+ self .assertIs (module_same_name_var_order2 .bar , bar_mod )
2652+
2653+
2654+ class DeletedModuleReimportTests (unittest .TestCase ):
2655+ """Tests for reimporting after module deletion from sys.modules."""
2656+
2657+ def tearDown (self ):
2658+ for key in list (sys .modules .keys ()):
2659+ if key .startswith ('test.test_lazy_import.data' ):
2660+ del sys .modules [key ]
2661+ sys .set_lazy_imports_filter (None )
2662+ sys .set_lazy_imports ("normal" )
2663+
2664+ def test_reimport_creates_new_module (self ):
2665+ """Deleting and reimporting should create a new module object."""
2666+ import test .test_lazy_import .data .metasyntactic .foo
2667+ import test .test_lazy_import .data .metasyntactic .foo .bar .baz
2668+
2669+ first_bar = test .test_lazy_import .data .metasyntactic .foo .bar
2670+
2671+ del sys .modules [
2672+ "test.test_lazy_import.data.metasyntactic.foo.bar"
2673+ ]
2674+
2675+ import test .test_lazy_import .data .metasyntactic .foo .bar .thud
2676+
2677+ second_bar = test .test_lazy_import .data .metasyntactic .foo .bar
2678+
2679+ self .assertIsNot (first_bar , second_bar )
2680+ self .assertIn ("baz" , dir (first_bar ))
2681+ self .assertNotIn ("thud" , dir (first_bar ))
2682+ self .assertIn ("thud" , dir (second_bar ))
2683+ self .assertNotIn ("baz" , dir (second_bar ))
2684+
2685+
2686+ @support .requires_subprocess ()
2687+ class CircularImportLazyTests (unittest .TestCase ):
2688+ """Tests that lazy imports can break circular import patterns."""
2689+
2690+ def test_succeeds_with_lazy (self ):
2691+ """Same-level circular imports should succeed with lazy mode."""
2692+ proc = assert_python_ok (
2693+ "-X" , "lazy_imports=all" , "-c" ,
2694+ "import test.test_lazy_import.data.circular_import_pkg.main;"
2695+ "print('OK')" ,
2696+ )
2697+ self .assertIn (b"OK" , proc .out )
2698+
2699+ def test_fails_without_lazy (self ):
2700+ """Same-level circular imports should fail without lazy mode."""
2701+ result = subprocess .run (
2702+ [sys .executable , "-c" ,
2703+ "import test.test_lazy_import.data.circular_import_pkg.main" ],
2704+ capture_output = True , text = True ,
2705+ )
2706+ self .assertNotEqual (result .returncode , 0 )
2707+ self .assertIn ("ImportError" , result .stderr )
2708+
25802709
25812710if __name__ == '__main__' :
25822711 unittest .main ()
0 commit comments