Skip to content

Commit 8fe5897

Browse files
miss-islingtonaisk
andauthored
[3.15] gh-151510: Fix __lazy_import__ without frame (GH-151511) (#151610)
gh-151510: Fix __lazy_import__ without frame (GH-151511) (cherry picked from commit eff805b) Co-authored-by: AN Long <aisk@users.noreply.github.com>
1 parent 6d29a08 commit 8fe5897

4 files changed

Lines changed: 41 additions & 0 deletions

File tree

Lib/test/test_lazy_import/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,6 +1957,17 @@ def filter(*args):
19571957
def test_set_bad_filter(self):
19581958
self.assertRaises(ValueError, _testcapi.PyImport_SetLazyImportsFilter, 42)
19591959

1960+
def test_dunder_lazy_import_without_frame(self):
1961+
# gh-151510: __lazy_import__() called with no globals and no running
1962+
# Python frame must raise TypeError instead of crashing.
1963+
with self.assertRaisesRegex(
1964+
TypeError,
1965+
r"__lazy_import__\(\) missing globals when called without a frame",
1966+
):
1967+
_testcapi.lazy_import_without_frame(
1968+
"test.test_lazy_import.data.basic2"
1969+
)
1970+
19601971

19611972
if __name__ == '__main__':
19621973
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash in :func:`!__lazy_import__` when called without an explicit
2+
``globals`` argument and without a current Python frame.

Modules/_testcapi/import.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
#include "parts.h"
22
#include "util.h"
33

4+
static PyObject *
5+
pyimport_lazyimportwithoutframe(PyObject *self, PyObject *name)
6+
{
7+
PyObject *lazy_import = PyImport_ImportModuleAttrString("builtins",
8+
"__lazy_import__");
9+
if (lazy_import == NULL) {
10+
return NULL;
11+
}
12+
13+
// Simulate being called with no running Python frame (e.g. from a freshly
14+
// attached C thread), so that PyEval_GetGlobals() returns NULL.
15+
PyThreadState *tstate = PyThreadState_Get();
16+
struct _PyInterpreterFrame *saved = tstate->current_frame;
17+
tstate->current_frame = NULL;
18+
PyObject *res = PyObject_CallOneArg(lazy_import, name);
19+
tstate->current_frame = saved;
20+
21+
Py_DECREF(lazy_import);
22+
return res;
23+
}
24+
425
// Test PyImport_ImportModuleAttr()
526
static PyObject *
627
pyimport_importmoduleattr(PyObject *self, PyObject *args)
@@ -95,6 +116,7 @@ static PyMethodDef test_methods[] = {
95116
{"PyImport_GetLazyImportsMode", pyimport_getlazyimportsmode, METH_NOARGS},
96117
{"PyImport_SetLazyImportsFilter", pyimport_setlazyimportsfilter, METH_VARARGS},
97118
{"PyImport_GetLazyImportsFilter", pyimport_getlazyimportsfilter, METH_NOARGS},
119+
{"lazy_import_without_frame", pyimport_lazyimportwithoutframe, METH_O},
98120
{NULL},
99121
};
100122

Python/bltinmodule.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,12 @@ builtin___lazy_import___impl(PyObject *module, PyObject *name,
313313
PyThreadState *tstate = PyThreadState_GET();
314314
if (globals == NULL) {
315315
globals = PyEval_GetGlobals();
316+
if (globals == NULL) {
317+
PyErr_SetString(PyExc_TypeError,
318+
"__lazy_import__() missing globals "
319+
"when called without a frame");
320+
return NULL;
321+
}
316322
}
317323
if (locals == NULL) {
318324
locals = globals;

0 commit comments

Comments
 (0)