Skip to content

Commit 15a6d3a

Browse files
vstinnersrinivasreddy
authored andcommitted
pythongh-93649: Move PyFrame C API tests to test_capi (python#129512)
* Add Lib/test/test_capi/test_frame.py file. * Move C API tests from test_frame to test_capi.test_frame. * Add Modules/_testcapi/frame.c file. * Move C API tests from _testcapimodule.c to frame.c
1 parent 5d81ab6 commit 15a6d3a

File tree

8 files changed

+199
-157
lines changed

8 files changed

+199
-157
lines changed

Lib/test/test_capi/test_frame.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import sys
2+
import unittest
3+
from test.support import import_helper
4+
5+
6+
_testcapi = import_helper.import_module('_testcapi')
7+
8+
9+
class FrameTest(unittest.TestCase):
10+
def getframe(self):
11+
return sys._getframe()
12+
13+
def test_frame_getters(self):
14+
frame = self.getframe()
15+
self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
16+
self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
17+
self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
18+
self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))
19+
20+
def test_getvar(self):
21+
current_frame = sys._getframe()
22+
x = 1
23+
self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1)
24+
self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1)
25+
with self.assertRaises(NameError):
26+
_testcapi.frame_getvar(current_frame, "y")
27+
with self.assertRaises(NameError):
28+
_testcapi.frame_getvarstring(current_frame, b"y")
29+
30+
# wrong name type
31+
with self.assertRaises(TypeError):
32+
_testcapi.frame_getvar(current_frame, b'x')
33+
with self.assertRaises(TypeError):
34+
_testcapi.frame_getvar(current_frame, 123)
35+
36+
def getgenframe(self):
37+
yield sys._getframe()
38+
39+
def test_frame_get_generator(self):
40+
gen = self.getgenframe()
41+
frame = next(gen)
42+
self.assertIs(gen, _testcapi.frame_getgenerator(frame))
43+
44+
def test_frame_fback_api(self):
45+
"""Test that accessing `f_back` does not cause a segmentation fault on
46+
a frame created with `PyFrame_New` (GH-99110)."""
47+
def dummy():
48+
pass
49+
50+
frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
51+
# The following line should not cause a segmentation fault.
52+
self.assertIsNone(frame.f_back)
53+
54+
55+
if __name__ == "__main__":
56+
unittest.main()

Lib/test/test_frame.py

-45
Original file line numberDiff line numberDiff line change
@@ -773,51 +773,6 @@ def f():
773773
self.assertIs(catcher.unraisable.exc_type, TypeError)
774774
self.assertIsNone(weak())
775775

776-
@unittest.skipIf(_testcapi is None, 'need _testcapi')
777-
class TestCAPI(unittest.TestCase):
778-
def getframe(self):
779-
return sys._getframe()
780-
781-
def test_frame_getters(self):
782-
frame = self.getframe()
783-
self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
784-
self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
785-
self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
786-
self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))
787-
788-
def test_getvar(self):
789-
current_frame = sys._getframe()
790-
x = 1
791-
self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1)
792-
self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1)
793-
with self.assertRaises(NameError):
794-
_testcapi.frame_getvar(current_frame, "y")
795-
with self.assertRaises(NameError):
796-
_testcapi.frame_getvarstring(current_frame, b"y")
797-
798-
# wrong name type
799-
with self.assertRaises(TypeError):
800-
_testcapi.frame_getvar(current_frame, b'x')
801-
with self.assertRaises(TypeError):
802-
_testcapi.frame_getvar(current_frame, 123)
803-
804-
def getgenframe(self):
805-
yield sys._getframe()
806-
807-
def test_frame_get_generator(self):
808-
gen = self.getgenframe()
809-
frame = next(gen)
810-
self.assertIs(gen, _testcapi.frame_getgenerator(frame))
811-
812-
def test_frame_fback_api(self):
813-
"""Test that accessing `f_back` does not cause a segmentation fault on
814-
a frame created with `PyFrame_New` (GH-99110)."""
815-
def dummy():
816-
pass
817-
818-
frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
819-
# The following line should not cause a segmentation fault.
820-
self.assertIsNone(frame.f_back)
821776

822777
if __name__ == "__main__":
823778
unittest.main()

Modules/Setup.stdlib.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
163163
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
164164
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
165-
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c
165+
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c
166166
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
167167
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
168168
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c

Modules/_testcapi/frame.c

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#include "parts.h"
2+
#include "util.h"
3+
4+
#include "frameobject.h" // PyFrame_New()
5+
6+
7+
static PyObject *
8+
frame_getlocals(PyObject *self, PyObject *frame)
9+
{
10+
if (!PyFrame_Check(frame)) {
11+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
12+
return NULL;
13+
}
14+
return PyFrame_GetLocals((PyFrameObject *)frame);
15+
}
16+
17+
18+
static PyObject *
19+
frame_getglobals(PyObject *self, PyObject *frame)
20+
{
21+
if (!PyFrame_Check(frame)) {
22+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
23+
return NULL;
24+
}
25+
return PyFrame_GetGlobals((PyFrameObject *)frame);
26+
}
27+
28+
29+
static PyObject *
30+
frame_getgenerator(PyObject *self, PyObject *frame)
31+
{
32+
if (!PyFrame_Check(frame)) {
33+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
34+
return NULL;
35+
}
36+
return PyFrame_GetGenerator((PyFrameObject *)frame);
37+
}
38+
39+
40+
static PyObject *
41+
frame_getbuiltins(PyObject *self, PyObject *frame)
42+
{
43+
if (!PyFrame_Check(frame)) {
44+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
45+
return NULL;
46+
}
47+
return PyFrame_GetBuiltins((PyFrameObject *)frame);
48+
}
49+
50+
51+
static PyObject *
52+
frame_getlasti(PyObject *self, PyObject *frame)
53+
{
54+
if (!PyFrame_Check(frame)) {
55+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
56+
return NULL;
57+
}
58+
int lasti = PyFrame_GetLasti((PyFrameObject *)frame);
59+
if (lasti < 0) {
60+
assert(lasti == -1);
61+
Py_RETURN_NONE;
62+
}
63+
return PyLong_FromLong(lasti);
64+
}
65+
66+
67+
static PyObject *
68+
frame_new(PyObject *self, PyObject *args)
69+
{
70+
PyObject *code, *globals, *locals;
71+
if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) {
72+
return NULL;
73+
}
74+
if (!PyCode_Check(code)) {
75+
PyErr_SetString(PyExc_TypeError, "argument must be a code object");
76+
return NULL;
77+
}
78+
PyThreadState *tstate = PyThreadState_Get();
79+
80+
return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals);
81+
}
82+
83+
84+
static PyObject *
85+
frame_getvar(PyObject *self, PyObject *args)
86+
{
87+
PyObject *frame, *name;
88+
if (!PyArg_ParseTuple(args, "OO", &frame, &name)) {
89+
return NULL;
90+
}
91+
if (!PyFrame_Check(frame)) {
92+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
93+
return NULL;
94+
}
95+
96+
return PyFrame_GetVar((PyFrameObject *)frame, name);
97+
}
98+
99+
100+
static PyObject *
101+
frame_getvarstring(PyObject *self, PyObject *args)
102+
{
103+
PyObject *frame;
104+
const char *name;
105+
if (!PyArg_ParseTuple(args, "Oy", &frame, &name)) {
106+
return NULL;
107+
}
108+
if (!PyFrame_Check(frame)) {
109+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
110+
return NULL;
111+
}
112+
113+
return PyFrame_GetVarString((PyFrameObject *)frame, name);
114+
}
115+
116+
117+
static PyMethodDef test_methods[] = {
118+
{"frame_getlocals", frame_getlocals, METH_O, NULL},
119+
{"frame_getglobals", frame_getglobals, METH_O, NULL},
120+
{"frame_getgenerator", frame_getgenerator, METH_O, NULL},
121+
{"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
122+
{"frame_getlasti", frame_getlasti, METH_O, NULL},
123+
{"frame_new", frame_new, METH_VARARGS, NULL},
124+
{"frame_getvar", frame_getvar, METH_VARARGS, NULL},
125+
{"frame_getvarstring", frame_getvarstring, METH_VARARGS, NULL},
126+
{NULL},
127+
};
128+
129+
int
130+
_PyTestCapi_Init_Frame(PyObject *m)
131+
{
132+
return PyModule_AddFunctions(m, test_methods);
133+
}
134+

Modules/_testcapi/parts.h

+1
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,6 @@ int _PyTestCapi_Init_Monitoring(PyObject *module);
6262
int _PyTestCapi_Init_Object(PyObject *module);
6363
int _PyTestCapi_Init_Config(PyObject *mod);
6464
int _PyTestCapi_Init_Import(PyObject *mod);
65+
int _PyTestCapi_Init_Frame(PyObject *mod);
6566

6667
#endif // Py_TESTCAPI_PARTS_H

Modules/_testcapimodule.c

+3-111
Original file line numberDiff line numberDiff line change
@@ -2533,109 +2533,6 @@ test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args))
25332533
Py_RETURN_NONE;
25342534
}
25352535

2536-
static PyObject *
2537-
frame_getlocals(PyObject *self, PyObject *frame)
2538-
{
2539-
if (!PyFrame_Check(frame)) {
2540-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
2541-
return NULL;
2542-
}
2543-
return PyFrame_GetLocals((PyFrameObject *)frame);
2544-
}
2545-
2546-
static PyObject *
2547-
frame_getglobals(PyObject *self, PyObject *frame)
2548-
{
2549-
if (!PyFrame_Check(frame)) {
2550-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
2551-
return NULL;
2552-
}
2553-
return PyFrame_GetGlobals((PyFrameObject *)frame);
2554-
}
2555-
2556-
static PyObject *
2557-
frame_getgenerator(PyObject *self, PyObject *frame)
2558-
{
2559-
if (!PyFrame_Check(frame)) {
2560-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
2561-
return NULL;
2562-
}
2563-
return PyFrame_GetGenerator((PyFrameObject *)frame);
2564-
}
2565-
2566-
static PyObject *
2567-
frame_getbuiltins(PyObject *self, PyObject *frame)
2568-
{
2569-
if (!PyFrame_Check(frame)) {
2570-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
2571-
return NULL;
2572-
}
2573-
return PyFrame_GetBuiltins((PyFrameObject *)frame);
2574-
}
2575-
2576-
static PyObject *
2577-
frame_getlasti(PyObject *self, PyObject *frame)
2578-
{
2579-
if (!PyFrame_Check(frame)) {
2580-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
2581-
return NULL;
2582-
}
2583-
int lasti = PyFrame_GetLasti((PyFrameObject *)frame);
2584-
if (lasti < 0) {
2585-
assert(lasti == -1);
2586-
Py_RETURN_NONE;
2587-
}
2588-
return PyLong_FromLong(lasti);
2589-
}
2590-
2591-
static PyObject *
2592-
frame_new(PyObject *self, PyObject *args)
2593-
{
2594-
PyObject *code, *globals, *locals;
2595-
if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) {
2596-
return NULL;
2597-
}
2598-
if (!PyCode_Check(code)) {
2599-
PyErr_SetString(PyExc_TypeError, "argument must be a code object");
2600-
return NULL;
2601-
}
2602-
PyThreadState *tstate = PyThreadState_Get();
2603-
2604-
return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals);
2605-
}
2606-
2607-
static PyObject *
2608-
test_frame_getvar(PyObject *self, PyObject *args)
2609-
{
2610-
PyObject *frame, *name;
2611-
if (!PyArg_ParseTuple(args, "OO", &frame, &name)) {
2612-
return NULL;
2613-
}
2614-
if (!PyFrame_Check(frame)) {
2615-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
2616-
return NULL;
2617-
}
2618-
2619-
return PyFrame_GetVar((PyFrameObject *)frame, name);
2620-
}
2621-
2622-
static PyObject *
2623-
test_frame_getvarstring(PyObject *self, PyObject *args)
2624-
{
2625-
PyObject *frame;
2626-
const char *name;
2627-
if (!PyArg_ParseTuple(args, "Oy", &frame, &name)) {
2628-
return NULL;
2629-
}
2630-
if (!PyFrame_Check(frame)) {
2631-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
2632-
return NULL;
2633-
}
2634-
2635-
return PyFrame_GetVarString((PyFrameObject *)frame, name);
2636-
}
2637-
2638-
26392536
static PyObject *
26402537
gen_get_code(PyObject *self, PyObject *gen)
26412538
{
@@ -3599,14 +3496,6 @@ static PyMethodDef TestMethods[] = {
35993496
{"type_get_tp_mro", type_get_tp_mro, METH_O},
36003497
{"get_basic_static_type", get_basic_static_type, METH_VARARGS, NULL},
36013498
{"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
3602-
{"frame_getlocals", frame_getlocals, METH_O, NULL},
3603-
{"frame_getglobals", frame_getglobals, METH_O, NULL},
3604-
{"frame_getgenerator", frame_getgenerator, METH_O, NULL},
3605-
{"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
3606-
{"frame_getlasti", frame_getlasti, METH_O, NULL},
3607-
{"frame_new", frame_new, METH_VARARGS, NULL},
3608-
{"frame_getvar", test_frame_getvar, METH_VARARGS, NULL},
3609-
{"frame_getvarstring", test_frame_getvarstring, METH_VARARGS, NULL},
36103499
{"gen_get_code", gen_get_code, METH_O, NULL},
36113500
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
36123501
{"test_code_api", test_code_api, METH_NOARGS, NULL},
@@ -4404,6 +4293,9 @@ PyInit__testcapi(void)
44044293
if (_PyTestCapi_Init_Import(m) < 0) {
44054294
return NULL;
44064295
}
4296+
if (_PyTestCapi_Init_Frame(m) < 0) {
4297+
return NULL;
4298+
}
44074299

44084300
PyState_AddModule(m, &_testcapimodule);
44094301
return m;

0 commit comments

Comments
 (0)