mirror of
https://github.com/python/cpython.git
synced 2025-08-31 05:58:33 +00:00
gh-92203: Add closure support to exec(). (#92204)
Add a closure keyword-only parameter to exec(). It can only be specified when exec-ing a code object that uses free variables. When specified, it must be a tuple, with exactly the number of cell variables referenced by the code object. closure has a default value of None, and it must be None if the code object doesn't refer to any free variables.
This commit is contained in:
parent
973a5203c1
commit
5021064390
5 changed files with 171 additions and 21 deletions
|
@ -977,6 +977,8 @@ exec as builtin_exec
|
|||
globals: object = None
|
||||
locals: object = None
|
||||
/
|
||||
*
|
||||
closure: object(c_default="NULL") = None
|
||||
|
||||
Execute the given source in the context of globals and locals.
|
||||
|
||||
|
@ -985,12 +987,14 @@ or a code object as returned by compile().
|
|||
The globals must be a dictionary and locals can be any mapping,
|
||||
defaulting to the current globals and locals.
|
||||
If only globals is given, locals defaults to it.
|
||||
The closure must be a tuple of cellvars, and can only be used
|
||||
when source is a code object requiring exactly that many cellvars.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||
PyObject *locals)
|
||||
/*[clinic end generated code: output=3c90efc6ab68ef5d input=01ca3e1c01692829]*/
|
||||
PyObject *locals, PyObject *closure)
|
||||
/*[clinic end generated code: output=7579eb4e7646743d input=f13a7e2b503d1d9a]*/
|
||||
{
|
||||
PyObject *v;
|
||||
|
||||
|
@ -1029,20 +1033,60 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (closure == Py_None) {
|
||||
closure = NULL;
|
||||
}
|
||||
|
||||
if (PyCode_Check(source)) {
|
||||
Py_ssize_t num_free = PyCode_GetNumFree((PyCodeObject *)source);
|
||||
if (num_free == 0) {
|
||||
if (closure) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"cannot use a closure with this code object");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
int closure_is_ok =
|
||||
closure
|
||||
&& PyTuple_CheckExact(closure)
|
||||
&& (PyTuple_GET_SIZE(closure) == num_free);
|
||||
if (closure_is_ok) {
|
||||
for (Py_ssize_t i = 0; i < num_free; i++) {
|
||||
PyObject *cell = PyTuple_GET_ITEM(closure, i);
|
||||
if (!PyCell_Check(cell)) {
|
||||
closure_is_ok = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!closure_is_ok) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"code object requires a closure of exactly length %zd",
|
||||
num_free);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (PySys_Audit("exec", "O", source) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (PyCode_GetNumFree((PyCodeObject *)source) > 0) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"code object passed to exec() may not "
|
||||
"contain free variables");
|
||||
return NULL;
|
||||
if (!closure) {
|
||||
v = PyEval_EvalCode(source, globals, locals);
|
||||
} else {
|
||||
v = PyEval_EvalCodeEx(source, globals, locals,
|
||||
NULL, 0,
|
||||
NULL, 0,
|
||||
NULL, 0,
|
||||
NULL,
|
||||
closure);
|
||||
}
|
||||
v = PyEval_EvalCode(source, globals, locals);
|
||||
}
|
||||
else {
|
||||
if (closure != NULL) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"closure can only be used when source is a code object");
|
||||
}
|
||||
PyObject *source_copy;
|
||||
const char *str;
|
||||
PyCompilerFlags cf = _PyCompilerFlags_INIT;
|
||||
|
|
37
Python/clinic/bltinmodule.c.h
generated
37
Python/clinic/bltinmodule.c.h
generated
|
@ -408,7 +408,7 @@ exit:
|
|||
}
|
||||
|
||||
PyDoc_STRVAR(builtin_exec__doc__,
|
||||
"exec($module, source, globals=None, locals=None, /)\n"
|
||||
"exec($module, source, globals=None, locals=None, /, *, closure=None)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Execute the given source in the context of globals and locals.\n"
|
||||
|
@ -417,37 +417,52 @@ PyDoc_STRVAR(builtin_exec__doc__,
|
|||
"or a code object as returned by compile().\n"
|
||||
"The globals must be a dictionary and locals can be any mapping,\n"
|
||||
"defaulting to the current globals and locals.\n"
|
||||
"If only globals is given, locals defaults to it.");
|
||||
"If only globals is given, locals defaults to it.\n"
|
||||
"The closure must be a tuple of cellvars, and can only be used\n"
|
||||
"when source is a code object requiring exactly that many cellvars.");
|
||||
|
||||
#define BUILTIN_EXEC_METHODDEF \
|
||||
{"exec", _PyCFunction_CAST(builtin_exec), METH_FASTCALL, builtin_exec__doc__},
|
||||
{"exec", _PyCFunction_CAST(builtin_exec), METH_FASTCALL|METH_KEYWORDS, builtin_exec__doc__},
|
||||
|
||||
static PyObject *
|
||||
builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||
PyObject *locals);
|
||||
PyObject *locals, PyObject *closure);
|
||||
|
||||
static PyObject *
|
||||
builtin_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
builtin_exec(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
static const char * const _keywords[] = {"", "", "", "closure", NULL};
|
||||
static _PyArg_Parser _parser = {NULL, _keywords, "exec", 0};
|
||||
PyObject *argsbuf[4];
|
||||
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
|
||||
PyObject *source;
|
||||
PyObject *globals = Py_None;
|
||||
PyObject *locals = Py_None;
|
||||
PyObject *closure = NULL;
|
||||
|
||||
if (!_PyArg_CheckPositional("exec", nargs, 1, 3)) {
|
||||
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf);
|
||||
if (!args) {
|
||||
goto exit;
|
||||
}
|
||||
source = args[0];
|
||||
if (nargs < 2) {
|
||||
goto skip_optional;
|
||||
goto skip_optional_posonly;
|
||||
}
|
||||
noptargs--;
|
||||
globals = args[1];
|
||||
if (nargs < 3) {
|
||||
goto skip_optional;
|
||||
goto skip_optional_posonly;
|
||||
}
|
||||
noptargs--;
|
||||
locals = args[2];
|
||||
skip_optional:
|
||||
return_value = builtin_exec_impl(module, source, globals, locals);
|
||||
skip_optional_posonly:
|
||||
if (!noptargs) {
|
||||
goto skip_optional_kwonly;
|
||||
}
|
||||
closure = args[3];
|
||||
skip_optional_kwonly:
|
||||
return_value = builtin_exec_impl(module, source, globals, locals, closure);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
@ -1030,4 +1045,4 @@ builtin_issubclass(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=6a2b78ef82bc5155 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=a2c5c53e8aead7c3 input=a9049054013a1b77]*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue