https://github.com/python/cpython/commit/dffac6163e693cf80ed42cdc8e2cb5c0cb9577d7
commit: dffac6163e693cf80ed42cdc8e2cb5c0cb9577d7
branch: main
author: Dino Viehland <[email protected]>
committer: DinoV <[email protected]>
date: 2026-08-14T14:17:23-07:00
summary:

gh-155194: Fix not raising on non-module import (#155189)

* Fix not raising on non-module import

* Fix traceback tests

* Fix doc string and add new test

* Fix up feedback on tests

* Add test case for lazy import dotted.name as name

files:
A Lib/test/test_lazy_import/data/lazypkg/__init__.py
A Lib/test/test_lazy_import/data/lazypkg/bar.py
M Lib/test/test_lazy_import/__init__.py
M Lib/test/test_traceback.py
M Makefile.pre.in
M Python/import.c

diff --git a/Lib/test/test_lazy_import/__init__.py 
b/Lib/test/test_lazy_import/__init__.py
index 899ecec1ba2afa4..b12e209707a9de3 100644
--- a/Lib/test/test_lazy_import/__init__.py
+++ b/Lib/test/test_lazy_import/__init__.py
@@ -678,17 +678,53 @@ def test_lazy_modules_tracks_lazy_imports(self):
 class ErrorHandlingTests(LazyImportTestCase):
     """Tests for error handling during lazy import reification."""
 
-    def test_missing_lazy_submodule_raises_attribute_error(self):
-        """Accessing a nonexistent lazy submodule via parent attr raises 
AttributeError."""
+    def test_missing_lazy_submodule_raises_module_not_found_error(self):
+        """Accessing a nonexistent lazy submodule via parent attr raises 
ModuleNotFoundError."""
         code = textwrap.dedent("""
             lazy import test.test_lazy_import.data.nonexistent_module
 
             try:
                 _ = test.test_lazy_import.data.nonexistent_module
-            except AttributeError:
+            except ModuleNotFoundError:
                 pass
             else:
-                raise AssertionError("AttributeError was not raised")
+                raise AssertionError("ModuleNotFoundError was not raised")
+        """)
+        assert_python_ok("-c", code)
+
+    def test_non_package_lazily_imported(self):
+        """Accessing a nonexistent lazy name via parent attr raises 
ModuleNotFoundError."""
+        code = textwrap.dedent("""
+            lazy import math.pi
+
+            try:
+                _ = math.pi
+            except ModuleNotFoundError:
+                pass
+            else:
+                raise AssertionError("ModuleNotFoundError was not raised")
+        """)
+        assert_python_ok("-c", code)
+
+    def test_non_package_lazily_imported_as(self):
+        """Doing a dotted lazy import as still works"""
+        code = textwrap.dedent("""
+            lazy import math.pi as pi
+            pi
+        """)
+        assert_python_ok("-c", code)
+
+    def test_missing_attribute_raises_import_error(self):
+        """Accessing a nonexistent lazy name via from import raises 
ImportError."""
+        code = textwrap.dedent("""
+            lazy from sys import doesnotexist
+
+            try:
+                _ = doesnotexist
+            except ImportError:
+                pass
+            else:
+                raise AssertionError("ImportError was not raised")
         """)
         assert_python_ok("-c", code)
 
diff --git a/Lib/test/test_lazy_import/data/lazypkg/__init__.py 
b/Lib/test/test_lazy_import/data/lazypkg/__init__.py
new file mode 100644
index 000000000000000..276b51823fee32a
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/lazypkg/__init__.py
@@ -0,0 +1 @@
+lazy from . import bar
diff --git a/Lib/test/test_lazy_import/data/lazypkg/bar.py 
b/Lib/test/test_lazy_import/data/lazypkg/bar.py
new file mode 100644
index 000000000000000..b8d8b60b886b88a
--- /dev/null
+++ b/Lib/test/test_lazy_import/data/lazypkg/bar.py
@@ -0,0 +1,2 @@
+print("BAR_MODULE_LOADED")
+def f(): pass
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index e38d0942e463e9c..8e4c28562a6cbe5 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -5611,11 +5611,11 @@ class TestLazyImportSuggestions(unittest.TestCase):
 
     def test_attribute_error_does_not_reify_lazy_imports(self):
         """Printing an AttributeError should not trigger lazy import 
reification."""
-        # pkg.bar prints "BAR_MODULE_LOADED" when imported.
+        # lazypkg.bar prints "BAR_MODULE_LOADED" when imported.
         # If lazy import is reified during suggestion computation, we'll see 
it.
         code = textwrap.dedent("""
-            lazy import test.test_lazy_import.data.pkg.bar
-            test.test_lazy_import.data.pkg.nonexistent
+            lazy import test.test_lazy_import.data.lazypkg
+            test.test_lazy_import.data.lazypkg.nonexistent
         """)
         rc, stdout, stderr = assert_python_failure('-c', code)
         self.assertNotIn(b"BAR_MODULE_LOADED", stdout)
@@ -5624,9 +5624,9 @@ def 
test_traceback_formatting_does_not_reify_lazy_imports(self):
         """Formatting a traceback should not trigger lazy import 
reification."""
         code = textwrap.dedent("""
             import traceback
-            lazy import test.test_lazy_import.data.pkg.bar
+            lazy import test.test_lazy_import.data.lazypkg
             try:
-                test.test_lazy_import.data.pkg.nonexistent
+                test.test_lazy_import.data.lazypkg.nonexistent
             except AttributeError:
                 traceback.format_exc()
             print("OK")
@@ -5638,9 +5638,9 @@ def 
test_traceback_formatting_does_not_reify_lazy_imports(self):
     def test_suggestion_still_works_for_non_lazy_attributes(self):
         """Suggestions should still work for non-lazy module attributes."""
         code = textwrap.dedent("""
-            lazy import test.test_lazy_import.data.pkg.bar
+            lazy import test.test_lazy_import.data.lazypkg
             # Typo for __name__
-            test.test_lazy_import.data.pkg.__nme__
+            test.test_lazy_import.data.lazypkg.__nme__
         """)
         rc, stdout, stderr = assert_python_failure('-c', code)
         self.assertIn(b"__name__", stderr)
diff --git a/Makefile.pre.in b/Makefile.pre.in
index 46fa26e01572cc2..d3d24a13898d992 100644
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -2690,6 +2690,7 @@ TESTSUBDIRS=      idlelib/idle_test \
                test/test_lazy_import/data/pkg \
                test/test_lazy_import/data/badsyntax \
                test/test_lazy_import/data/circular_import_pkg \
+               test/test_lazy_import/data/lazypkg \
                test/test_lazy_import/data/metasyntactic \
                test/test_lazy_import/data/metasyntactic/foo \
                test/test_lazy_import/data/metasyntactic/foo/ack \
diff --git a/Python/import.c b/Python/import.c
index 5ca78a971fa54c6..47d5296a2fec9b1 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -3940,19 +3940,6 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, 
PyObject *lazy_import)
         goto error;
     }
 
-    Py_ssize_t dot = -1;
-    int full = 0;
-    if (lz->lz_attr != NULL) {
-        full = 1;
-    }
-    if (!full) {
-        dot = PyUnicode_FindChar(lz->lz_from, '.', 0,
-                                 PyUnicode_GET_LENGTH(lz->lz_from), 1);
-    }
-    if (dot < 0) {
-        full = 1;
-    }
-
     if (lz->lz_attr != NULL) {
         if (PyUnicode_Check(lz->lz_attr)) {
             fromlist = PyTuple_New(1);
@@ -3978,23 +3965,10 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, 
PyObject *lazy_import)
         PyErr_SetString(PyExc_ImportError, "__import__ not found");
         goto error;
     }
-    if (full) {
-        obj = _PyEval_ImportNameWithImport(
-            tstate, import_func, globals, globals,
-            lz->lz_from, fromlist, _PyLong_GetZero()
-        );
-    }
-    else {
-        PyObject *name = PyUnicode_Substring(lz->lz_from, 0, dot);
-        if (name == NULL) {
-            goto error;
-        }
-        obj = _PyEval_ImportNameWithImport(
-            tstate, import_func, globals, globals,
-            name, fromlist, _PyLong_GetZero()
-        );
-        Py_DECREF(name);
-    }
+    obj = _PyEval_ImportNameWithImport(
+        tstate, import_func, globals, globals,
+        lz->lz_from, fromlist, _PyLong_GetZero()
+    );
     if (obj == NULL) {
         goto error;
     }

_______________________________________________
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