From a5d2667247f53efa776cbbf59549bbbedb0d0315 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 16 Jun 2026 00:41:41 +0900 Subject: [PATCH 1/4] Fix __lazy_import__ without frame --- Python/bltinmodule.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index d5129bf6a5a6bc0..fa64255be00e75d 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -313,6 +313,12 @@ builtin___lazy_import___impl(PyObject *module, PyObject *name, PyThreadState *tstate = PyThreadState_GET(); if (globals == NULL) { globals = PyEval_GetGlobals(); + if (globals == NULL) { + PyErr_SetString(PyExc_TypeError, + "__lazy_import__() missing globals " + "when called without a frame"); + return NULL; + } } if (locals == NULL) { locals = globals; From 04a8160a09b90d9204d325a47da02ff162236e89 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 16 Jun 2026 00:45:56 +0900 Subject: [PATCH 2/4] Blurb it --- .../2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst new file mode 100644 index 000000000000000..1b398c4d5e486d3 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst @@ -0,0 +1,2 @@ +Fix a crash in :func:`__lazy_import__` when called without an explicit +``globals`` argument and without a current Python frame. From 6d8002f3e6e7b6389f311bd45fa61ff1d6b9e478 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 16 Jun 2026 00:53:23 +0900 Subject: [PATCH 3/4] Fix news entry --- .../2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst index 1b398c4d5e486d3..cfa5ee8d3839c1b 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-16-00-45-42.gh-issue-151510.HJ-kGn.rst @@ -1,2 +1,2 @@ -Fix a crash in :func:`__lazy_import__` when called without an explicit +Fix a crash in :func:`!__lazy_import__` when called without an explicit ``globals`` argument and without a current Python frame. From 6ba3896663aa6deb8d05155011f48879528989d4 Mon Sep 17 00:00:00 2001 From: AN Long Date: Tue, 16 Jun 2026 21:49:41 +0900 Subject: [PATCH 4/4] Add regression test --- Lib/test/test_lazy_import/__init__.py | 11 +++++++++++ Modules/_testcapi/import.c | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/Lib/test/test_lazy_import/__init__.py b/Lib/test/test_lazy_import/__init__.py index 1724beb8ce69517..1c5ab4ef73da2fb 100644 --- a/Lib/test/test_lazy_import/__init__.py +++ b/Lib/test/test_lazy_import/__init__.py @@ -1949,6 +1949,17 @@ def filter(*args): def test_set_bad_filter(self): self.assertRaises(ValueError, _testcapi.PyImport_SetLazyImportsFilter, 42) + def test_dunder_lazy_import_without_frame(self): + # gh-151510: __lazy_import__() called with no globals and no running + # Python frame must raise TypeError instead of crashing. + with self.assertRaisesRegex( + TypeError, + r"__lazy_import__\(\) missing globals when called without a frame", + ): + _testcapi.lazy_import_without_frame( + "test.test_lazy_import.data.basic2" + ) + if __name__ == '__main__': unittest.main() diff --git a/Modules/_testcapi/import.c b/Modules/_testcapi/import.c index 384a8f52da4b984..11d0e6acaebe1f2 100644 --- a/Modules/_testcapi/import.c +++ b/Modules/_testcapi/import.c @@ -1,6 +1,27 @@ #include "parts.h" #include "util.h" +static PyObject * +pyimport_lazyimportwithoutframe(PyObject *self, PyObject *name) +{ + PyObject *lazy_import = PyImport_ImportModuleAttrString("builtins", + "__lazy_import__"); + if (lazy_import == NULL) { + return NULL; + } + + // Simulate being called with no running Python frame (e.g. from a freshly + // attached C thread), so that PyEval_GetGlobals() returns NULL. + PyThreadState *tstate = PyThreadState_Get(); + struct _PyInterpreterFrame *saved = tstate->current_frame; + tstate->current_frame = NULL; + PyObject *res = PyObject_CallOneArg(lazy_import, name); + tstate->current_frame = saved; + + Py_DECREF(lazy_import); + return res; +} + // Test PyImport_ImportModuleAttr() static PyObject * pyimport_importmoduleattr(PyObject *self, PyObject *args) @@ -95,6 +116,7 @@ static PyMethodDef test_methods[] = { {"PyImport_GetLazyImportsMode", pyimport_getlazyimportsmode, METH_NOARGS}, {"PyImport_SetLazyImportsFilter", pyimport_setlazyimportsfilter, METH_VARARGS}, {"PyImport_GetLazyImportsFilter", pyimport_getlazyimportsfilter, METH_NOARGS}, + {"lazy_import_without_frame", pyimport_lazyimportwithoutframe, METH_O}, {NULL}, };