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.
extern _PyInterpreterFrame* _PyEval_GetFrame(void);
+extern PyObject * _PyEval_GetGlobalsFromRunningMain(PyThreadState *);
+extern int _PyEval_EnsureBuiltins(
+ PyThreadState *,
+ PyObject *,
+ PyObject **p_builtins);
+extern int _PyEval_EnsureBuiltinsWithModule(
+ PyThreadState *,
+ PyObject *,
+ PyObject **p_builtins);
+
PyAPI_FUNC(PyObject *)_Py_MakeCoro(PyFunctionObject *func);
/* Handle signals, pending calls, GIL drop request
with self.assertRaises(interpreters.NotShareableError):
interp.call(func, op, 'eggs!')
+ def test_callable_requires_frame(self):
+ # There are various functions that require a current frame.
+ interp = interpreters.create()
+ for call, expected in [
+ ((eval, '[1, 2, 3]'),
+ [1, 2, 3]),
+ ((eval, 'sum([1, 2, 3])'),
+ 6),
+ ((exec, '...'),
+ None),
+ ]:
+ with self.subTest(str(call)):
+ res = interp.call(*call)
+ self.assertEqual(res, expected)
+
+ result_not_pickleable = [
+ globals,
+ locals,
+ vars,
+ ]
+ for func, expectedtype in {
+ globals: dict,
+ locals: dict,
+ vars: dict,
+ dir: list,
+ }.items():
+ with self.subTest(str(func)):
+ if func in result_not_pickleable:
+ with self.assertRaises(interpreters.NotShareableError):
+ interp.call(func)
+ else:
+ res = interp.call(func)
+ self.assertIsInstance(res, expectedtype)
+ self.assertIn('__builtins__', res)
+
+ def test_globals_from_builtins(self):
+ # The builtins exec(), eval(), globals(), locals(), vars(),
+ # and dir() each runs relative to the target interpreter's
+ # __main__ module, when called directly. However,
+ # globals(), locals(), and vars() don't work when called
+ # directly so we don't check them.
+ from _frozen_importlib import BuiltinImporter
+ interp = interpreters.create()
+
+ names = interp.call(dir)
+ self.assertEqual(names, [
+ '__builtins__',
+ '__doc__',
+ '__loader__',
+ '__name__',
+ '__package__',
+ '__spec__',
+ ])
+
+ values = {name: interp.call(eval, name)
+ for name in names if name != '__builtins__'}
+ self.assertEqual(values, {
+ '__name__': '__main__',
+ '__doc__': None,
+ '__spec__': None, # It wasn't imported, so no module spec?
+ '__package__': None,
+ '__loader__': BuiltinImporter,
+ })
+ with self.assertRaises(ExecutionFailed):
+ interp.call(eval, 'spam'),
+
+ interp.call(exec, f'assert dir() == {names}')
+
+ # Update the interpreter's __main__.
+ interp.prepare_main(spam=42)
+ expected = names + ['spam']
+
+ names = interp.call(dir)
+ self.assertEqual(names, expected)
+
+ value = interp.call(eval, 'spam')
+ self.assertEqual(value, 42)
+
+ interp.call(exec, f'assert dir() == {expected}, dir()')
+
+ def test_globals_from_stateless_func(self):
+ # A stateless func, which doesn't depend on any globals,
+ # doesn't go through pickle, so it runs in __main__.
+ def set_global(name, value):
+ globals()[name] = value
+
+ def get_global(name):
+ return globals().get(name)
+
+ interp = interpreters.create()
+
+ modname = interp.call(get_global, '__name__')
+ self.assertEqual(modname, '__main__')
+
+ res = interp.call(get_global, 'spam')
+ self.assertIsNone(res)
+
+ interp.exec('spam = True')
+ res = interp.call(get_global, 'spam')
+ self.assertTrue(res)
+
+ interp.call(set_global, 'spam', 42)
+ res = interp.call(get_global, 'spam')
+ self.assertEqual(res, 42)
+
+ interp.exec('assert spam == 42, repr(spam)')
+
def test_call_in_thread(self):
interp = interpreters.create()
PyObject *names;
PyObject *locals;
- locals = _PyEval_GetFrameLocals();
- if (locals == NULL)
+ if (_PyEval_GetFrame() != NULL) {
+ locals = _PyEval_GetFrameLocals();
+ }
+ else {
+ PyThreadState *tstate = _PyThreadState_GET();
+ locals = _PyEval_GetGlobalsFromRunningMain(tstate);
+ if (locals == NULL) {
+ if (!_PyErr_Occurred(tstate)) {
+ locals = _PyEval_GetFrameLocals();
+ assert(_PyErr_Occurred(tstate));
+ }
+ }
+ else {
+ Py_INCREF(locals);
+ }
+ }
+ if (locals == NULL) {
return NULL;
+ }
names = PyMapping_Keys(locals);
Py_DECREF(locals);
PyObject *locals)
/*[clinic end generated code: output=0a0824aa70093116 input=7c7bce5299a89062]*/
{
+ PyThreadState *tstate = _PyThreadState_GET();
PyObject *result = NULL, *source_copy;
const char *str;
: "globals must be a dict");
return NULL;
}
- 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;
- }
- else {
- Py_INCREF(locals);
- }
+ assert(globals != NULL);
+ Py_INCREF(globals);
}
- else if (locals == Py_None)
- locals = Py_NewRef(globals);
else {
- Py_INCREF(locals);
+ 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 (globals == NULL || locals == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "eval must be given globals and locals "
- "when called without a frame");
- goto error;
+ if (locals != Py_None) {
+ Py_INCREF(locals);
}
-
- int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
- if (r == 0) {
- r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
+ else if (fromframe) {
+ locals = _PyEval_GetFrameLocals();
+ if (locals == NULL) {
+ assert(PyErr_Occurred());
+ Py_DECREF(globals);
+ return NULL;
+ }
+ }
+ else {
+ locals = Py_NewRef(globals);
}
- if (r < 0) {
+
+ if (_PyEval_EnsureBuiltins(tstate, globals, NULL) < 0) {
goto error;
}
}
error:
+ Py_XDECREF(globals);
Py_XDECREF(locals);
return result;
}
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;
- }
- else {
- Py_INCREF(locals);
+ 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;
}
- 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)) {
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;
}
}
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;
}
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);
}
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);
}
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) {
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;
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)
{
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();
+ PyThreadState *tstate = _PyThreadState_GET();
+ PyObject *globals = _PyEval_GetGlobalsFromRunningMain(tstate); // borrowed
if (globals == NULL) {
- Py_DECREF(code);
- return NULL;
+ if (_PyErr_Occurred(tstate)) {
+ Py_DECREF(code);
+ return NULL;
+ }
+ 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)
- {
+ else {
+ Py_INCREF(globals);
+ }
+ if (_PyEval_EnsureBuiltins(tstate, globals, NULL) < 0) {
Py_DECREF(code);
Py_DECREF(globals);
return NULL;
}
/* 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 */