Skip to content

Commit 2b92f57

Browse files
[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 2d0003c) Co-authored-by: Brandt Bucher <brandt@python.org>
1 parent d97d52c commit 2b92f57

3 files changed

Lines changed: 43 additions & 8 deletions

File tree

Lib/test/test_lazy_import/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,31 @@ def test_dunder_lazy_import_used(self):
572572
import test.test_lazy_import.data.dunder_lazy_import_used
573573
self.assertIn("test.test_lazy_import.data.basic2", sys.modules)
574574

575+
@support.requires_subprocess()
576+
def test_dunder_lazy_import_fromlist_resolves_to_module(self):
577+
for fromlist in ["basic2", ("basic2",)]:
578+
with self.subTest(fromlist=fromlist):
579+
code = textwrap.dedent(f"""
580+
import sys
581+
import types
582+
583+
lazy = __lazy_import__("test.test_lazy_import.data", fromlist={fromlist!r})
584+
585+
def check():
586+
lazy_obj = globals()["lazy"]
587+
assert type(lazy_obj) is types.LazyImportType, lazy_obj
588+
assert "test.test_lazy_import.data.basic2" not in sys.modules
589+
590+
resolved = lazy_obj.resolve()
591+
assert type(resolved) is types.ModuleType, resolved
592+
assert "test.test_lazy_import.data.basic2" in sys.modules
593+
assert resolved.__name__ == "test.test_lazy_import.data"
594+
assert resolved.basic2.x == 42
595+
596+
check()
597+
""")
598+
assert_python_ok("-c", code)
599+
575600
def test_dunder_lazy_import_invalid_arguments(self):
576601
"""__lazy_import__ should reject invalid arguments."""
577602
for invalid_name in (b"", 123, None):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix an inconsistency where calling ``__lazy_import__`` with a string
2+
``fromlist`` would return a :class:`types.LazyImportType` that resolves to
3+
the named member, rather than the module being imported.

Python/import.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4536,7 +4536,7 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
45364536
}
45374537
if (fromlist == NULL) {
45384538
assert(!PyErr_Occurred());
4539-
fromlist = Py_NewRef(Py_None);
4539+
fromlist = Py_None;
45404540
}
45414541
PyObject *args[] = {modname, abs_name, fromlist};
45424542
PyObject *res = PyObject_Vectorcall(filter, args, 3, NULL);
@@ -4565,8 +4565,19 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
45654565
}
45664566

45674567
// here, 'filter' is either NULL or is equivalent to a borrowed reference
4568+
if (fromlist && PyUnicode_Check(fromlist)) {
4569+
fromlist = PyTuple_Pack(1, fromlist);
4570+
if (fromlist == NULL) {
4571+
Py_DECREF(abs_name);
4572+
return NULL;
4573+
}
4574+
}
4575+
else {
4576+
Py_XINCREF(fromlist);
4577+
}
45684578
PyObject *res = _PyLazyImport_New(frame, builtins, abs_name, fromlist);
45694579
if (res == NULL) {
4580+
Py_XDECREF(fromlist);
45704581
Py_DECREF(abs_name);
45714582
return NULL;
45724583
}
@@ -4577,13 +4588,7 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
45774588
goto error;
45784589
}
45794590

4580-
if (fromlist && PyUnicode_Check(fromlist)) {
4581-
if (register_from_lazy_on_parent(tstate, abs_name, fromlist) < 0) {
4582-
goto error;
4583-
}
4584-
}
4585-
else if (fromlist && PyTuple_Check(fromlist) &&
4586-
PyTuple_GET_SIZE(fromlist)) {
4591+
if (fromlist && PyTuple_Check(fromlist) && PyTuple_GET_SIZE(fromlist)) {
45874592
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(fromlist); i++) {
45884593
if (register_from_lazy_on_parent(tstate, abs_name,
45894594
PyTuple_GET_ITEM(fromlist, i)) < 0)
@@ -4596,9 +4601,11 @@ _PyImport_LazyImportModuleLevelObject(PyThreadState *tstate,
45964601
goto error;
45974602
}
45984603

4604+
Py_XDECREF(fromlist);
45994605
Py_DECREF(abs_name);
46004606
return res;
46014607
error:
4608+
Py_XDECREF(fromlist);
46024609
Py_DECREF(abs_name);
46034610
Py_DECREF(res);
46044611
return NULL;

0 commit comments

Comments
 (0)