8000 [3.12] gh-117694: Improve tests for PyEval_EvalCodeEx() (GH-117695) (… · python/cpython@50b94b1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 50b94b1

Browse files
[3.12] gh-117694: Improve tests for PyEval_EvalCodeEx() (GH-117695) (GH-117884)
(cherry picked from commit 57bdb75) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent a00dd31 
10000
commit 50b94b1

File tree

2 files changed

+114
-91
lines changed

2 files changed

+114
-91
lines changed

Lib/test/test_capi/test_eval_code_ex.py

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,125 @@
11
import unittest
2+
import builtins
3+
from collections import UserDict
24

35
from test.support import import_helper
6+
from test.support import swap_attr
47

58

69
# Skip this test if the _testcapi module isn't available.
710
_testcapi = import_helper.import_module('_testcapi')
811

12+
NULL = None
13+
914

1015
class PyEval_EvalCodeExTests(unittest.TestCase):
1116

1217
def test_simple(self):
1318
def f():
1419
return a
1520

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
21+
eval_code_ex = _testcapi.eval_code_ex
22+
code = f.__code__
23+
self.assertEqual(eval_code_ex(code, dict(a=1)), 1)
24+
25+
self.assertRaises(NameError, eval_code_ex, code, {})
26+
self.assertRaises(SystemError, eval_code_ex, code, UserDict(a=1))
27+
self.assertRaises(SystemError, eval_code_ex, code, [])
28+
self.assertRaises(SystemError, eval_code_ex, code, 1)
29+
# CRASHES eval_code_ex(code, NULL)
30+
# CRASHES eval_code_ex(1, {})
31+
# CRASHES eval_code_ex(NULL, {})
32+
33+
def test_custom_locals(self):
34+
# Monkey-patch __build_class__ to get a class code object.
35+
code = None
36+
def build_class(func, name, /, *bases, **kwds):
37+
nonlocal code
38+
code = func.__code__
39+
40+
with swap_attr(builtins, '__build_class__', build_class):
41+
class A:
42+
# Uses LOAD_NAME for a
43+
r[:] = [a]
44+
45+
eval_code_ex = _testcapi.eval_code_ex
46+
results = []
47+
g = dict(a=1, r=results)
48+
self.assertIsNone(eval_code_ex(code, g))
49+
self.assertEqual(results, [1])
50+
self.assertIsNone(eval_code_ex(code, g, dict(a=2)))
51+
self.assertEqual(results, [2])
52+
self.assertIsNone(eval_code_ex(code, g, UserDict(a=3)))
53+
self.assertEqual(results, [3])
54+
self.assertIsNone(eval_code_ex(code, g, {}))
55+
self.assertEqual(results, [1])
56+
self.assertIsNone(eval_code_ex(code, g, NULL))
57+
self.assertEqual(results, [1])
58+
59+
self.assertRaises(TypeError, eval_code_ex, code, g, [])
60+
self.assertRaises(TypeError, eval_code_ex, code, g, 1)
61+
self.assertRaises(NameError, eval_code_ex, code, dict(r=results), {})
62+
self.assertRaises(NameError, eval_code_ex, code, dict(r=results), NULL)
63+
self.assertRaises(TypeError, eval_code_ex, code, dict(r=results), [])
64+
self.assertRaises(TypeError, eval_code_ex, code, dict(r=results), 1)
2265

2366
def test_with_args(self):
2467
def f(a, b, c):
2568
return a
2669

27-
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (1, 2, 3)), 1)
70+
eval_code_ex = _testcapi.eval_code_ex
71+
code = f.__code__
72+
self.assertEqual(eval_code_ex(code, {}, {}, (1, 2, 3)), 1)
73+
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (1, 2))
74+
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (1, 2, 3, 4))
2875

2976
def test_with_kwargs(self):
3077
def f(a, b, c):
3178
return a
3279

33-
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), dict(a=1, b=2, c=3)), 1)
80+
eval_code_ex = _testcapi.eval_code_ex
81+
code = f.__code__
82+
self.assertEqual(eval_code_ex(code, {}, {}, (), dict(a=1, b=2, c=3)), 1)
83+
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), dict(a=1, b=2))
84+
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), dict(a=1, b=2))
85+
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), dict(a=1, b=2, c=3, d=4))
3486

3587
def test_with_default(self):
3688
def f(a):
3789
return a
3890

39-
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (1,)), 1)
91+
eval_code_ex = _testcapi.eval_code_ex
92+
code = f.__code__
93+
self.assertEqual(eval_code_ex(code, {}, {}, (), {}, (1,)), 1)
94+
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), {}, ())
4095

4196
def test_with_kwarg_default(self):
4297
def f(*, a):
4398
return a
4499

45-
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), dict(a=1)), 1)
100+
eval_code_ex = _testcapi.eval_code_ex
101+
code = f.__code__
102+
self.assertEqual(eval_code_ex(code, {}, {}, (), {}, (), dict(a=1)), 1)
103+
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), {}, (), {})
104+
self.assertRaises(TypeError, eval_code_ex, code, {}, {}, (), {}, (), NULL)
105+
self.assertRaises(SystemError, eval_code_ex, code, {}, {}, (), {}, (), UserDict(a=1))
106+
self.assertRaises(SystemError, eval_code_ex, code, {}, {}, (), {}, (), [])
107+
self.assertRaises(SystemError, eval_code_ex, code, {}, {}, (), {}, (), 1)
46108

47109
def test_with_closure(self):
48110
a = 1
111+
b = 2
49112
def f():
113+
b
50114
return a
51115

52-
self.assertEqual(_testcapi.eval_code_ex(f.__code__, {}, {}, (), {}, (), {}, f.__closure__), 1)
116+
eval_code_ex = _testcapi.eval_code_ex
117+
code = f.__code__
118+
self.assertEqual(eval_code_ex(code, {}, {}, (), {}, (), {}, f.__closure__), 1)
119+
self.assertEqual(eval_code_ex(code, {}, {}, (), {}, (), {}, f.__closure__[::-1]), 2)
120+
121+
# CRASHES eval_code_ex(code, {}, {}, (), {}, (), {}, ()), 1)
122+
# CRASHES eval_code_ex(code, {}, {}, (), {}, (), {}, NULL), 1)
53123

54124

55125
if __name__ == "__main__":

Modules/_testcapimodule.c

Lines changed: 33 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2826,107 +2826,60 @@ eval_eval_code_ex(PyObject *mod, PyObject *pos_args)
28262826

28272827
PyObject **c_kwargs = NULL;
28282828

2829-
if (!PyArg_UnpackTuple(pos_args,
2830-
"eval_code_ex",
2831-
2,
2832-
8,
2833-
&code,
2834-
&globals,
2835-
&locals,
2836-
&args,
2837-
&kwargs,
2838-
&defaults,
2839-
&kw_defaults,
2840-
&closure))
2829+
if (!PyArg_ParseTuple(pos_args,
2830+
"OO|OO!O!O!OO:eval_code_ex",
2831+
&code,
2832+
&globals,
2833+
&locals,
2834+
&PyTuple_Type, &args,
2835+
&PyDict_Type, &kwargs,
2836+
&PyTuple_Type, &defaults,
2837+
&kw_defaults,
2838+
&closure))
28412839
{
28422840
goto exit;
28432841
}
28442842

2845-
if (!PyCode_Check(code)) {
2846-
PyErr_SetString(PyExc_TypeError,
2847-
"code must be a Python code object");
2848-
goto exit;
2849-
}
2850-
2851-
if (!PyDict_Check(globals)) {
2852-
PyErr_SetString(PyExc_TypeError, "globals must be a dict");
2853-
goto exit;
2854-
}
2855-
2856-
if (locals && !PyMapping_Check(locals)) {
2857-
PyErr_SetString(PyExc_TypeError, "locals must be a mapping");
2858-
goto exit;
2859-
}
2860-
if (locals == Py_None) {
2861-
locals = NULL;
2862-
}
2843+
NULLABLE(code);
2844+
NULLABLE(globals);
2845+
NULLABLE(locals);
2846+
NULLABLE(kw_defaults);
2847+
NULLABLE(closure);
28632848

28642849
PyObject **c_args = NULL;
28652850
Py_ssize_t c_args_len = 0;
2866-
2867-
if (args)
2868-
{
2869-
if (!PyTuple_Check(args)) {
2870-
PyErr_SetString(PyExc_TypeError, "args must be a tuple");
2871-
goto exit;
2872-
} else {
2873-
c_args = &PyTuple_GET_ITEM(args, 0);
2874-
c_args_len = PyTuple_Size(args);
2875-
}
2851+
if (args) {
2852+
c_args = &PyTuple_GET_ITEM(args, 0);
2853+
c_args_len = PyTuple_Size(args);
28762854
}
28772855

28782856
Py_ssize_t c_kwargs_len = 0;
2857+
if (kwargs) {
2858+
c_kwargs_len = PyDict_Size(kwargs);
2859+
if (c_kwargs_len > 0) {
2860+
c_kwargs = PyMem_NEW(PyObject*, 2 * c_kwargs_len);
2861+
if (!c_kwargs) {
2862+
PyErr_NoMemory();
2863+
goto exit;
2864+
}
28792865

2880-
if (kwargs)
2881-
{
2882-
if (!PyDict_Check(kwargs)) {
2883-
PyErr_SetString(PyExc_TypeError, "keywords must be a dict");
2884-
goto exit;
2885-
} else {
2886-
c_kwargs_len = PyDict_Size(kwargs);
2887-
if (c_kwargs_len > 0) {
2888-
c_kwargs = PyMem_NEW(PyObject*, 2 * c_kwargs_len);
2889-
if (!c_kwargs) {
2890-
PyErr_NoMemory();
2891-
goto exit;
2892-
}
2893-
2894-
Py_ssize_t i = 0;
2895-
Py_ssize_t pos = 0;
2896-
2897-
while (PyDict_Next(kwargs,
2898-
&pos,
2899-
&c_kwargs[i],
2900-
&c_kwargs[i + 1]))
2901-
{
2902-
i += 2;
2903-
}
2904-
c_kwargs_len = i / 2;
2905-
/* XXX This is broken if the caller deletes dict items! */
2866+
Py_ssize_t i = 0;
2867+
Py_ssize_t pos = 0;
2868+
while (PyDict_Next(kwargs, &pos, &c_kwargs[i], &c_kwargs[i + 1])) {
2869+
i += 2;
29062870
}
2871+
c_kwargs_len = i / 2;
2872+
/* XXX This is broken if the caller deletes dict items! */
29072873
}
29082874
}
29092875

2910-
29112876
PyObject **c_defaults = NULL;
29122877
Py_ssize_t c_defaults_len = 0;
2913-
2914-
if (defaults && PyTuple_Check(defaults)) {
2878+
if (defaults) {
29152879
c_defaults = &PyTuple_GET_ITEM(defaults, 0);
29162880
c_defaults_len = PyTuple_Size(defaults);
29172881
}
29182882

2919-
if (kw_defaults && !PyDict_Check(kw_defaults)) {
2920-
PyErr_SetString(PyExc_TypeError, "kw_defaults must be a dict");
2921-
goto exit;
2922-
}
2923-
2924-
if (closure && !PyTuple_Check(closure)) {
2925-
PyErr_SetString(PyExc_TypeError, "closure must be a tuple of cells");
2926-
goto exit;
2927-
}
2928-
2929-
29302883
result = PyEval_EvalCodeEx(
29312884
code,
29322885
globals,

0 commit comments

Comments
 (0)
0