https://github.com/python/cpython/commit/2b92f57cac456a8c28fe4a0711eb74887395cac3
commit: 2b92f57cac456a8c28fe4a0711eb74887395cac3
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-06-29T10:23:25+01:00
summary:

[3.15] GH-151672: `__lazy_import__` always resolves to the module being 
imported (GH-151827) (#152534)

GH-151672: `__lazy_import__` always resolves to the module being imported 
(GH-151827)
(cherry picked from commit 2d0003c0b28dca86197f4b74810966856a27dc60)

Co-authored-by: Brandt Bucher <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-06-20-10-01-26.gh-issue-151672.K-w7j0.rst
M Lib/test/test_lazy_import/__init__.py
M Python/import.c

diff --git a/Lib/test/test_lazy_import/__init__.py 
b/Lib/test/test_lazy_import/__init__.py
index 2c9dd0627f697c..417eab84c1088d 100644
--- a/Lib/test/test_lazy_import/__init__.py
+++ b/Lib/test/test_lazy_import/__init__.py
@@ -572,6 +572,31 @@ def test_dunder_lazy_import_used(self):
         import test.test_lazy_import.data.dunder_lazy_import_used
         self.assertIn("test.test_lazy_import.data.basic2", sys.modules)
 
+    @support.requires_subprocess()
+    def test_dunder_lazy_import_fromlist_resolves_to_module(self):
+        for fromlist in ["basic2", ("basic2",)]:
+            with self.subTest(fromlist=fromlist):
+                code = textwrap.dedent(f"""
+                    import sys
+                    import types
+
+                    lazy = __lazy_import__("test.test_lazy_import.data", 
fromlist={fromlist!r})
+
+                    def check():
+                        lazy_obj = globals()["lazy"]
+                        assert type(lazy_obj) is types.LazyImportType, lazy_obj
+                        assert "test.test_lazy_import.data.basic2" not in 
sys.modules
+
+                        resolved = lazy_obj.resolve()
+                        assert type(resolved) is types.ModuleType, resolved
+                        assert "test.test_lazy_import.data.basic2" in 
sys.modules
+                        assert resolved.__name__ == 
"test.test_lazy_import.data"
+                        assert resolved.basic2.x == 42
+
+                    check()
+                """)
+                assert_python_ok("-c", code)
+
     def test_dunder_lazy_import_invalid_arguments(self):
         """__lazy_import__ should reject invalid arguments."""
         for invalid_name in (b"", 123, None):
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-20-10-01-26.gh-issue-151672.K-w7j0.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-20-10-01-26.gh-issue-151672.K-w7j0.rst
new file mode 100644
index 00000000000000..4f4823df0b2101
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-20-10-01-26.gh-issue-151672.K-w7j0.rst
@@ -0,0 +1,3 @@
+Fix an inconsistency where calling ``__lazy_import__`` with a string
+``fromlist`` would return a :class:`types.LazyImportType` that resolves to
+the named member, rather than the module being imported.
diff --git a/Python/import.c b/Python/import.c
index 63021208a23d3b..2c975295f9b5d3 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -4536,7 +4536,7 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState 
*tstate,
         }
         if (fromlist == NULL) {
             assert(!PyErr_Occurred());
-            fromlist = Py_NewRef(Py_None);
+            fromlist = Py_None;
         }
         PyObject *args[] = {modname, abs_name, fromlist};
         PyObject *res = PyObject_Vectorcall(filter, args, 3, NULL);
@@ -4565,8 +4565,19 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState 
*tstate,
     }
 
     // here, 'filter' is either NULL or is equivalent to a borrowed reference
+    if (fromlist && PyUnicode_Check(fromlist)) {
+        fromlist = PyTuple_Pack(1, fromlist);
+        if (fromlist == NULL) {
+            Py_DECREF(abs_name);
+            return NULL;
+        }
+    }
+    else {
+        Py_XINCREF(fromlist);
+    }
     PyObject *res = _PyLazyImport_New(frame, builtins, abs_name, fromlist);
     if (res == NULL) {
+        Py_XDECREF(fromlist);
         Py_DECREF(abs_name);
         return NULL;
     }
@@ -4577,13 +4588,7 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState 
*tstate,
         goto error;
     }
 
-    if (fromlist && PyUnicode_Check(fromlist)) {
-        if (register_from_lazy_on_parent(tstate, abs_name, fromlist) < 0) {
-            goto error;
-        }
-    }
-    else if (fromlist && PyTuple_Check(fromlist) &&
-             PyTuple_GET_SIZE(fromlist)) {
+    if (fromlist && PyTuple_Check(fromlist) && PyTuple_GET_SIZE(fromlist)) {
         for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(fromlist); i++) {
             if (register_from_lazy_on_parent(tstate, abs_name,
                                              PyTuple_GET_ITEM(fromlist, i)) < 
0)
@@ -4596,9 +4601,11 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState 
*tstate,
         goto error;
     }
 
+    Py_XDECREF(fromlist);
     Py_DECREF(abs_name);
     return res;
 error:
+    Py_XDECREF(fromlist);
     Py_DECREF(abs_name);
     Py_DECREF(res);
     return NULL;

_______________________________________________
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