From 400759193bdfa399baeaf7585802da530197015c Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Sat, 20 Jun 2026 09:53:16 -0700 Subject: [PATCH 1/2] Fix lazy import string fromlist resolution --- Lib/test/test_lazy_import/__init__.py | 25 +++++++++++++++++++++++++ Python/import.c | 23 +++++++++++++++-------- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_lazy_import/__init__.py b/Lib/test/test_lazy_import/__init__.py index 1724beb8ce69517..75b4b7cee482baa 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/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; From 58a062dfbb6a09406623eae3ba7c36e57dbb6950 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Sat, 20 Jun 2026 10:01:37 -0700 Subject: [PATCH 2/2] blurb add --- .../2026-06-20-10-01-26.gh-issue-151672.K-w7j0.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-20-10-01-26.gh-issue-151672.K-w7j0.rst 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.