Skip to content

Commit 0bb1c96

Browse files
test: add a test for PyEval_EvalCodeEx
1 parent f95777b commit 0bb1c96

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import unittest
2+
3+
from test.support import import_helper
4+
5+
6+
# Skip this test if the _testcapi module isn't available.
7+
_testcapi = import_helper.import_module('_testcapi')
8+
9+
10+
class PyEval_EvalCodeExTests(unittest.TestCase):
11+
12+
def test_simple(self):
13+
def f():
14+
return a
15+
16+
self.assertEqual(_testcapi.eval_code_ex(f.__code__, dict(a=1)), 1)
17+
18+
# Need to force the compiler to use LOAD_NAME
19+
# def test_custom_locals(self):
20+
# def f():
21+
# return
22+
23+
def test_with_args(self):
24+
def f(a):
25+
return a
26+
27+
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (1,)), 1)
28+
29+
def test_with_kwargs(self):
30+
def f(a):
31+
return a
32+
33+
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), dict(a=1)), 1)
34+
35+
def test_with_default(self):
36+
def f(a):
37+
return a
38+
39+
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (1,)), 1)
40+
41+
def test_with_kwarg_default(self):
42+
def f(*, a):
43+
return a
44+
45+
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), dict(a=1)), 1)
46+
47+
def test_with_closure(self):
48+
a = 1
49+
def f():
50+
return a
51+
52+
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), {}, f.__closure__), 1)
53+
54+
55+
if __name__ == "__main__":
56+
unittest.main()

Modules/_testcapimodule.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2973,6 +2973,130 @@ eval_get_func_desc(PyObject *self, PyObject *func)
29732973
return PyUnicode_FromString(PyEval_GetFuncDesc(func));
29742974
}
29752975

2976+
static PyObject *
2977+
eval_eval_code_ex(PyObject *mod, PyObject *pos_args)
2978+
{
2979+
PyObject* result = NULL;
2980+
PyObject* code;
2981+
PyObject* globals;
2982+
PyObject* locals = NULL;
2983+
PyObject* args = NULL;
2984+
PyObject* kwargs = NULL;
2985+
PyObject* defaults = NULL;
2986+
PyObject* kw_defaults = NULL;
2987+
PyObject* closure = NULL;
2988+
2989+
PyObject** c_kwargs = NULL;
2990+
2991+
if( !PyArg_UnpackTuple( pos_args, "eval_code_ex", 2, 8, &code, &globals, &locals, &args, &kwargs, &defaults, &kw_defaults, &closure) )
2992+
{
2993+
goto exit;
2994+
}
2995+
2996+
if( !PyCode_Check( code ) )
2997+
{
2998+
PyErr_SetString( PyExc_TypeError, "code must be a Python code object" );
2999+
goto exit;
3000+
}
3001+
3002+
if( !PyDict_Check( globals ) )
3003+
{
3004+
PyErr_SetString( PyExc_TypeError, "globals must be a dict" );
3005+
goto exit;
3006+
}
3007+
3008+
if( locals && !PyMapping_Check( locals ) )
3009+
{
3010+
PyErr_SetString( PyExc_TypeError, "locals must be a mapping" );
3011+
goto exit;
3012+
}
3013+
if( locals == Py_None )
3014+
locals = NULL;
3015+
3016+
PyObject** c_args = NULL;
3017+
Py_ssize_t c_args_len = 0;
3018+
if( args )
3019+
{
3020+
if ( !PyTuple_Check( args ) )
3021+
{
3022+
PyErr_SetString( PyExc_TypeError, "args must be a tuple" );
3023+
goto exit;
3024+
} else {
3025+
c_args = &PyTuple_GET_ITEM( args, 0 );
3026+
c_args_len = PyTuple_Size( args );
3027+
}
3028+
}
3029+
3030+
Py_ssize_t c_kwargs_len = 0;
3031+
if( kwargs )
3032+
{
3033+
if( !PyDict_Check( kwargs ) )
3034+
{
3035+
PyErr_SetString( PyExc_TypeError, "keywords must be a dict" );
3036+
goto exit;
3037+
} else {
3038+
c_kwargs_len = PyDict_Size( kwargs );
3039+
if( c_kwargs_len > 0 )
3040+
{
3041+
c_kwargs = PyMem_NEW( PyObject*, 2 * c_kwargs_len );
3042+
if( !c_kwargs )
3043+
{
3044+
PyErr_NoMemory();
3045+
goto exit;
3046+
}
3047+
Py_ssize_t i = 0;
3048+
Py_ssize_t pos = 0;
3049+
while( PyDict_Next( kwargs, &pos, &c_kwargs[ i ], &c_kwargs[ i + 1 ] ) )
3050+
i += 2;
3051+
c_kwargs_len = i / 2;
3052+
/* XXX This is broken if the caller deletes dict items! */
3053+
}
3054+
}
3055+
}
3056+
3057+
3058+
PyObject** c_defaults = NULL;
3059+
Py_ssize_t c_defaults_len = 0;
3060+
if( ( defaults ) && PyTuple_Check( defaults ) )
3061+
{
3062+
c_defaults = &PyTuple_GET_ITEM( defaults, 0 );
3063+
c_defaults_len = PyTuple_Size( defaults );
3064+
}
3065+
3066+
if( ( kw_defaults ) && !PyDict_Check( kw_defaults ) )
3067+
{
3068+
PyErr_SetString( PyExc_TypeError, "kw_defaults must be a dict" );
3069+
goto exit;
3070+
}
3071+
3072+
if( ( closure ) && !PyTuple_Check( closure ) )
3073+
{
3074+
PyErr_SetString( PyExc_TypeError, "closure must be a tuple of cells" );
3075+
goto exit;
3076+
}
3077+
3078+
3079+
result = PyEval_EvalCodeEx(
3080+
code,
3081+
globals,
3082+
locals,
3083+
c_args,
3084+
c_args_len,
3085+
c_kwargs,
3086+
c_kwargs_len,
3087+
c_defaults,
3088+
c_defaults_len,
3089+
kw_defaults,
3090+
closure
3091+
);
3092+
3093+
exit:
3094+
if( c_kwargs )
3095+
PyMem_DEL( c_kwargs );
3096+
3097+
return result;
3098+
}
3099+
29763100
static PyObject *
29773101
get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args))
29783102
{
@@ -3294,6 +3418,7 @@ static PyMethodDef TestMethods[] = {
32943418
{"set_exc_info", test_set_exc_info, METH_VARARGS},
32953419
{"argparsing", argparsing, METH_VARARGS},
32963420
{"code_newempty", code_newempty, METH_VARARGS},
3421+
{"eval_code_ex", eval_eval_code_ex, METH_VARARGS},
32973422
{"make_exception_with_doc", _PyCFunction_CAST(make_exception_with_doc),
32983423
METH_VARARGS | METH_KEYWORDS},
32993424
{"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer,

0 commit comments

Comments
 (0)