Skip to content

Commit 3d9473f

Browse files
hugovkbrittanyrey
andauthored
[3.15] gh-150565: Add additional tests for Lazy Imports (GH-149739) (#158614)
Co-authored-by: Brittany Reynoso <breynoso@meta.com>
1 parent c2eb1a8 commit 3d9473f

23 files changed

Lines changed: 189 additions & 0 deletions

File tree

‎Lib/test/test_lazy_import/__init__.py‎

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

25812710
if __name__ == '__main__':
25822711
unittest.main()

‎Lib/test/test_lazy_import/data/circular_import_pkg/__init__.py‎

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .x import X2
2+
X2()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def X1():
2+
return "X"
3+
4+
from .y import Y1
5+
6+
def X2():
7+
return Y1()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def Y1():
2+
return "Y"
3+
4+
from .x import X2
5+
6+
def Y2():
7+
return X2()

‎Lib/test/test_lazy_import/data/metasyntactic/__init__.py‎

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Foo = "Foo"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ack = "Ack"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Bar = "Bar"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Baz = "Baz"

0 commit comments

Comments
 (0)