Skip to content

Commit 58ac314

Browse files
committed
gh-141518: Add PyUnstable_InterpreterFrame_GetFrameObject() function
Add also PyUnstable_InterpreterFrame type.
1 parent da1d468 commit 58ac314

File tree

11 files changed

+78
-32
lines changed

11 files changed

+78
-32
lines changed

Doc/c-api/frame.rst

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,16 @@ Unless using :pep:`523`, you will not need this.
236236
.. versionadded:: 3.12
237237
238238
239+
.. c:function:: PyFrameObject* PyUnstable_InterpreterFrame_GetFrameObject(PyUnstable_InterpreterFrame *frame)
240+
241+
Get a frame object from an interpreter frame.
242+
243+
Return a new :term:`strong reference` on success, or set an exception and
244+
return ``NULL`` on error.
245+
246+
.. versionadded:: next
247+
248+
239249
.. c:function:: int PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame);
240250
241251
Return the byte offset into the last executed instruction.
@@ -248,5 +258,3 @@ Unless using :pep:`523`, you will not need this.
248258
Return the currently executing line number, or -1 if there is no line number.
249259
250260
.. versionadded:: 3.12
251-
252-

Doc/c-api/init.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1438,7 +1438,7 @@ All of the following functions must be called after :c:func:`Py_Initialize`.
14381438
.. versionadded:: 3.8
14391439
14401440
1441-
.. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
1441+
.. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, PyUnstable_InterpreterFrame *frame, int throwflag)
14421442
14431443
Type of a frame evaluation function.
14441444

Doc/whatsnew/3.15.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,10 @@ New features
10991099
* Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array.
11001100
(Contributed by Victor Stinner in :gh:`111489`.)
11011101

1102+
* Add :c:func:`PyUnstable_InterpreterFrame_GetFrameObject` to get a frame
1103+
object from an interpreter frame.
1104+
(Contributed by Victor Stinner in :gh:`141518`.)
1105+
11021106
* Add :c:func:`PyUnstable_Object_Dump` to dump an object to ``stderr``.
11031107
It should only be used for debugging.
11041108
(Contributed by Victor Stinner in :gh:`141070`.)

Include/cpython/pyframe.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,25 @@ PyAPI_FUNC(PyObject*) PyFrame_GetVarString(PyFrameObject *frame, const char *nam
2323
* implementing custom frame evaluators with PEP 523. */
2424

2525
struct _PyInterpreterFrame;
26+
typedef struct _PyInterpreterFrame PyUnstable_InterpreterFrame;
2627

2728
/* Returns the code object of the frame (strong reference).
2829
* Does not raise an exception. */
29-
PyAPI_FUNC(PyObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
30+
PyAPI_FUNC(PyObject *) PyUnstable_InterpreterFrame_GetCode(
31+
PyUnstable_InterpreterFrame *frame);
3032

3133
/* Returns a byte offset into the last executed instruction.
3234
* Does not raise an exception. */
33-
PyAPI_FUNC(int) PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame);
35+
PyAPI_FUNC(int) PyUnstable_InterpreterFrame_GetLasti(
36+
PyUnstable_InterpreterFrame *frame);
3437

3538
/* Returns the currently executing line number, or -1 if there is no line number.
3639
* Does not raise an exception. */
37-
PyAPI_FUNC(int) PyUnstable_InterpreterFrame_GetLine(struct _PyInterpreterFrame *frame);
40+
PyAPI_FUNC(int) PyUnstable_InterpreterFrame_GetLine(
41+
PyUnstable_InterpreterFrame *frame);
42+
43+
PyAPI_FUNC(PyFrameObject*) PyUnstable_InterpreterFrame_GetFrameObject(
44+
PyUnstable_InterpreterFrame *frame);
3845

3946
#define PyUnstable_EXECUTABLE_KIND_SKIP 0
4047
#define PyUnstable_EXECUTABLE_KIND_PY_FUNCTION 1

Include/cpython/pystate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
302302

303303
/* Frame evaluation API */
304304

305-
typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, struct _PyInterpreterFrame *, int);
305+
typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, PyUnstable_InterpreterFrame *, int);
306306

307307
PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc(
308308
PyInterpreterState *interp);

Include/internal/pycore_interpframe.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ _PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
290290
{
291291

292292
assert(!_PyFrame_IsIncomplete(frame));
293-
PyFrameObject *res = frame->frame_obj;
293+
PyFrameObject *res = frame->frame_obj;
294294
if (res != NULL) {
295295
return res;
296296
}

Lib/test/test_capi/test_frame.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55

66
_testcapi = import_helper.import_module('_testcapi')
7+
_testinternalcapi = import_helper.import_module('_testinternalcapi')
78

89

910
class FrameTest(unittest.TestCase):
@@ -52,5 +53,34 @@ def dummy():
5253
self.assertIsNone(frame.f_back)
5354

5455

56+
class InterpreterFrameTest(unittest.TestCase):
57+
58+
@staticmethod
59+
def func():
60+
return sys._getframe()
61+
62+
def test_code(self):
63+
frame = self.func()
64+
code = _testinternalcapi.iframe_getcode(frame)
65+
self.assertIs(code, self.func.__code__)
66+
67+
def test_lasti(self):
68+
frame = self.func()
69+
lasti = _testinternalcapi.iframe_getlasti(frame)
70+
self.assertGreater(lasti, 0)
71+
self.assertLess(lasti, len(self.func.__code__.co_code))
72+
73+
def test_line(self):
74+
frame = self.func()
75+
line = _testinternalcapi.iframe_getline(frame)
76+
firstline = self.func.__code__.co_firstlineno
77+
self.assertEqual(line, firstline + 2)
78+
79+
def test_frame_object(self):
80+
frame = sys._getframe()
81+
obj = _testinternalcapi.iframe_getframeobject(frame)
82+
self.assertIs(obj, frame)
83+
84+
5585
if __name__ == "__main__":
5686
unittest.main()

Lib/test/test_capi/test_misc.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,30 +2754,6 @@ class Subclass(BaseException, self.module.StateAccessType):
27542754
self.assertIs(Subclass().get_defining_module(), self.module)
27552755

27562756

2757-
class TestInternalFrameApi(unittest.TestCase):
2758-
2759-
@staticmethod
2760-
def func():
2761-
return sys._getframe()
2762-
2763-
def test_code(self):
2764-
frame = self.func()
2765-
code = _testinternalcapi.iframe_getcode(frame)
2766-
self.assertIs(code, self.func.__code__)
2767-
2768-
def test_lasti(self):
2769-
frame = self.func()
2770-
lasti = _testinternalcapi.iframe_getlasti(frame)
2771-
self.assertGreater(lasti, 0)
2772-
self.assertLess(lasti, len(self.func.__code__.co_code))
2773-
2774-
def test_line(self):
2775-
frame = self.func()
2776-
line = _testinternalcapi.iframe_getline(frame)
2777-
firstline = self.func.__code__.co_firstlineno
2778-
self.assertEqual(line, firstline + 2)
2779-
2780-
27812757
SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
27822758

27832759
class Test_Pep523API(unittest.TestCase):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyUnstable_InterpreterFrame_GetFrameObject` to get a frame
2+
object from an interpreter frame. Patch by Victor Stinner.

Modules/_testinternalcapi.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,17 @@ iframe_getlasti(PyObject *self, PyObject *frame)
959959
return PyLong_FromLong(PyUnstable_InterpreterFrame_GetLasti(f));
960960
}
961961

962+
static PyObject *
963+
iframe_getframeobject(PyObject *self, PyObject *frame)
964+
{
965+
if (!PyFrame_Check(frame)) {
966+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
967+
return NULL;
968+
}
969+
struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame;
970+
return (PyObject*)PyUnstable_InterpreterFrame_GetFrameObject(f);
971+
}
972+
962973
static PyObject *
963974
code_returns_only_none(PyObject *self, PyObject *arg)
964975
{
@@ -2529,6 +2540,7 @@ static PyMethodDef module_functions[] = {
25292540
{"iframe_getcode", iframe_getcode, METH_O, NULL},
25302541
{"iframe_getline", iframe_getline, METH_O, NULL},
25312542
{"iframe_getlasti", iframe_getlasti, METH_O, NULL},
2543+
{"iframe_getframeobject", iframe_getframeobject, METH_O, NULL},
25322544
{"code_returns_only_none", code_returns_only_none, METH_O, NULL},
25332545
{"get_co_framesize", get_co_framesize, METH_O, NULL},
25342546
{"get_co_localskinds", get_co_localskinds, METH_O, NULL},

0 commit comments

Comments
 (0)