gh-149981: Test lazy import corner cases with module-level __getattr__ (#149982)

This commit is contained in:
sobolevn 2026-05-21 12:21:45 +03:00 committed by GitHub
parent 388e023fe1
commit 6dbf4ba403
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 122 additions and 0 deletions

View file

@ -98,6 +98,59 @@ def test_from_import_with_module_getattr(self):
""")
assert_python_ok("-c", code)
@support.requires_subprocess()
def test_from_import_with_module_getattr_raising(self):
"""Lazy from import should respect module-level __getattr__."""
code = textwrap.dedent("""
lazy from test.test_lazy_import.data.module_with_getattr import raising_attr
try:
raising_attr
except ValueError as exc:
assert str(exc) == 'from_getattr', exc
else:
assert False, f'ValueError is not raised: {raising_attr}'
""")
assert_python_ok("-c", code)
@support.requires_subprocess()
def test_from_import_with_module_getattr_missing(self):
"""Lazy from import should respect module-level __getattr__."""
for attr in ("missing_attr", "import_error_attr"):
with self.subTest(attr=attr):
code = textwrap.dedent(f"""
lazy from test.test_lazy_import.data.module_with_getattr import {attr}
try:
{attr}
except ImportError as exc:
assert '{attr}' in str(exc), exc
assert exc.__cause__ is not None
else:
assert False, ('ImportError is not raised', {attr})
""")
assert_python_ok("-c", code)
@support.requires_subprocess()
def test_from_import_with_module_getattr_warning(self):
"""Lazy from import should respect module-level __getattr__."""
code = textwrap.dedent("""
import warnings
with warnings.catch_warnings(record=True) as log:
lazy from test.test_lazy_import.data.module_with_getattr import warning_attr
assert log == []
with warnings.catch_warnings(record=True) as log:
warning_attr
assert warning_attr == 'from_warning_attr', warning_attr
assert len(log) == 1, log
assert isinstance(log[0].message, UserWarning), log
assert str(log[0].message) == 'from_getattr', log
""")
assert_python_ok("-c", code)
@support.requires_subprocess()
def test_from_import_with_imported_module_getattr(self):
"""Lazy from import should not shadow an imported module's __getattr__."""
@ -463,6 +516,59 @@ def test_lazy_import_pkg_cross_import(self):
self.assertEqual(type(g["x"]), int)
self.assertEqual(type(g["b"]), types.LazyImportType)
@support.requires_subprocess()
def test_package_from_import_with_module_getattr_raising(self):
"""Lazy from import should respect a package's __getattr__."""
code = textwrap.dedent("""
lazy from test.test_lazy_import.data.pkg import raising_attr
try:
raising_attr
except ValueError as exc:
assert str(exc) == 'from_getattr', exc
else:
assert False, f'ValueError is not raised: {raising_attr}'
""")
assert_python_ok("-c", code)
@support.requires_subprocess()
def test_package_from_import_with_module_getattr_missing(self):
"""Lazy from import should respect package's __getattr__."""
for attr in ("missing_attr", "import_error_attr"):
with self.subTest(attr=attr):
code = textwrap.dedent(f"""
lazy from test.test_lazy_import.data.pkg import {attr}
try:
{attr}
except ImportError as exc:
assert '{attr}' in str(exc), exc
assert exc.__cause__ is not None
else:
assert False, ('ImportError is not raised', {attr})
""")
assert_python_ok("-c", code)
@support.requires_subprocess()
def test_from_import_with_module_getattr_warning(self):
"""Lazy from import should respect package's __getattr__."""
code = textwrap.dedent("""
import warnings
with warnings.catch_warnings(record=True) as log:
lazy from test.test_lazy_import.data.pkg import warning_attr
assert log == []
with warnings.catch_warnings(record=True) as log:
warning_attr
assert warning_attr == 'from_warning_attr', warning_attr
assert len(log) == 1, log
assert isinstance(log[0].message, UserWarning), log
assert str(log[0].message) == 'from_getattr', log
""")
assert_python_ok("-c", code)
@support.requires_subprocess()
def test_package_from_import_with_module_getattr(self):
"""Lazy from import should respect a package's __getattr__."""

View file

@ -1,4 +1,12 @@
def __getattr__(name):
if name == "dynamic_attr":
return "from_getattr"
elif name == "raising_attr":
raise ValueError("from_getattr")
elif name == "import_error_attr":
raise ImportError(name)
elif name == "warning_attr":
import warnings
warnings.warn("from_getattr", category=UserWarning)
return "from_warning_attr"
raise AttributeError(name)

View file

@ -3,4 +3,12 @@
def __getattr__(name):
if name == "dynamic_attr":
return "from_getattr"
elif name == "raising_attr":
raise ValueError("from_getattr")
elif name == "import_error_attr":
raise ImportError(name)
elif name == "warning_attr":
import warnings
warnings.warn("from_getattr", category=UserWarning)
return "from_warning_attr"
raise AttributeError(name)