mirror of
https://github.com/python/cpython.git
synced 2025-08-30 13:38:43 +00:00
gh-101072: support default and kw default in PyEval_EvalCodeEx for 3.11+ (#101127)
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
parent
c4de6b1d52
commit
ae62bddaf8
5 changed files with 206 additions and 6 deletions
56
Lib/test/test_capi/test_eval_code_ex.py
Normal file
56
Lib/test/test_capi/test_eval_code_ex.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from test.support import import_helper
|
||||||
|
|
||||||
|
|
||||||
|
# Skip this test if the _testcapi module isn't available.
|
||||||
|
_testcapi = import_helper.import_module('_testcapi')
|
||||||
|
|
||||||
|
|
||||||
|
class PyEval_EvalCodeExTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_simple(self):
|
||||||
|
def f():
|
||||||
|
return a
|
||||||
|
|
||||||
|
self.assertEqual(_testcapi.eval_code_ex(f.__code__, dict(a=1)), 1)
|
||||||
|
|
||||||
|
# Need to force the compiler to use LOAD_NAME
|
||||||
|
# def test_custom_locals(self):
|
||||||
|
# def f():
|
||||||
|
# return
|
||||||
|
|
||||||
|
def test_with_args(self):
|
||||||
|
def f(a, b, c):
|
||||||
|
return a
|
||||||
|
|
||||||
|
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (1, 2, 3)), 1)
|
||||||
|
|
||||||
|
def test_with_kwargs(self):
|
||||||
|
def f(a, b, c):
|
||||||
|
return a
|
||||||
|
|
||||||
|
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), dict(a=1, b=2, c=3)), 1)
|
||||||
|
|
||||||
|
def test_with_default(self):
|
||||||
|
def f(a):
|
||||||
|
return a
|
||||||
|
|
||||||
|
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (1,)), 1)
|
||||||
|
|
||||||
|
def test_with_kwarg_default(self):
|
||||||
|
def f(*, a):
|
||||||
|
return a
|
||||||
|
|
||||||
|
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), dict(a=1)), 1)
|
||||||
|
|
||||||
|
def test_with_closure(self):
|
||||||
|
a = 1
|
||||||
|
def f():
|
||||||
|
return a
|
||||||
|
|
||||||
|
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), {}, f.__closure__), 1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
|
@ -0,0 +1,8 @@
|
||||||
|
macOS #.. section: IDLE #.. section: Tools/Demos #.. section: C API
|
||||||
|
|
||||||
|
# Write your Misc/NEWS entry below. It should be a simple ReST paragraph. #
|
||||||
|
Don't start with "- Issue #<n>: " or "- gh-issue-<n>: " or that sort of
|
||||||
|
stuff.
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
Fix the ``defs`` and ``kwdefs`` arguments to :c:func:`PyEval_EvalCodeEx`.
|
|
@ -3064,6 +3064,144 @@ eval_get_func_desc(PyObject *self, PyObject *func)
|
||||||
return PyUnicode_FromString(PyEval_GetFuncDesc(func));
|
return PyUnicode_FromString(PyEval_GetFuncDesc(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
eval_eval_code_ex(PyObject *mod, PyObject *pos_args)
|
||||||
|
{
|
||||||
|
PyObject *result = NULL;
|
||||||
|
PyObject *code;
|
||||||
|
PyObject *globals;
|
||||||
|
PyObject *locals = NULL;
|
||||||
|
PyObject *args = NULL;
|
||||||
|
PyObject *kwargs = NULL;
|
||||||
|
PyObject *defaults = NULL;
|
||||||
|
PyObject *kw_defaults = NULL;
|
||||||
|
PyObject *closure = NULL;
|
||||||
|
|
||||||
|
PyObject **c_kwargs = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_UnpackTuple(pos_args,
|
||||||
|
"eval_code_ex",
|
||||||
|
2,
|
||||||
|
8,
|
||||||
|
&code,
|
||||||
|
&globals,
|
||||||
|
&locals,
|
||||||
|
&args,
|
||||||
|
&kwargs,
|
||||||
|
&defaults,
|
||||||
|
&kw_defaults,
|
||||||
|
&closure))
|
||||||
|
{
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyCode_Check(code)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"code must be a Python code object");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PyDict_Check(globals)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "globals must be a dict");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locals && !PyMapping_Check(locals)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "locals must be a mapping");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (locals == Py_None) {
|
||||||
|
locals = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject **c_args = NULL;
|
||||||
|
Py_ssize_t c_args_len = 0;
|
||||||
|
|
||||||
|
if (args)
|
||||||
|
{
|
||||||
|
if (!PyTuple_Check(args)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "args must be a tuple");
|
||||||
|
goto exit;
|
||||||
|
} else {
|
||||||
|
c_args = &PyTuple_GET_ITEM(args, 0);
|
||||||
|
c_args_len = PyTuple_Size(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t c_kwargs_len = 0;
|
||||||
|
|
||||||
|
if (kwargs)
|
||||||
|
{
|
||||||
|
if (!PyDict_Check(kwargs)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "keywords must be a dict");
|
||||||
|
goto exit;
|
||||||
|
} else {
|
||||||
|
c_kwargs_len = PyDict_Size(kwargs);
|
||||||
|
if (c_kwargs_len > 0) {
|
||||||
|
c_kwargs = PyMem_NEW(PyObject*, 2 * c_kwargs_len);
|
||||||
|
if (!c_kwargs) {
|
||||||
|
PyErr_NoMemory();
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t i = 0;
|
||||||
|
Py_ssize_t pos = 0;
|
||||||
|
|
||||||
|
while (PyDict_Next(kwargs,
|
||||||
|
&pos,
|
||||||
|
&c_kwargs[i],
|
||||||
|
&c_kwargs[i + 1]))
|
||||||
|
{
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
c_kwargs_len = i / 2;
|
||||||
|
/* XXX This is broken if the caller deletes dict items! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject **c_defaults = NULL;
|
||||||
|
Py_ssize_t c_defaults_len = 0;
|
||||||
|
|
||||||
|
if (defaults && PyTuple_Check(defaults)) {
|
||||||
|
c_defaults = &PyTuple_GET_ITEM(defaults, 0);
|
||||||
|
c_defaults_len = PyTuple_Size(defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kw_defaults && !PyDict_Check(kw_defaults)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "kw_defaults must be a dict");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closure && !PyTuple_Check(closure)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "closure must be a tuple of cells");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result = PyEval_EvalCodeEx(
|
||||||
|
code,
|
||||||
|
globals,
|
||||||
|
locals,
|
||||||
|
c_args,
|
||||||
|
c_args_len,
|
||||||
|
c_kwargs,
|
||||||
|
c_kwargs_len,
|
||||||
|
c_defaults,
|
||||||
|
c_defaults_len,
|
||||||
|
kw_defaults,
|
||||||
|
closure
|
||||||
|
);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (c_kwargs) {
|
||||||
|
PyMem_DEL(c_kwargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args))
|
get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args))
|
||||||
{
|
{
|
||||||
|
@ -3385,6 +3523,7 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"set_exc_info", test_set_exc_info, METH_VARARGS},
|
{"set_exc_info", test_set_exc_info, METH_VARARGS},
|
||||||
{"argparsing", argparsing, METH_VARARGS},
|
{"argparsing", argparsing, METH_VARARGS},
|
||||||
{"code_newempty", code_newempty, METH_VARARGS},
|
{"code_newempty", code_newempty, METH_VARARGS},
|
||||||
|
{"eval_code_ex", eval_eval_code_ex, METH_VARARGS},
|
||||||
{"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc),
|
{"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc),
|
||||||
METH_VARARGS | METH_KEYWORDS},
|
METH_VARARGS | METH_KEYWORDS},
|
||||||
{"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer,
|
{"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer,
|
||||||
|
|
|
@ -87,8 +87,8 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr)
|
||||||
op->func_name = Py_NewRef(constr->fc_name);
|
op->func_name = Py_NewRef(constr->fc_name);
|
||||||
op->func_qualname = Py_NewRef(constr->fc_qualname);
|
op->func_qualname = Py_NewRef(constr->fc_qualname);
|
||||||
op->func_code = Py_NewRef(constr->fc_code);
|
op->func_code = Py_NewRef(constr->fc_code);
|
||||||
op->func_defaults = NULL;
|
op->func_defaults = Py_XNewRef(constr->fc_defaults);
|
||||||
op->func_kwdefaults = NULL;
|
op->func_kwdefaults = Py_XNewRef(constr->fc_kwdefaults);
|
||||||
op->func_closure = Py_XNewRef(constr->fc_closure);
|
op->func_closure = Py_XNewRef(constr->fc_closure);
|
||||||
op->func_doc = Py_NewRef(Py_None);
|
op->func_doc = Py_NewRef(Py_None);
|
||||||
op->func_dict = NULL;
|
op->func_dict = NULL;
|
||||||
|
|
|
@ -1761,9 +1761,6 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
|
||||||
}
|
}
|
||||||
allargs = newargs;
|
allargs = newargs;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < kwcount; i++) {
|
|
||||||
PyTuple_SET_ITEM(kwnames, i, Py_NewRef(kws[2*i]));
|
|
||||||
}
|
|
||||||
PyFrameConstructor constr = {
|
PyFrameConstructor constr = {
|
||||||
.fc_globals = globals,
|
.fc_globals = globals,
|
||||||
.fc_builtins = builtins,
|
.fc_builtins = builtins,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue