]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-150565: Add additional tests for Lazy Imports (#149739)
authorBrittany Reynoso <breynoso@meta.com>
Tue, 23 Jun 2026 20:15:11 +0000 (13:15 -0700)
committerGitHub <noreply@github.com>
Tue, 23 Jun 2026 20:15:11 +0000 (13:15 -0700)
Add lazy import tests ported from internal test suite

23 files changed:
Lib/test/test_lazy_import/__init__.py
Lib/test/test_lazy_import/data/circular_import_pkg/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/circular_import_pkg/main.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/circular_import_pkg/x.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/circular_import_pkg/y.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/foo/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/foo/ack/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/foo/bar/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/foo/bar/baz/qux/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/foo/bar/thud/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/names.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/plugh/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/waldo/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/metasyntactic/waldo/fred/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/module_same_name_var_order1/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/module_same_name_var_order1/bar.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/module_same_name_var_order2/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/module_same_name_var_order2/bar.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/versioned/__init__.py [new file with mode: 0644]
Lib/test/test_lazy_import/data/versioned/__version__.py [new file with mode: 0644]
Makefile.pre.in

index 1c5ab4ef73da2fb739fa1c62202ddb53f640be02..4658882243d65ff0b9373ba5292c689bee463730 100644 (file)
@@ -1961,5 +1961,135 @@ class LazyCApiTests(LazyImportTestCase):
             )
 
 
+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))
+
+
+@support.requires_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 (file)
index 0000000..e69de29
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 (file)
index 0000000..0b897b3
--- /dev/null
@@ -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 (file)
index 0000000..b44d518
--- /dev/null
@@ -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 (file)
index 0000000..d2385f7
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..632a9fb
--- /dev/null
@@ -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 (file)
index 0000000..c4c249f
--- /dev/null
@@ -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 (file)
index 0000000..03faa55
--- /dev/null
@@ -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 (file)
index 0000000..62d8366
--- /dev/null
@@ -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 (file)
index 0000000..12389d7
--- /dev/null
@@ -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 (file)
index 0000000..a4dd8ab
--- /dev/null
@@ -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 (file)
index 0000000..b2edf13
--- /dev/null
@@ -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 (file)
index 0000000..5db0532
--- /dev/null
@@ -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 (file)
index 0000000..39f5a1b
--- /dev/null
@@ -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 (file)
index 0000000..8c9dce1
--- /dev/null
@@ -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 (file)
index 0000000..b815b2a
--- /dev/null
@@ -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 (file)
index 0000000..03faa55
--- /dev/null
@@ -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 (file)
index 0000000..008d199
--- /dev/null
@@ -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 (file)
index 0000000..03faa55
--- /dev/null
@@ -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 (file)
index 0000000..be5bb3f
--- /dev/null
@@ -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 (file)
index 0000000..37a1043
--- /dev/null
@@ -0,0 +1,2 @@
+__version__ = "1.0"
+__copyright__ = "Copyright (c) 2001-2022 Python Software Foundation."
index cf700e9f23409015af87a80837302c8e989f08d5..b9914369ad1bedcce561be08d7f907bd8270217c 100644 (file)
@@ -2675,6 +2675,20 @@ 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/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 \