Skip to content

Commit 63d8e3a

Browse files
committed
pythongh-130931: Add PyFrame_SetBack() function
Add tests on PyFrame_GetBack() and PyFrame_SetBack().
1 parent c497f83 commit 63d8e3a

File tree

7 files changed

+96
-16
lines changed

7 files changed

+96
-16
lines changed

Doc/c-api/frame.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ See also :ref:`Reflection <reflection>`.
140140
Return the line number that *frame* is currently executing.
141141
142142
143+
.. c:function:: void PyFrame_SetBack(PyFrameObject *frame, PyFrameObject *back)
144+
145+
Set the *frame* next outer frame to a :term:`strong reference` to *back*.
146+
147+
.. versionadded:: next
148+
149+
143150
Frame Locals Proxies
144151
^^^^^^^^^^^^^^^^^^^^
145152

Doc/data/refcounts.dat

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,10 @@ PyFrame_GetVarString:PyObject*::+1:
948948
PyFrame_GetVarString:PyFrameObject*:frame:0:
949949
PyFrame_GetVarString:const char*:name::
950950

951+
PyFrame_SetBack:void:::
952+
PyFrame_SetBack:PyFrameObject*:frame:0:
953+
PyFrame_SetBack:PyFrameObject*:back:+1:
954+
951955
PyFrozenSet_Check:int:::
952956
PyFrozenSet_Check:PyObject*:p:0:
953957

Doc/whatsnew/3.14.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,9 @@ New features
15901590
take a C integer and produce a Python :class:`bool` object. (Contributed by
15911591
Pablo Galindo in :issue:`45325`.)
15921592

1593+
* Add :c:func:`PyFrame_SetBack` function to set a frame next outer frame.
1594+
(Contributed by Victor Stinner in :gh:`130931`.)
1595+
15931596

15941597
Limited C API changes
15951598
---------------------

Include/cpython/pyframe.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ PyAPI_DATA(PyTypeObject) PyFrameLocalsProxy_Type;
99
#define PyFrameLocalsProxy_Check(op) Py_IS_TYPE((op), &PyFrameLocalsProxy_Type)
1010

1111
PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);
12+
PyAPI_FUNC(void) PyFrame_SetBack(PyFrameObject *frame, PyFrameObject *back);
1213
PyAPI_FUNC(PyObject *) PyFrame_GetLocals(PyFrameObject *frame);
1314

1415
PyAPI_FUNC(PyObject *) PyFrame_GetGlobals(PyFrameObject *frame);

Lib/test/test_capi/test_frame.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88

99
class FrameTest(unittest.TestCase):
10-
def getframe(self):
10+
def create_frame(self):
1111
return sys._getframe()
1212

1313
def test_frame_getters(self):
14-
frame = self.getframe()
14+
frame = self.create_frame()
1515
self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
1616
self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
1717
self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
@@ -51,6 +51,22 @@ def dummy():
5151
# The following line should not cause a segmentation fault.
5252
self.assertIsNone(frame.f_back)
5353

54+
def test_back(self):
55+
# Test PyFrame_GetBack() and PyFrame_SetBack()
56+
frame_getback = _testcapi.frame_getback
57+
frame_setback = _testcapi.frame_setback
58+
59+
frame = self.create_frame()
60+
current_frame = sys._getframe()
61+
self.assertIs(frame_getback(frame), current_frame)
62+
63+
frame2 = self.create_frame()
64+
try:
65+
frame_setback(frame, frame2)
66+
self.assertIs(frame_getback(frame), frame2)
67+
finally:
68+
frame_setback(frame, current_frame)
69+
5470

5571
if __name__ == "__main__":
5672
unittest.main()

Modules/_testcapi/frame.c

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@
44
#include "frameobject.h" // PyFrame_New()
55

66

7+
static int
8+
check_frame(PyObject *op)
9+
{
10+
if (!PyFrame_Check(op)) {
11+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
12+
return -1;
13+
}
14+
return 0;
15+
}
16+
17+
718
static PyObject *
819
frame_getlocals(PyObject *self, PyObject *frame)
920
{
10-
if (!PyFrame_Check(frame)) {
11-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
21+
if (check_frame(frame) < 0) {
1222
return NULL;
1323
}
1424
return PyFrame_GetLocals((PyFrameObject *)frame);
@@ -18,8 +28,7 @@ frame_getlocals(PyObject *self, PyObject *frame)
1828
static PyObject *
1929
frame_getglobals(PyObject *self, PyObject *frame)
2030
{
21-
if (!PyFrame_Check(frame)) {
22-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
31+
if (check_frame(frame) < 0) {
2332
return NULL;
2433
}
2534
return PyFrame_GetGlobals((PyFrameObject *)frame);
@@ -29,8 +38,7 @@ frame_getglobals(PyObject *self, PyObject *frame)
2938
static PyObject *
3039
frame_getgenerator(PyObject *self, PyObject *frame)
3140
{
32-
if (!PyFrame_Check(frame)) {
33-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
41+
if (check_frame(frame) < 0) {
3442
return NULL;
3543
}
3644
return PyFrame_GetGenerator((PyFrameObject *)frame);
@@ -40,8 +48,7 @@ frame_getgenerator(PyObject *self, PyObject *frame)
4048
static PyObject *
4149
frame_getbuiltins(PyObject *self, PyObject *frame)
4250
{
43-
if (!PyFrame_Check(frame)) {
44-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
51+
if (check_frame(frame) < 0) {
4552
return NULL;
4653
}
4754
return PyFrame_GetBuiltins((PyFrameObject *)frame);
@@ -51,8 +58,7 @@ frame_getbuiltins(PyObject *self, PyObject *frame)
5158
static PyObject *
5259
frame_getlasti(PyObject *self, PyObject *frame)
5360
{
54-
if (!PyFrame_Check(frame)) {
55-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
61+
if (check_frame(frame) < 0) {
5662
return NULL;
5763
}
5864
int lasti = PyFrame_GetLasti((PyFrameObject *)frame);
@@ -88,8 +94,7 @@ frame_getvar(PyObject *self, PyObject *args)
8894
if (!PyArg_ParseTuple(args, "OO", &frame, &name)) {
8995
return NULL;
9096
}
91-
if (!PyFrame_Check(frame)) {
92-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
97+
if (check_frame(frame) < 0) {
9398
return NULL;
9499
}
95100

@@ -105,15 +110,47 @@ frame_getvarstring(PyObject *self, PyObject *args)
105110
if (!PyArg_ParseTuple(args, "Oy", &frame, &name)) {
106111
return NULL;
107112
}
108-
if (!PyFrame_Check(frame)) {
109-
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
113+
if (check_frame(frame) < 0) {
110114
return NULL;
111115
}
112116

113117
return PyFrame_GetVarString((PyFrameObject *)frame, name);
114118
}
115119

116120

121+
static PyObject *
122+
frame_getback(PyObject *self, PyObject *frame)
123+
{
124+
if (check_frame(frame) < 0) {
125+
return NULL;
126+
}
127+
PyFrameObject *back = PyFrame_GetBack((PyFrameObject *)frame);
128+
if (back == NULL) {
129+
Py_RETURN_NONE;
130+
}
131+
return (PyObject*)back;
132+
}
133+
134+
135+
static PyObject *
136+
frame_setback(PyObject *self, PyObject *args)
137+
{
138+
PyObject *frame, *back;
139+
if (!PyArg_ParseTuple(args, "OO", &frame, &back)) {
140+
return NULL;
141+
}
142+
if (check_frame(frame) < 0) {
143+
return NULL;
144+
}
145+
if (check_frame(back) < 0) {
146+
return NULL;
147+
}
148+
149+
PyFrame_SetBack((PyFrameObject *)frame, (PyFrameObject *)back);
150+
Py_RETURN_NONE;
151+
}
152+
153+
117154
static PyMethodDef test_methods[] = {
118155
{"frame_getlocals", frame_getlocals, METH_O, NULL},
119156
{"frame_getglobals", frame_getglobals, METH_O, NULL},
@@ -123,6 +160,8 @@ static PyMethodDef test_methods[] = {
123160
{"frame_new", frame_new, METH_VARARGS, NULL},
124161
{"frame_getvar", frame_getvar, METH_VARARGS, NULL},
125162
{"frame_getvarstring", frame_getvarstring, METH_VARARGS, NULL},
163+
{"frame_getback", frame_getback, METH_O, NULL},
164+
{"frame_setback", frame_setback, METH_VARARGS, NULL},
126165
{NULL},
127166
};
128167

Objects/frameobject.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2250,6 +2250,16 @@ PyFrame_GetBack(PyFrameObject *frame)
22502250
return (PyFrameObject*)Py_XNewRef(back);
22512251
}
22522252

2253+
2254+
void
2255+
PyFrame_SetBack(PyFrameObject *frame, PyFrameObject *back)
2256+
{
2257+
assert(frame != NULL);
2258+
assert(!_PyFrame_IsIncomplete(frame->f_frame));
2259+
Py_XSETREF(frame->f_back, (PyFrameObject*)Py_XNewRef(back));
2260+
}
2261+
2262+
22532263
PyObject*
22542264
PyFrame_GetLocals(PyFrameObject *frame)
22552265
{

0 commit comments

Comments
 (0)