gh-132775: Add _PyCode_VerifyStateless() (gh-133221)

"Stateless" code is a function or code object which does not rely on external state or internal state.
It may rely on arguments and builtins, but not globals or a closure. I've left a comment in
pycore_code.h that provides more detail.

We also add _PyFunction_VerifyStateless(). The new functions will be used in several later changes
that facilitate "sharing" functions and code objects between interpreters.
This commit is contained in:
Eric Snow 2025-05-05 15:48:58 -06:00 committed by GitHub
parent f610bbdf74
commit d270bb5792
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 442 additions and 38 deletions

View file

@ -1,12 +1,14 @@
/* Function object implementation */
#include "Python.h"
#include "pycore_code.h" // _PyCode_VerifyStateless()
#include "pycore_dict.h" // _Py_INCREF_DICT()
#include "pycore_function.h" // _PyFunction_Vectorcall
#include "pycore_long.h" // _PyLong_GetOne()
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_stats.h"
@ -1240,6 +1242,58 @@ PyTypeObject PyFunction_Type = {
};
int
_PyFunction_VerifyStateless(PyThreadState *tstate, PyObject *func)
{
assert(!PyErr_Occurred());
assert(PyFunction_Check(func));
// Check the globals.
PyObject *globalsns = PyFunction_GET_GLOBALS(func);
if (globalsns != NULL && !PyDict_Check(globalsns)) {
_PyErr_Format(tstate, PyExc_TypeError,
"unsupported globals %R", globalsns);
return -1;
}
// Check the builtins.
PyObject *builtinsns = PyFunction_GET_BUILTINS(func);
if (builtinsns != NULL && !PyDict_Check(builtinsns)) {
_PyErr_Format(tstate, PyExc_TypeError,
"unsupported builtins %R", builtinsns);
return -1;
}
// Disallow __defaults__.
PyObject *defaults = PyFunction_GET_DEFAULTS(func);
if (defaults != NULL && defaults != Py_None && PyDict_Size(defaults) > 0)
{
_PyErr_SetString(tstate, PyExc_ValueError, "defaults not supported");
return -1;
}
// Disallow __kwdefaults__.
PyObject *kwdefaults = PyFunction_GET_KW_DEFAULTS(func);
if (kwdefaults != NULL && kwdefaults != Py_None
&& PyDict_Size(kwdefaults) > 0)
{
_PyErr_SetString(tstate, PyExc_ValueError,
"keyword defaults not supported");
return -1;
}
// Disallow __closure__.
PyObject *closure = PyFunction_GET_CLOSURE(func);
if (closure != NULL && closure != Py_None && PyTuple_GET_SIZE(closure) > 0)
{
_PyErr_SetString(tstate, PyExc_ValueError, "closures not supported");
return -1;
}
// Check the code.
PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
if (_PyCode_VerifyStateless(tstate, co, NULL, globalsns, builtinsns) < 0) {
return -1;
}
return 0;
}
static int
functools_copy_attr(PyObject *wrapper, PyObject *wrapped, PyObject *name)
{