Skip to content

Commit 10d1b63

Browse files
authored
gh-150565: Add additional tests for Lazy Imports (#149739)
Add lazy import tests ported from internal test suite
1 parent 7928a8b commit 10d1b63

23 files changed

Lines changed: 190 additions & 0 deletions

File tree

Lib/test/test_lazy_import/__init__.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,5 +1961,135 @@ def test_dunder_lazy_import_without_frame(self):
19611961
)
19621962

19631963

1964+
class SubmoduleLazinessTests(unittest.TestCase):
1965+
"""Tests that module-level lazy imports remain lazy until accessed."""
1966+
1967+
def tearDown(self):
1968+
for key in list(sys.modules.keys()):
1969+
if key.startswith('test.test_lazy_import.data'):
1970+
del sys.modules[key]
1971+
sys.set_lazy_imports_filter(None)
1972+
sys.set_lazy_imports("normal")
1973+
1974+
def test_unaccessed_imports_stay_lazy(self):
1975+
"""Imports in 'all' mode should stay lazy until accessed."""
1976+
sys.set_lazy_imports("all")
1977+
from test.test_lazy_import.data.metasyntactic import names
1978+
self.assertIsInstance(names.__dict__["Foo"], types.LazyImportType)
1979+
self.assertNotIn(
1980+
"test.test_lazy_import.data.metasyntactic.foo", sys.modules
1981+
)
1982+
_ = names.Foo
1983+
self.assertEqual(names.Foo, "Foo")
1984+
self.assertIn(
1985+
"test.test_lazy_import.data.metasyntactic.foo", sys.modules
1986+
)
1987+
self.assertIsInstance(names.__dict__["Ack"], types.LazyImportType)
1988+
self.assertNotIn(
1989+
"test.test_lazy_import.data.metasyntactic.foo.ack", sys.modules
1990+
)
1991+
1992+
1993+
class AttributeSideEffectTests(unittest.TestCase):
1994+
"""Tests that submodule imports don't overwrite parent attributes."""
1995+
1996+
def tearDown(self):
1997+
for key in list(sys.modules.keys()):
1998+
if key.startswith('test.test_lazy_import.data'):
1999+
del sys.modules[key]
2000+
sys.set_lazy_imports_filter(None)
2001+
sys.set_lazy_imports("normal")
2002+
2003+
def test_version_submodule_does_not_overwrite(self):
2004+
"""A __version__ submodule should not overwrite the parent's
2005+
__version__ attribute imported in __init__.py."""
2006+
import test.test_lazy_import.data.versioned as versioned
2007+
self.assertEqual(versioned.__version__, "1.0")
2008+
self.assertEqual(
2009+
versioned.__copyright__,
2010+
"Copyright (c) 2001-2022 Python Software Foundation.",
2011+
)
2012+
2013+
2014+
class ModuleVariableNameCollisionTests(unittest.TestCase):
2015+
"""Tests for name collision between a submodule and a variable."""
2016+
2017+
def tearDown(self):
2018+
for key in list(sys.modules.keys()):
2019+
if key.startswith('test.test_lazy_import.data'):
2020+
del sys.modules[key]
2021+
sys.set_lazy_imports_filter(None)
2022+
sys.set_lazy_imports("normal")
2023+
2024+
def test_variable_after_import_wins(self):
2025+
"""Variable assigned after import should overwrite the submodule."""
2026+
from test.test_lazy_import.data import module_same_name_var_order1
2027+
self.assertEqual(module_same_name_var_order1.bar, "Blah")
2028+
2029+
def test_import_after_variable_wins(self):
2030+
"""Import after variable assignment should overwrite the variable."""
2031+
from test.test_lazy_import.data import module_same_name_var_order2
2032+
bar_mod = sys.modules[
2033+
"test.test_lazy_import.data.module_same_name_var_order2.bar"
2034+
]
2035+
self.assertIs(module_same_name_var_order2.bar, bar_mod)
2036+
2037+
2038+
class DeletedModuleReimportTests(unittest.TestCase):
2039+
"""Tests for reimporting after module deletion from sys.modules."""
2040+
2041+
def tearDown(self):
2042+
for key in list(sys.modules.keys()):
2043+
if key.startswith('test.test_lazy_import.data'):
2044+
del sys.modules[key]
2045+
sys.set_lazy_imports_filter(None)
2046+
sys.set_lazy_imports("normal")
2047+
2048+
def test_reimport_creates_new_module(self):
2049+
"""Deleting and reimporting should create a new module object."""
2050+
import test.test_lazy_import.data.metasyntactic.foo
2051+
import test.test_lazy_import.data.metasyntactic.foo.bar.baz
2052+
2053+
first_bar = test.test_lazy_import.data.metasyntactic.foo.bar
2054+
2055+
del sys.modules[
2056+
"test.test_lazy_import.data.metasyntactic.foo.bar"
2057+
]
2058+
2059+
import test.test_lazy_import.data.metasyntactic.foo.bar.thud
2060+
2061+
second_bar = test.test_lazy_import.data.metasyntactic.foo.bar
2062+
2063+
self.assertIsNot(first_bar, second_bar)
2064+
self.assertIn("baz", dir(first_bar))
2065+
self.assertNotIn("thud", dir(first_bar))
2066+
self.assertIn("thud", dir(second_bar))
2067+
self.assertNotIn("baz", dir(second_bar))
2068+
2069+
2070+
@support.requires_subprocess()
2071+
class CircularImportLazyTests(unittest.TestCase):
2072+
"""Tests that lazy imports can break circular import patterns."""
2073+
2074+
def test_succeeds_with_lazy(self):
2075+
"""Same-level circular imports should succeed with lazy mode."""
2076+
proc = assert_python_ok(
2077+
"-X", "lazy_imports=all", "-c",
2078+
"import test.test_lazy_import.data.circular_import_pkg.main;"
2079+
"print('OK')",
2080+
)
2081+
self.assertIn(b"OK", proc.out)
2082+
2083+
def test_fails_without_lazy(self):
2084+
"""Same-level circular imports should fail without lazy mode."""
2085+
result = subprocess.run(
2086+
[sys.executable, "-c",
2087+
"import test.test_lazy_import.data.circular_import_pkg.main"],
2088+
capture_output=True, text=True,
2089+
)
2090+
self.assertNotEqual(result.returncode, 0)
2091+
self.assertIn("ImportError", result.stderr)
2092+
2093+
19642094
if __name__ == '__main__':
19652095
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)