https://github.com/python/cpython/commit/3d9473fd230bb8a7037e41a31e1185ff2612992b
commit: 3d9473fd230bb8a7037e41a31e1185ff2612992b
branch: 3.15
author: Hugo van Kemenade <[email protected]>
committer: hugovk <[email protected]>
date: 2026-10-02T18:15:05Z
summary:

[3.15] gh-150565: Add additional tests for Lazy Imports (GH-149739) (#158614)

Co-authored-by: Brittany Reynoso <[email protected]>

files:
A Lib/test/test_lazy_import/data/circular_import_pkg/__init__.py
A Lib/test/test_lazy_import/data/circular_import_pkg/main.py
A Lib/test/test_lazy_import/data/circular_import_pkg/x.py
A Lib/test/test_lazy_import/data/circular_import_pkg/y.py
A Lib/test/test_lazy_import/data/metasyntactic/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/foo/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/foo/ack/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/foo/bar/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/qux/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/foo/bar/thud/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/names.py
A Lib/test/test_lazy_import/data/metasyntactic/plugh/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/waldo/__init__.py
A Lib/test/test_lazy_import/data/metasyntactic/waldo/fred/__init__.py
A Lib/test/test_lazy_import/data/module_same_name_var_order1/__init__.py
A Lib/test/test_lazy_import/data/module_same_name_var_order1/bar.py
A Lib/test/test_lazy_import/data/module_same_name_var_order2/__init__.py
A Lib/test/test_lazy_import/data/module_same_name_var_order2/bar.py
A Lib/test/test_lazy_import/data/versioned/__init__.py
A Lib/test/test_lazy_import/data/versioned/__version__.py
M Lib/test/test_lazy_import/__init__.py
M Makefile.pre.in

diff --git a/Lib/test/test_lazy_import/__init__.py 
b/Lib/test/test_lazy_import/__init__.py
index 8f5fe26728b7fb6..dcee906c630c74f 100644
--- a/Lib/test/test_lazy_import/__init__.py
+++ b/Lib/test/test_lazy_import/__init__.py
@@ -2577,6 +2577,135 @@ def 
test_dotted_as_replays_lookups_on_dotted_placeholder(self):
                 assert_python_ok("-c", code)
 
 
+class SubmoduleLazinessTests(unittest.TestCase):
+    """Tests that module-level lazy imports remain lazy until accessed."""
+
+    def tearDown(self):
+        for key in list(sys.modules.keys()):
+            if key.startswith('test.test_lazy_import.data'):
+                del sys.modules[key]
+        sys.set_lazy_imports_filter(None)
+        sys.set_lazy_imports("normal")
+
+    def test_unaccessed_imports_stay_lazy(self):
+        """Imports in 'all' mode should stay lazy until accessed."""
+        sys.set_lazy_imports("all")
+        from test.test_lazy_import.data.metasyntactic import names
+        self.assertIsInstance(names.__dict__["Foo"], types.LazyImportType)
+        self.assertNotIn(
+            "test.test_lazy_import.data.metasyntactic.foo", sys.modules
+        )
+        _ = names.Foo
+        self.assertEqual(names.Foo, "Foo")
+        self.assertIn(
+            "test.test_lazy_import.data.metasyntactic.foo", sys.modules
+        )
+        self.assertIsInstance(names.__dict__["Ack"], types.LazyImportType)
+        self.assertNotIn(
+            "test.test_lazy_import.data.metasyntactic.foo.ack", sys.modules
+        )
+
+
+class AttributeSideEffectTests(unittest.TestCase):
+    """Tests that submodule imports don't overwrite parent attributes."""
+
+    def tearDown(self):
+        for key in list(sys.modules.keys()):
+            if key.startswith('test.test_lazy_import.data'):
+                del sys.modules[key]
+        sys.set_lazy_imports_filter(None)
+        sys.set_lazy_imports("normal")
+
+    def test_version_submodule_does_not_overwrite(self):
+        """A __version__ submodule should not overwrite the parent's
+        __version__ attribute imported in __init__.py."""
+        import test.test_lazy_import.data.versioned as versioned
+        self.assertEqual(versioned.__version__, "1.0")
+        self.assertEqual(
+            versioned.__copyright__,
+            "Copyright (c) 2001-2022 Python Software Foundation.",
+        )
+
+
+class ModuleVariableNameCollisionTests(unittest.TestCase):
+    """Tests for name collision between a submodule and a variable."""
+
+    def tearDown(self):
+        for key in list(sys.modules.keys()):
+            if key.startswith('test.test_lazy_import.data'):
+                del sys.modules[key]
+        sys.set_lazy_imports_filter(None)
+        sys.set_lazy_imports("normal")
+
+    def test_variable_after_import_wins(self):
+        """Variable assigned after import should overwrite the submodule."""
+        from test.test_lazy_import.data import module_same_name_var_order1
+        self.assertEqual(module_same_name_var_order1.bar, "Blah")
+
+    def test_import_after_variable_wins(self):
+        """Import after variable assignment should overwrite the variable."""
+        from test.test_lazy_import.data import module_same_name_var_order2
+        bar_mod = sys.modules[
+            "test.test_lazy_import.data.module_same_name_var_order2.bar"
+        ]
+        self.assertIs(module_same_name_var_order2.bar, bar_mod)
+
+
+class DeletedModuleReimportTests(unittest.TestCase):
+    """Tests for reimporting after module deletion from sys.modules."""
+
+    def tearDown(self):
+        for key in list(sys.modules.keys()):
+            if key.startswith('test.test_lazy_import.data'):
+                del sys.modules[key]
+        sys.set_lazy_imports_filter(None)
+        sys.set_lazy_imports("normal")
+
+    def test_reimport_creates_new_module(self):
+        """Deleting and reimporting should create a new module object."""
+        import test.test_lazy_import.data.metasyntactic.foo
+        import test.test_lazy_import.data.metasyntactic.foo.bar.baz
+
+        first_bar = test.test_lazy_import.data.metasyntactic.foo.bar
+
+        del sys.modules[
+            "test.test_lazy_import.data.metasyntactic.foo.bar"
+        ]
+
+        import test.test_lazy_import.data.metasyntactic.foo.bar.thud
+
+        second_bar = test.test_lazy_import.data.metasyntactic.foo.bar
+
+        self.assertIsNot(first_bar, second_bar)
+        self.assertIn("baz", dir(first_bar))
+        self.assertNotIn("thud", dir(first_bar))
+        self.assertIn("thud", dir(second_bar))
+        self.assertNotIn("baz", dir(second_bar))
+
+
[email protected]_subprocess()
+class CircularImportLazyTests(unittest.TestCase):
+    """Tests that lazy imports can break circular import patterns."""
+
+    def test_succeeds_with_lazy(self):
+        """Same-level circular imports should succeed with lazy mode."""
+        proc = assert_python_ok(
+            "-X", "lazy_imports=all", "-c",
+            "import test.test_lazy_import.data.circular_import_pkg.main;"
+            "print('OK')",
+        )
+        self.assertIn(b"OK", proc.out)
+
+    def test_fails_without_lazy(self):
+        """Same-level circular imports should fail without lazy mode."""
+        result = subprocess.run(
+            [sys.executable, "-c",
+             "import test.test_lazy_import.data.circular_import_pkg.main"],
+            capture_output=True, text=True,
+        )
+        self.assertNotEqual(result.returncode, 0)
+        self.assertIn("ImportError", result.stderr)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/Lib/test/test_lazy_import/data/circular_import_pkg/__init__.py 
b/Lib/test/test_lazy_import/data/circular_import_pkg/__init__.py
new file mode 100644
index 000000000000000..e69de29bb2d1d64
diff --git a/Lib/test/test_lazy_import/data/circular_import_pkg/main.py 
b/Lib/test/test_lazy_import/data/circular_import_pkg/main.py
new file mode 100644
index 000000000000000..0b897b377e28a66
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/circular_import_pkg/main.py
@@ -0,0 +1,2 @@
+from .x import X2
+X2()
diff --git a/Lib/test/test_lazy_import/data/circular_import_pkg/x.py 
b/Lib/test/test_lazy_import/data/circular_import_pkg/x.py
new file mode 100644
index 000000000000000..b44d5185c9e70fc
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/circular_import_pkg/x.py
@@ -0,0 +1,7 @@
+def X1():
+    return "X"
+
+from .y import Y1
+
+def X2():
+    return Y1()
diff --git a/Lib/test/test_lazy_import/data/circular_import_pkg/y.py 
b/Lib/test/test_lazy_import/data/circular_import_pkg/y.py
new file mode 100644
index 000000000000000..d2385f7e67c01fd
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/circular_import_pkg/y.py
@@ -0,0 +1,7 @@
+def Y1():
+    return "Y"
+
+from .x import X2
+
+def Y2():
+    return X2()
diff --git a/Lib/test/test_lazy_import/data/metasyntactic/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/__init__.py
new file mode 100644
index 000000000000000..e69de29bb2d1d64
diff --git a/Lib/test/test_lazy_import/data/metasyntactic/foo/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/foo/__init__.py
new file mode 100644
index 000000000000000..632a9fb87ac0fd7
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/foo/__init__.py
@@ -0,0 +1 @@
+Foo = "Foo"
diff --git a/Lib/test/test_lazy_import/data/metasyntactic/foo/ack/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/foo/ack/__init__.py
new file mode 100644
index 000000000000000..c4c249f3b651536
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/foo/ack/__init__.py
@@ -0,0 +1 @@
+Ack = "Ack"
diff --git a/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/__init__.py
new file mode 100644
index 000000000000000..03faa550258f97f
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/__init__.py
@@ -0,0 +1 @@
+Bar = "Bar"
diff --git 
a/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/__init__.py
new file mode 100644
index 000000000000000..62d8366b4404d6c
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/__init__.py
@@ -0,0 +1 @@
+Baz = "Baz"
diff --git 
a/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/qux/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/qux/__init__.py
new file mode 100644
index 000000000000000..12389d76a28539c
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/qux/__init__.py
@@ -0,0 +1 @@
+Qux = "Qux"
diff --git 
a/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/thud/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/thud/__init__.py
new file mode 100644
index 000000000000000..a4dd8ab0fae17ee
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/foo/bar/thud/__init__.py
@@ -0,0 +1 @@
+Thud = "Thud"
diff --git a/Lib/test/test_lazy_import/data/metasyntactic/names.py 
b/Lib/test/test_lazy_import/data/metasyntactic/names.py
new file mode 100644
index 000000000000000..b2edf13e850389a
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/names.py
@@ -0,0 +1,9 @@
+from .foo import Foo
+from .foo.ack import Ack
+from .foo.bar import Bar
+from .foo.bar.baz import Baz
+from .foo.bar.thud import Thud
+from .waldo import Waldo
+from .waldo.fred import Fred
+from .plugh import Plugh
+Metasyntactic = "Metasyntactic"
diff --git a/Lib/test/test_lazy_import/data/metasyntactic/plugh/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/plugh/__init__.py
new file mode 100644
index 000000000000000..5db053204fc17ce
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/plugh/__init__.py
@@ -0,0 +1 @@
+Plugh = "Plugh"
diff --git a/Lib/test/test_lazy_import/data/metasyntactic/waldo/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/waldo/__init__.py
new file mode 100644
index 000000000000000..39f5a1b18385733
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/waldo/__init__.py
@@ -0,0 +1 @@
+Waldo = "Waldo"
diff --git 
a/Lib/test/test_lazy_import/data/metasyntactic/waldo/fred/__init__.py 
b/Lib/test/test_lazy_import/data/metasyntactic/waldo/fred/__init__.py
new file mode 100644
index 000000000000000..8c9dce1b3cfb9d1
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/metasyntactic/waldo/fred/__init__.py
@@ -0,0 +1 @@
+Fred = "Fred"
diff --git 
a/Lib/test/test_lazy_import/data/module_same_name_var_order1/__init__.py 
b/Lib/test/test_lazy_import/data/module_same_name_var_order1/__init__.py
new file mode 100644
index 000000000000000..b815b2a7d6fedf1
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/module_same_name_var_order1/__init__.py
@@ -0,0 +1,3 @@
+from .bar import Bar
+bar = "Blah"
+Bar
diff --git a/Lib/test/test_lazy_import/data/module_same_name_var_order1/bar.py 
b/Lib/test/test_lazy_import/data/module_same_name_var_order1/bar.py
new file mode 100644
index 000000000000000..03faa550258f97f
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/module_same_name_var_order1/bar.py
@@ -0,0 +1 @@
+Bar = "Bar"
diff --git 
a/Lib/test/test_lazy_import/data/module_same_name_var_order2/__init__.py 
b/Lib/test/test_lazy_import/data/module_same_name_var_order2/__init__.py
new file mode 100644
index 000000000000000..008d199c73e534d
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/module_same_name_var_order2/__init__.py
@@ -0,0 +1,3 @@
+bar = "Blah"
+from .bar import Bar
+Bar
diff --git a/Lib/test/test_lazy_import/data/module_same_name_var_order2/bar.py 
b/Lib/test/test_lazy_import/data/module_same_name_var_order2/bar.py
new file mode 100644
index 000000000000000..03faa550258f97f
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/module_same_name_var_order2/bar.py
@@ -0,0 +1 @@
+Bar = "Bar"
diff --git a/Lib/test/test_lazy_import/data/versioned/__init__.py 
b/Lib/test/test_lazy_import/data/versioned/__init__.py
new file mode 100644
index 000000000000000..be5bb3f8e607185
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/versioned/__init__.py
@@ -0,0 +1,2 @@
+from .__version__ import __version__
+from .__version__ import __copyright__
diff --git a/Lib/test/test_lazy_import/data/versioned/__version__.py 
b/Lib/test/test_lazy_import/data/versioned/__version__.py
new file mode 100644
index 000000000000000..37a104387354e28
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/versioned/__version__.py
@@ -0,0 +1,2 @@
+__version__ = "1.0"
+__copyright__ = "Copyright (c) 2001-2022 Python Software Foundation."
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 4e55bbc3ebbf128..965244d8d2dd55b 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2753,7 +2753,21 @@ TESTSUBDIRS=     idlelib/idle_test \
                test/test_lazy_import/data \
                test/test_lazy_import/data/pkg \
                test/test_lazy_import/data/badsyntax \
+               test/test_lazy_import/data/circular_import_pkg \
                test/test_lazy_import/data/lazypkg \
+               test/test_lazy_import/data/metasyntactic \
+               test/test_lazy_import/data/metasyntactic/foo \
+               test/test_lazy_import/data/metasyntactic/foo/ack \
+               test/test_lazy_import/data/metasyntactic/foo/bar \
+               test/test_lazy_import/data/metasyntactic/foo/bar/baz \
+               test/test_lazy_import/data/metasyntactic/foo/bar/baz/qux \
+               test/test_lazy_import/data/metasyntactic/foo/bar/thud \
+               test/test_lazy_import/data/metasyntactic/plugh \
+               test/test_lazy_import/data/metasyntactic/waldo \
+               test/test_lazy_import/data/metasyntactic/waldo/fred \
+               test/test_lazy_import/data/module_same_name_var_order1 \
+               test/test_lazy_import/data/module_same_name_var_order2 \
+               test/test_lazy_import/data/versioned \
                test/test_module \
                test/test_multiprocessing_fork \
                test/test_multiprocessing_forkserver \

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to