gh-135443: Sometimes Fall Back to __main__.__dict__ For Globals (gh-135491)

For several builtin functions, we now fall back to __main__.__dict__ for the globals
when there is no current frame and _PyInterpreterState_IsRunningMain() returns
true.  This allows those functions to be run with Interpreter.call().

The affected builtins:

* exec()
* eval()
* globals()
* locals()
* vars()
* dir()

We take a similar approach with "stateless" functions, which don't use any
global variables.
This commit is contained in:
Eric Snow 2025-06-16 17:34:19 -06:00 committed by GitHub
parent 68b7e1a667
commit a450a0ddec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 394 additions and 68 deletions

View file

@ -957,6 +957,7 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
PyObject *locals)
/*[clinic end generated code: output=0a0824aa70093116 input=7c7bce5299a89062]*/
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *result = NULL, *source_copy;
const char *str;
@ -970,35 +971,46 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
: "globals must be a dict");
return NULL;
}
if (globals == Py_None) {
globals = PyEval_GetGlobals();
if (locals == Py_None) {
locals = _PyEval_GetFrameLocals();
if (locals == NULL)
return NULL;
}
else {
Py_INCREF(locals);
}
int fromframe = 0;
if (globals != Py_None) {
Py_INCREF(globals);
}
else if (_PyEval_GetFrame() != NULL) {
fromframe = 1;
globals = PyEval_GetGlobals();
assert(globals != NULL);
Py_INCREF(globals);
}
else if (locals == Py_None)
locals = Py_NewRef(globals);
else {
globals = _PyEval_GetGlobalsFromRunningMain(tstate);
if (globals == NULL) {
if (!_PyErr_Occurred(tstate)) {
PyErr_SetString(PyExc_TypeError,
"eval must be given globals and locals "
"when called without a frame");
}
return NULL;
}
Py_INCREF(globals);
}
if (locals != Py_None) {
Py_INCREF(locals);
}
if (globals == NULL || locals == NULL) {
PyErr_SetString(PyExc_TypeError,
"eval must be given globals and locals "
"when called without a frame");
goto error;
else if (fromframe) {
locals = _PyEval_GetFrameLocals();
if (locals == NULL) {
assert(PyErr_Occurred());
Py_DECREF(globals);
return NULL;
}
}
else {
locals = Py_NewRef(globals);
}
int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
if (r == 0) {
r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
}
if (r < 0) {
if (_PyEval_EnsureBuiltins(tstate, globals, NULL) < 0) {
goto error;
}
@ -1039,6 +1051,7 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
}
error:
Py_XDECREF(globals);
Py_XDECREF(locals);
return result;
}
@ -1069,29 +1082,44 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
PyObject *locals, PyObject *closure)
/*[clinic end generated code: output=7579eb4e7646743d input=25e989b6d87a3a21]*/
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *v;
if (globals == Py_None) {
int fromframe = 0;
if (globals != Py_None) {
Py_INCREF(globals);
}
else if (_PyEval_GetFrame() != NULL) {
fromframe = 1;
globals = PyEval_GetGlobals();
if (locals == Py_None) {
locals = _PyEval_GetFrameLocals();
if (locals == NULL)
return NULL;
assert(globals != NULL);
Py_INCREF(globals);
}
else {
globals = _PyEval_GetGlobalsFromRunningMain(tstate);
if (globals == NULL) {
if (!_PyErr_Occurred(tstate)) {
PyErr_SetString(PyExc_SystemError,
"globals and locals cannot be NULL");
}
goto error;
}
else {
Py_INCREF(locals);
}
if (!globals || !locals) {
PyErr_SetString(PyExc_SystemError,
"globals and locals cannot be NULL");
Py_INCREF(globals);
}
if (locals != Py_None) {
Py_INCREF(locals);
}
else if (fromframe) {
locals = _PyEval_GetFrameLocals();
if (locals == NULL) {
assert(PyErr_Occurred());
Py_DECREF(globals);
return NULL;
}
}
else if (locals == Py_None) {
locals = Py_NewRef(globals);
}
else {
Py_INCREF(locals);
locals = Py_NewRef(globals);
}
if (!PyDict_Check(globals)) {
@ -1105,11 +1133,8 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
Py_TYPE(locals)->tp_name);
goto error;
}
int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
if (r == 0) {
r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
}
if (r < 0) {
if (_PyEval_EnsureBuiltins(tstate, globals, NULL) < 0) {
goto error;
}
@ -1186,11 +1211,13 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
}
if (v == NULL)
goto error;
Py_DECREF(globals);
Py_DECREF(locals);
Py_DECREF(v);
Py_RETURN_NONE;
error:
Py_XDECREF(globals);
Py_XDECREF(locals);
return NULL;
}
@ -1240,10 +1267,21 @@ static PyObject *
builtin_globals_impl(PyObject *module)
/*[clinic end generated code: output=e5dd1527067b94d2 input=9327576f92bb48ba]*/
{
PyObject *d;
d = PyEval_GetGlobals();
return Py_XNewRef(d);
PyObject *globals;
if (_PyEval_GetFrame() != NULL) {
globals = PyEval_GetGlobals();
assert(globals != NULL);
return Py_NewRef(globals);
}
PyThreadState *tstate = _PyThreadState_GET();
globals = _PyEval_GetGlobalsFromRunningMain(tstate);
if (globals == NULL) {
if (_PyErr_Occurred(tstate)) {
return NULL;
}
Py_RETURN_NONE;
}
return Py_NewRef(globals);
}
@ -1887,7 +1925,21 @@ static PyObject *
builtin_locals_impl(PyObject *module)
/*[clinic end generated code: output=b46c94015ce11448 input=7874018d478d5c4b]*/
{
return _PyEval_GetFrameLocals();
PyObject *locals;
if (_PyEval_GetFrame() != NULL) {
locals = _PyEval_GetFrameLocals();
assert(locals != NULL || PyErr_Occurred());
return locals;
}
PyThreadState *tstate = _PyThreadState_GET();
locals = _PyEval_GetGlobalsFromRunningMain(tstate);
if (locals == NULL) {
if (_PyErr_Occurred(tstate)) {
return NULL;
}
Py_RETURN_NONE;
}
return Py_NewRef(locals);
}
@ -2623,7 +2675,22 @@ builtin_vars(PyObject *self, PyObject *args)
if (!PyArg_UnpackTuple(args, "vars", 0, 1, &v))
return NULL;
if (v == NULL) {
d = _PyEval_GetFrameLocals();
if (_PyEval_GetFrame() != NULL) {
d = _PyEval_GetFrameLocals();
}
else {
PyThreadState *tstate = _PyThreadState_GET();
d = _PyEval_GetGlobalsFromRunningMain(tstate);
if (d == NULL) {
if (!_PyErr_Occurred(tstate)) {
d = _PyEval_GetFrameLocals();
assert(_PyErr_Occurred(tstate));
}
}
else {
Py_INCREF(d);
}
}
}
else {
if (PyObject_GetOptionalAttr(v, &_Py_ID(__dict__), &d) == 0) {

View file

@ -2746,10 +2746,9 @@ _PyEval_GetFrameLocals(void)
return locals;
}
PyObject *
PyEval_GetGlobals(void)
static PyObject *
_PyEval_GetGlobals(PyThreadState *tstate)
{
PyThreadState *tstate = _PyThreadState_GET();
_PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate);
if (current_frame == NULL) {
return NULL;
@ -2757,6 +2756,120 @@ PyEval_GetGlobals(void)
return current_frame->f_globals;
}
PyObject *
PyEval_GetGlobals(void)
{
PyThreadState *tstate = _PyThreadState_GET();
return _PyEval_GetGlobals(tstate);
}
PyObject *
_PyEval_GetGlobalsFromRunningMain(PyThreadState *tstate)
{
if (!_PyInterpreterState_IsRunningMain(tstate->interp)) {
return NULL;
}
PyObject *mod = _Py_GetMainModule(tstate);
if (_Py_CheckMainModule(mod) < 0) {
Py_XDECREF(mod);
return NULL;
}
PyObject *globals = PyModule_GetDict(mod); // borrowed
Py_DECREF(mod);
return globals;
}
static PyObject *
get_globals_builtins(PyObject *globals)
{
PyObject *builtins = NULL;
if (PyDict_Check(globals)) {
if (PyDict_GetItemRef(globals, &_Py_ID(__builtins__), &builtins) < 0) {
return NULL;
}
}
else {
if (PyMapping_GetOptionalItem(
globals, &_Py_ID(__builtins__), &builtins) < 0)
{
return NULL;
}
}
return builtins;
}
static int
set_globals_builtins(PyObject *globals, PyObject *builtins)
{
if (PyDict_Check(globals)) {
if (PyDict_SetItem(globals, &_Py_ID(__builtins__), builtins) < 0) {
return -1;
}
}
else {
if (PyObject_SetItem(globals, &_Py_ID(__builtins__), builtins) < 0) {
return -1;
}
}
return 0;
}
int
_PyEval_EnsureBuiltins(PyThreadState *tstate, PyObject *globals,
PyObject **p_builtins)
{
PyObject *builtins = get_globals_builtins(globals);
if (builtins == NULL) {
if (_PyErr_Occurred(tstate)) {
return -1;
}
builtins = PyEval_GetBuiltins(); // borrowed
if (builtins == NULL) {
assert(_PyErr_Occurred(tstate));
return -1;
}
Py_INCREF(builtins);
if (set_globals_builtins(globals, builtins) < 0) {
Py_DECREF(builtins);
return -1;
}
}
if (p_builtins != NULL) {
*p_builtins = builtins;
}
else {
Py_DECREF(builtins);
}
return 0;
}
int
_PyEval_EnsureBuiltinsWithModule(PyThreadState *tstate, PyObject *globals,
PyObject **p_builtins)
{
PyObject *builtins = get_globals_builtins(globals);
if (builtins == NULL) {
if (_PyErr_Occurred(tstate)) {
return -1;
}
builtins = PyImport_ImportModuleLevel("builtins", NULL, NULL, NULL, 0);
if (builtins == NULL) {
return -1;
}
if (set_globals_builtins(globals, builtins) < 0) {
Py_DECREF(builtins);
return -1;
}
}
if (p_builtins != NULL) {
*p_builtins = builtins;
}
else {
Py_DECREF(builtins);
}
return 0;
}
PyObject*
PyEval_GetFrameLocals(void)
{

View file

@ -722,16 +722,26 @@ _PyFunction_FromXIData(_PyXIData_t *xidata)
return NULL;
}
// Create a new function.
// For stateless functions (no globals) we use __main__ as __globals__,
// just like we do for builtins like exec().
assert(PyCode_Check(code));
PyObject *globals = PyDict_New();
if (globals == NULL) {
Py_DECREF(code);
return NULL;
}
PyThreadState *tstate = _PyThreadState_GET();
if (PyDict_SetItem(globals, &_Py_ID(__builtins__),
tstate->interp->builtins) < 0)
{
PyObject *globals = _PyEval_GetGlobalsFromRunningMain(tstate); // borrowed
if (globals == NULL) {
if (_PyErr_Occurred(tstate)) {
Py_DECREF(code);
return NULL;
}
globals = PyDict_New();
if (globals == NULL) {
Py_DECREF(code);
return NULL;
}
}
else {
Py_INCREF(globals);
}
if (_PyEval_EnsureBuiltins(tstate, globals, NULL) < 0) {
Py_DECREF(code);
Py_DECREF(globals);
return NULL;

View file

@ -3960,25 +3960,28 @@ PyImport_Import(PyObject *module_name)
}
/* Get the builtins from current globals */
globals = PyEval_GetGlobals();
globals = PyEval_GetGlobals(); // borrowed
if (globals != NULL) {
Py_INCREF(globals);
// XXX Use _PyEval_EnsureBuiltins()?
builtins = PyObject_GetItem(globals, &_Py_ID(__builtins__));
if (builtins == NULL) {
// XXX Fall back to interp->builtins or sys.modules['builtins']?
goto err;
}
}
else if (_PyErr_Occurred(tstate)) {
goto err;
}
else {
/* No globals -- use standard builtins, and fake globals */
builtins = PyImport_ImportModuleLevel("builtins",
NULL, NULL, NULL, 0);
if (builtins == NULL) {
globals = PyDict_New();
if (globals == NULL) {
goto err;
}
globals = Py_BuildValue("{OO}", &_Py_ID(__builtins__), builtins);
if (globals == NULL)
if (_PyEval_EnsureBuiltinsWithModule(tstate, globals, &builtins) < 0) {
goto err;
}
}
/* Get the __import__ function from the builtins */