Skip to content

Commit bd39b8b

Browse files
committed
gh-149981: Test lazy import corner cases with module-level __getattr__
1 parent 0ed497a commit bd39b8b

3 files changed

Lines changed: 122 additions & 0 deletions

File tree

Lib/test/test_lazy_import/__init__.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,59 @@ def test_from_import_with_module_getattr(self):
9797
""")
9898
assert_python_ok("-c", code)
9999

100+
@support.requires_subprocess()
101+
def test_from_import_with_module_getattr_raising(self):
102+
"""Lazy from import should respect module-level __getattr__."""
103+
code = textwrap.dedent("""
104+
lazy from test.test_lazy_import.data.module_with_getattr import raising_attr
105+
106+
try:
107+
raising_attr
108+
except ValueError as exc:
109+
assert str(exc) == 'from_getattr', exc
110+
else:
111+
assert False, f'ValueError is not raised: {raising_attr}'
112+
""")
113+
assert_python_ok("-c", code)
114+
115+
@support.requires_subprocess()
116+
def test_from_import_with_module_getattr_missing(self):
117+
"""Lazy from import should respect module-level __getattr__."""
118+
for attr in ("missing_attr", "import_error_attr"):
119+
with self.subTest(attr=attr):
120+
code = textwrap.dedent(f"""
121+
lazy from test.test_lazy_import.data.module_with_getattr import {attr}
122+
123+
try:
124+
{attr}
125+
except ImportError as exc:
126+
assert '{attr}' in str(exc), exc
127+
assert exc.__cause__ is not None
128+
else:
129+
assert False, ('ImportError is not raised', {attr})
130+
""")
131+
assert_python_ok("-c", code)
132+
133+
@support.requires_subprocess()
134+
def test_from_import_with_module_getattr_warning(self):
135+
"""Lazy from import should respect module-level __getattr__."""
136+
code = textwrap.dedent("""
137+
import warnings
138+
139+
with warnings.catch_warnings(record=True) as log:
140+
lazy from test.test_lazy_import.data.module_with_getattr import warning_attr
141+
142+
assert log == []
143+
144+
with warnings.catch_warnings(record=True) as log:
145+
warning_attr
146+
assert warning_attr == 'from_warning_attr', warning_attr
147+
assert len(log) == 1, log
148+
assert isinstance(log[0].message, UserWarning), log
149+
assert str(log[0].message) == 'from_getattr', log
150+
""")
151+
assert_python_ok("-c", code)
152+
100153
@support.requires_subprocess()
101154
def test_from_import_with_imported_module_getattr(self):
102155
"""Lazy from import should not shadow an imported module's __getattr__."""
@@ -458,6 +511,59 @@ def test_lazy_import_pkg_cross_import(self):
458511
self.assertEqual(type(g["x"]), int)
459512
self.assertEqual(type(g["b"]), types.LazyImportType)
460513

514+
@support.requires_subprocess()
515+
def test_package_from_import_with_module_getattr_raising(self):
516+
"""Lazy from import should respect a package's __getattr__."""
517+
code = textwrap.dedent("""
518+
lazy from test.test_lazy_import.data.pkg import raising_attr
519+
520+
try:
521+
raising_attr
522+
except ValueError as exc:
523+
assert str(exc) == 'from_getattr', exc
524+
else:
525+
assert False, f'ValueError is not raised: {raising_attr}'
526+
""")
527+
assert_python_ok("-c", code)
528+
529+
@support.requires_subprocess()
530+
def test_package_from_import_with_module_getattr_missing(self):
531+
"""Lazy from import should respect package's __getattr__."""
532+
for attr in ("missing_attr", "import_error_attr"):
533+
with self.subTest(attr=attr):
534+
code = textwrap.dedent(f"""
535+
lazy from test.test_lazy_import.data.pkg import {attr}
536+
537+
try:
538+
{attr}
539+
except ImportError as exc:
540+
assert '{attr}' in str(exc), exc
541+
assert exc.__cause__ is not None
542+
else:
543+
assert False, ('ImportError is not raised', {attr})
544+
""")
545+
assert_python_ok("-c", code)
546+
547+
@support.requires_subprocess()
548+
def test_from_import_with_module_getattr_warning(self):
549+
"""Lazy from import should respect package's __getattr__."""
550+
code = textwrap.dedent("""
551+
import warnings
552+
553+
with warnings.catch_warnings(record=True) as log:
554+
lazy from test.test_lazy_import.data.pkg import warning_attr
555+
556+
assert log == []
557+
558+
with warnings.catch_warnings(record=True) as log:
559+
warning_attr
560+
assert warning_attr == 'from_warning_attr', warning_attr
561+
assert len(log) == 1, log
562+
assert isinstance(log[0].message, UserWarning), log
563+
assert str(log[0].message) == 'from_getattr', log
564+
""")
565+
assert_python_ok("-c", code)
566+
461567
@support.requires_subprocess()
462568
def test_package_from_import_with_module_getattr(self):
463569
"""Lazy from import should respect a package's __getattr__."""
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
def __getattr__(name):
22
if name == "dynamic_attr":
33
return "from_getattr"
4+
elif name == "raising_attr":
5+
raise ValueError("from_getattr")
6+
elif name == "import_error_attr":
7+
raise ImportError(name)
8+
elif name == "warning_attr":
9+
import warnings
10+
warnings.warn("from_getattr", category=UserWarning)
11+
return "from_warning_attr"
412
raise AttributeError(name)

Lib/test/test_lazy_import/data/pkg/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,12 @@
33
def __getattr__(name):
44
if name == "dynamic_attr":
55
return "from_getattr"
6+
elif name == "raising_attr":
7+
raise ValueError("from_getattr")
8+
elif name == "import_error_attr":
9+
raise ImportError(name)
10+
elif name == "warning_attr":
11+
import warnings
12+
warnings.warn("from_getattr", category=UserWarning)
13+
return "from_warning_attr"
614
raise AttributeError(name)

0 commit comments

Comments
 (0)