Skip to content

Commit a56a271

Browse files
authored
[3.15] gh-149981: Test lazy import corner cases with module-level __getattr__ (GH-149982) (#150185)
(cherry picked from commit 6dbf4ba)
1 parent dea552c commit a56a271

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__."""
@@ -482,6 +535,59 @@ def test_lazy_from_import_does_not_pollute_parent(self):
482535
""")
483536
assert_python_ok("-c", code)
484537

538+
@support.requires_subprocess()
539+
def test_package_from_import_with_module_getattr_raising(self):
540+
"""Lazy from import should respect a package's __getattr__."""
541+
code = textwrap.dedent("""
542+
lazy from test.test_lazy_import.data.pkg import raising_attr
543+
544+
try:
545+
raising_attr
546+
except ValueError as exc:
547+
assert str(exc) == 'from_getattr', exc
548+
else:
549+
assert False, f'ValueError is not raised: {raising_attr}'
550+
""")
551+
assert_python_ok("-c", code)
552+
553+
@support.requires_subprocess()
554+
def test_package_from_import_with_module_getattr_missing(self):
555+
"""Lazy from import should respect package's __getattr__."""
556+
for attr in ("missing_attr", "import_error_attr"):
557+
with self.subTest(attr=attr):
558+
code = textwrap.dedent(f"""
559+
lazy from test.test_lazy_import.data.pkg import {attr}
560+
561+
try:
562+
{attr}
563+
except ImportError as exc:
564+
assert '{attr}' in str(exc), exc
565+
assert exc.__cause__ is not None
566+
else:
567+
assert False, ('ImportError is not raised', {attr})
568+
""")
569+
assert_python_ok("-c", code)
570+
571+
@support.requires_subprocess()
572+
def test_from_import_with_module_getattr_warning(self):
573+
"""Lazy from import should respect package's __getattr__."""
574+
code = textwrap.dedent("""
575+
import warnings
576+
577+
with warnings.catch_warnings(record=True) as log:
578+
lazy from test.test_lazy_import.data.pkg import warning_attr
579+
580+
assert log == []
581+
582+
with warnings.catch_warnings(record=True) as log:
583+
warning_attr
584+
assert warning_attr == 'from_warning_attr', warning_attr
585+
assert len(log) == 1, log
586+
assert isinstance(log[0].message, UserWarning), log
587+
assert str(log[0].message) == 'from_getattr', log
588+
""")
589+
assert_python_ok("-c", code)
590+
485591
@support.requires_subprocess()
486592
def test_package_from_import_with_module_getattr(self):
487593
"""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)