https://github.com/python/cpython/commit/2d0003c0b28dca86197f4b74810966856a27dc60
commit: 2d0003c0b28dca86197f4b74810966856a27dc60
branch: main
author: Brandt Bucher <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-06-29T00:13:12+01:00
summary:
GH-151672: `__lazy_import__` always resolves to the module being imported
(#151827)
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 e145ae060d7150f..cf1d3eb793a8b94 100644
--- a/Lib/test/test_lazy_import/__init__.py
+++ b/Lib/test/test_lazy_import/__init__.py
@@ -564,6 +564,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 000000000000000..4f4823df0b21015
--- /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 6da6faf5f28cc3b..9b315b3125bd942 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -4539,7 +4539,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);
@@ -4568,8 +4568,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;
}
@@ -4580,13 +4591,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)
@@ -4599,9 +4604,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]