Skip to content

Commit cfa517d

Browse files
authored
GH-96803: Document and test new unstable internal frame API functions (GH-104211)
Weaken contract of PyUnstable_InterpreterFrame_GetCode to return PyObject*.
1 parent 68b5f08 commit cfa517d

File tree

5 files changed

+98
-3
lines changed

5 files changed

+98
-3
lines changed

Doc/c-api/frame.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,38 @@ See also :ref:`Reflection <reflection>`.
130130
.. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame)
131131
132132
Return the line number that *frame* is currently executing.
133+
134+
135+
136+
Internal Frames
137+
---------------
138+
139+
Unless using :pep:`523`, you will not need this.
140+
141+
.. c:struct:: _PyInterpreterFrame
142+
143+
The interpreter's internal frame representation.
144+
145+
.. versionadded:: 3.11
146+
147+
.. c:function:: PyObject* PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
148+
149+
Return a :term:`strong reference` to the code object for the frame.
150+
151+
.. versionadded:: 3.12
152+
153+
154+
.. c:function:: int PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame);
155+
156+
Return the byte offset into the last executed instruction.
157+
158+
.. versionadded:: 3.12
159+
160+
161+
.. c:function:: int PyUnstable_InterpreterFrame_GetLine(struct _PyInterpreterFrame *frame);
162+
163+
Return the currently executing line number, or -1 if there is no line number.
164+
165+
.. versionadded:: 3.12
166+
167+

Include/cpython/frameobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
3535

3636
/* Returns the code object of the frame (strong reference).
3737
* Does not raise an exception. */
38-
PyAPI_FUNC(PyCodeObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
38+
PyAPI_FUNC(PyObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
3939

4040
/* Returns a byte ofsset into the last executed instruction.
4141
* Does not raise an exception. */

Lib/test/test_capi/test_misc.py

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

17461746

1747+
class TestInternalFrameApi(unittest.TestCase):
1748+
1749+
@staticmethod
1750+
def func():
1751+
return sys._getframe()
1752+
1753+
def test_code(self):
1754+
frame = self.func()
1755+
code = _testinternalcapi.iframe_getcode(frame)
1756+
self.assertIs(code, self.func.__code__)
1757+
1758+
def test_lasti(self):
1759+
frame = self.func()
1760+
lasti = _testinternalcapi.iframe_getlasti(frame)
1761+
self.assertGreater(lasti, 0)
1762+
self.assertLess(lasti, len(self.func.__code__.co_code))
1763+
1764+
def test_line(self):
1765+
frame = self.func()
1766+
line = _testinternalcapi.iframe_getline(frame)
1767+
firstline = self.func.__code__.co_firstlineno
1768+
self.assertEqual(line, firstline + 2)
1769+
1770+
17471771
SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
17481772

17491773
class Test_Pep523API(unittest.TestCase):

Modules/_testinternalcapi.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define PY_SSIZE_T_CLEAN
1313

1414
#include "Python.h"
15+
#include "frameobject.h"
1516
#include "pycore_atomic_funcs.h" // _Py_atomic_int_get()
1617
#include "pycore_bitutils.h" // _Py_bswap32()
1718
#include "pycore_compile.h" // _PyCompile_CodeGen, _PyCompile_OptimizeCfg, _PyCompile_Assemble
@@ -757,6 +758,38 @@ clear_extension(PyObject *self, PyObject *args)
757758
Py_RETURN_NONE;
758759
}
759760

761+
static PyObject *
762+
iframe_getcode(PyObject *self, PyObject *frame)
763+
{
764+
if (!PyFrame_Check(frame)) {
765+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
766+
return NULL;
767+
}
768+
struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame;
769+
return PyUnstable_InterpreterFrame_GetCode(f);
770+
}
771+
772+
static PyObject *
773+
iframe_getline(PyObject *self, PyObject *frame)
774+
{
775+
if (!PyFrame_Check(frame)) {
776+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
777+
return NULL;
778+
}
779+
struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame;
780+
return PyLong_FromLong(PyUnstable_InterpreterFrame_GetLine(f));
781+
}
782+
783+
static PyObject *
784+
iframe_getlasti(PyObject *self, PyObject *frame)
785+
{
786+
if (!PyFrame_Check(frame)) {
787+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
788+
return NULL;
789+
}
790+
struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame;
791+
return PyLong_FromLong(PyUnstable_InterpreterFrame_GetLasti(f));
792+
}
760793

761794
static PyMethodDef module_functions[] = {
762795
{"get_configs", get_configs, METH_NOARGS},
@@ -781,6 +814,9 @@ static PyMethodDef module_functions[] = {
781814
_TESTINTERNALCAPI_ASSEMBLE_CODE_OBJECT_METHODDEF
782815
{"get_interp_settings", get_interp_settings, METH_VARARGS, NULL},
783816
{"clear_extension", clear_extension, METH_VARARGS, NULL},
817+
{"iframe_getcode", iframe_getcode, METH_O, NULL},
818+
{"iframe_getline", iframe_getline, METH_O, NULL},
819+
{"iframe_getlasti", iframe_getlasti, METH_O, NULL},
784820
{NULL, NULL} /* sentinel */
785821
};
786822

Python/frame.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,10 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
146146

147147
/* Unstable API functions */
148148

149-
PyCodeObject *
149+
PyObject *
150150
PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame)
151151
{
152-
PyCodeObject *code = frame->f_code;
152+
PyObject *code = (PyObject *)frame->f_code;
153153
Py_INCREF(code);
154154
return code;
155155
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy