_testcapi = None
-class LazyImportTests(unittest.TestCase):
- """Tests for basic lazy import functionality."""
+class LazyImportTestCase(unittest.TestCase):
+ def setUp(self):
+ self.lazy_imports_filter = sys.get_lazy_imports_filter()
+ self.lazy_imports = sys.get_lazy_imports()
def tearDown(self):
"""Clean up any test modules from sys.modules."""
if key.startswith('test.test_lazy_import.data'):
del sys.modules[key]
- sys.set_lazy_imports_filter(None)
- sys.set_lazy_imports("normal")
+ sys.set_lazy_imports_filter(self.lazy_imports_filter)
+ sys.set_lazy_imports(self.lazy_imports)
sys.lazy_modules.clear()
+
+class LazyImportTests(LazyImportTestCase):
+ """Tests for basic lazy import functionality."""
+
def test_basic_unused(self):
"""Lazy imported module should not be loaded if never accessed."""
import test.test_lazy_import.data.basic_unused
assert_python_ok("-c", code)
-class GlobalLazyImportModeTests(unittest.TestCase):
+class GlobalLazyImportModeTests(LazyImportTestCase):
"""Tests for sys.set_lazy_imports() global mode control."""
- 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_global_off(self):
"""Mode 'none' should disable lazy imports entirely."""
import test.test_lazy_import.data.global_off
self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules)
-class CompatibilityModeTests(unittest.TestCase):
+class CompatibilityModeTests(LazyImportTestCase):
"""Tests for __lazy_modules__ compatibility mode."""
- 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_compatibility_mode(self):
"""__lazy_modules__ should enable lazy imports for listed modules."""
import test.test_lazy_import.data.basic_compatibility_mode
self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules)
-class ModuleIntrospectionTests(unittest.TestCase):
+class ModuleIntrospectionTests(LazyImportTestCase):
"""Tests for module dict and getattr behavior with lazy imports."""
- 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_modules_dict(self):
"""Accessing module.__dict__ should not trigger reification."""
import test.test_lazy_import.data.modules_dict
self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules)
-class LazyImportTypeTests(unittest.TestCase):
+class LazyImportTypeTests(LazyImportTestCase):
"""Tests for the LazyImportType and its resolve() method."""
- 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_lazy_value_resolve(self):
"""resolve() method should force the lazy import to load."""
import test.test_lazy_import.data.lazy_get_value
self.assertIn(b"<built-in method resolve of lazy_import object at", proc.out)
-class SyntaxRestrictionTests(unittest.TestCase):
+class SyntaxRestrictionTests(LazyImportTestCase):
"""Tests for syntax restrictions on lazy imports."""
- 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_lazy_try_except(self):
"""lazy import inside try/except should raise SyntaxError."""
with self.assertRaises(SyntaxError):
self.assertIn("OK", result.stdout)
-class EagerImportInLazyModeTests(unittest.TestCase):
+class EagerImportInLazyModeTests(LazyImportTestCase):
"""Tests for imports that should remain eager even in lazy mode."""
- 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_try_except_eager(self):
"""Imports in try/except should be eager even with mode='all'."""
sys.set_lazy_imports("all")
del globals()["__lazy_modules__"]
-class WithStatementTests(unittest.TestCase):
+class WithStatementTests(LazyImportTestCase):
"""Tests for lazy imports in with statement context."""
- 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_lazy_with(self):
"""lazy import with 'with' statement should work."""
import test.test_lazy_import.data.lazy_with
self.assertNotIn("test.test_lazy_import.data.basic2", sys.modules)
-class PackageTests(unittest.TestCase):
+class PackageTests(LazyImportTestCase):
"""Tests for lazy imports with packages."""
- 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_lazy_import_pkg(self):
"""lazy import of package submodule should load the package."""
out = io.StringIO()
assert_python_ok("-c", code)
-class DunderLazyImportTests(unittest.TestCase):
+class DunderLazyImportTests(LazyImportTestCase):
"""Tests for __lazy_import__ builtin function."""
- 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_dunder_lazy_import(self):
"""__lazy_import__ should create lazy import proxy."""
import test.test_lazy_import.data.dunder_lazy_import
self.assertEqual(dunder_lazy_import_builtins.basic.basic2, 42)
-class SysLazyImportsAPITests(unittest.TestCase):
+class SysLazyImportsAPITests(LazyImportTestCase):
"""Tests for sys lazy imports API functions."""
- 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_set_lazy_imports_requires_string(self):
"""set_lazy_imports should reject non-string arguments."""
with self.assertRaises(TypeError):
@support.requires_subprocess()
-class ErrorHandlingTests(unittest.TestCase):
+class ErrorHandlingTests(LazyImportTestCase):
"""Tests for error handling during lazy import reification.
PEP 810: Errors during reification should show exception chaining with
both the lazy import definition location and the access location.
"""
- 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_import_error_shows_chained_traceback(self):
"""Accessing a nonexistent lazy submodule via parent attr raises AttributeError."""
code = textwrap.dedent("""
@support.requires_subprocess()
-class GlobalsAndDictTests(unittest.TestCase):
+class GlobalsAndDictTests(LazyImportTestCase):
"""Tests for globals() and __dict__ behavior with lazy imports.
PEP 810: "Calling globals() or accessing a module's __dict__ does not trigger
through that dictionary still returns lazy proxy objects."
"""
- 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_globals_returns_lazy_proxy_when_accessed_from_function(self):
"""globals() accessed from a function should return lazy proxy without reification.
@support.requires_subprocess()
-class MultipleNameFromImportTests(unittest.TestCase):
+class MultipleNameFromImportTests(LazyImportTestCase):
"""Tests for lazy from ... import with multiple names.
PEP 810: "When using lazy from ... import, each imported name is bound to a
Other names remain as lazy proxies until they are 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_accessing_one_name_leaves_others_as_proxies(self):
"""Accessing one name from multi-name import should leave others lazy."""
code = textwrap.dedent("""
@support.requires_subprocess()
-class SysLazyModulesTrackingTests(unittest.TestCase):
+class SysLazyModulesTrackingTests(LazyImportTestCase):
"""Tests for sys.lazy_modules tracking behavior.
PEP 810: "When the module is reified, it's removed from sys.lazy_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_module_added_to_lazy_modules_on_lazy_import(self):
"""Module should be added to sys.lazy_modules when lazily imported."""
# PEP 810 states lazy_modules tracks modules that have been lazily imported
@support.requires_subprocess()
-class FilterFunctionSignatureTests(unittest.TestCase):
+class FilterFunctionSignatureTests(LazyImportTestCase):
"""Tests for the filter function signature per PEP 810.
PEP 810: func(importer: str, name: str, fromlist: tuple[str, ...] | None) -> bool
"""
- 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 _run_subprocess_with_modules(self, code, files):
with tempfile.TemporaryDirectory() as tmpdir:
for relpath, contents in files.items():
self.assertIn("OK", result.stdout)
-class AdditionalSyntaxRestrictionTests(unittest.TestCase):
+class AdditionalSyntaxRestrictionTests(LazyImportTestCase):
"""Additional syntax restriction tests per PEP 810."""
- 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_lazy_import_inside_class_raises_syntax_error(self):
"""lazy import inside class body should raise SyntaxError."""
# PEP 810: "The soft keyword is only allowed at the global (module) level,
@support.requires_subprocess()
-class MixedLazyEagerImportTests(unittest.TestCase):
+class MixedLazyEagerImportTests(LazyImportTestCase):
"""Tests for mixing lazy and eager imports of the same module.
PEP 810: "If module foo is imported both lazily and eagerly in the same
the same module object."
"""
- 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_eager_import_before_lazy_resolves_to_same_module(self):
"""Eager import before lazy should make lazy resolve to same module."""
code = textwrap.dedent("""
self.assertIn("OK", result.stdout)
-class RelativeImportTests(unittest.TestCase):
+class RelativeImportTests(LazyImportTestCase):
"""Tests for relative imports with lazy keyword."""
- 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_relative_lazy_import(self):
"""lazy from . import submodule should work."""
from test.test_lazy_import.data import relative_lazy
self.assertIn("test.test_lazy_import.data.basic2", sys.modules)
-class LazyModulesCompatibilityFromImportTests(unittest.TestCase):
+class LazyModulesCompatibilityFromImportTests(LazyImportTestCase):
"""Tests for __lazy_modules__ with from imports.
PEP 810: "When a module is made lazy this way, from-imports using that
module are also lazy"
"""
- 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_lazy_modules_makes_from_imports_lazy(self):
"""__lazy_modules__ should make from imports of listed modules lazy."""
from test.test_lazy_import.data import lazy_compat_from
@support.requires_subprocess()
-class ImportStateAtReificationTests(unittest.TestCase):
+class ImportStateAtReificationTests(LazyImportTestCase):
"""Tests for import system state at reification time.
PEP 810: "Reification still calls __import__ to resolve the import, which uses
statement was evaluated."
"""
- 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_sys_path_at_reification_time_is_used(self):
"""sys.path changes after lazy import should affect reification."""
code = textwrap.dedent("""
@support.requires_subprocess()
-class ThreadSafetyTests(unittest.TestCase):
+class ThreadSafetyTests(LazyImportTestCase):
"""Tests for thread-safety of lazy imports."""
- 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_concurrent_lazy_import_reification(self):
"""Multiple threads racing to reify the same lazy import should succeed."""
from test.test_lazy_import.data import basic_unused
@unittest.skipIf(_testcapi is None, 'need the _testcapi module')
-class LazyCApiTests(unittest.TestCase):
- def tearDown(self):
- sys.set_lazy_imports("normal")
- sys.set_lazy_imports_filter(None)
-
+class LazyCApiTests(LazyImportTestCase):
def test_set_matches_sys(self):
self.assertEqual(_testcapi.PyImport_GetLazyImportsMode(), sys.get_lazy_imports())
for mode in ("normal", "all", "none"):