https://github.com/python/cpython/commit/6dbf4ba403cd38d0219d3c7514f61c2ac8f6a74f
commit: 6dbf4ba403cd38d0219d3c7514f61c2ac8f6a74f
branch: main
author: sobolevn <[email protected]>
committer: sobolevn <[email protected]>
date: 2026-05-21T12:21:45+03:00
summary:

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

files:
M Lib/test/test_lazy_import/__init__.py
M Lib/test/test_lazy_import/data/module_with_getattr.py
M Lib/test/test_lazy_import/data/pkg/__init__.py

diff --git a/Lib/test/test_lazy_import/__init__.py 
b/Lib/test/test_lazy_import/__init__.py
index 9b440b8859d662b..1298d532b91b97c 100644
--- a/Lib/test/test_lazy_import/__init__.py
+++ b/Lib/test/test_lazy_import/__init__.py
@@ -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__."""
diff --git a/Lib/test/test_lazy_import/data/module_with_getattr.py 
b/Lib/test/test_lazy_import/data/module_with_getattr.py
index 2ac01a90d76e620..db3a2301075c2ee 100644
--- a/Lib/test/test_lazy_import/data/module_with_getattr.py
+++ b/Lib/test/test_lazy_import/data/module_with_getattr.py
@@ -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)
diff --git a/Lib/test/test_lazy_import/data/pkg/__init__.py 
b/Lib/test/test_lazy_import/data/pkg/__init__.py
index e526aab94131b86..5f7b8662596cac6 100644
--- a/Lib/test/test_lazy_import/data/pkg/__init__.py
+++ b/Lib/test/test_lazy_import/data/pkg/__init__.py
@@ -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)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to