[Python-checkins] GH-96803: Document and test new unstable internal frame API functions (GH-104211)

markshannon webhook-mailer at python.org
Thu May 18 05:10:24 EDT 2023


https://github.com/python/cpython/commit/cfa517d5a68bae24cbe8d9fe6b8e0d4935e507d2
commit: cfa517d5a68bae24cbe8d9fe6b8e0d4935e507d2
branch: main
author: Mark Shannon <mark at hotpy.org>
committer: markshannon <mark at hotpy.org>
date: 2023-05-18T10:10:15+01:00
summary:

GH-96803: Document and test new unstable internal frame API functions (GH-104211)

Weaken contract of PyUnstable_InterpreterFrame_GetCode to return PyObject*.

files:
M Doc/c-api/frame.rst
M Include/cpython/frameobject.h
M Lib/test/test_capi/test_misc.py
M Modules/_testinternalcapi.c
M Python/frame.c

diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst
index 1ac8f03d6e48..9f7addfbbf3c 100644
--- a/Doc/c-api/frame.rst
+++ b/Doc/c-api/frame.rst
@@ -130,3 +130,38 @@ See also :ref:`Reflection <reflection>`.
 .. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame)
 
    Return the line number that *frame* is currently executing.
+
+
+
+Internal Frames
+---------------
+
+Unless using :pep:`523`, you will not need this.
+
+.. c:struct:: _PyInterpreterFrame
+
+   The interpreter's internal frame representation.
+
+   .. versionadded:: 3.11
+
+.. c:function:: PyObject* PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
+
+    Return a :term:`strong reference` to the code object for the frame.
+
+   .. versionadded:: 3.12
+
+
+.. c:function:: int PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame);
+
+   Return the byte offset into the last executed instruction.
+
+   .. versionadded:: 3.12
+
+
+.. c:function:: int PyUnstable_InterpreterFrame_GetLine(struct _PyInterpreterFrame *frame);
+
+   Return the currently executing line number, or -1 if there is no line number.
+
+   .. versionadded:: 3.12
+
+
diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h
index 6f3efe36ede5..a3dc66617864 100644
--- a/Include/cpython/frameobject.h
+++ b/Include/cpython/frameobject.h
@@ -35,7 +35,7 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
 
 /* Returns the code object of the frame (strong reference).
  * Does not raise an exception. */
-PyAPI_FUNC(PyCodeObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
+PyAPI_FUNC(PyObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame);
 
 /* Returns a byte ofsset into the last executed instruction.
  * Does not raise an exception. */
diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index 0ef0b5d8b658..dc3441e4496a 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -1744,6 +1744,30 @@ class Subclass(BaseException, self.module.StateAccessType):
         self.assertIs(Subclass().get_defining_module(), self.module)
 
 
+class TestInternalFrameApi(unittest.TestCase):
+
+    @staticmethod
+    def func():
+        return sys._getframe()
+
+    def test_code(self):
+        frame = self.func()
+        code = _testinternalcapi.iframe_getcode(frame)
+        self.assertIs(code, self.func.__code__)
+
+    def test_lasti(self):
+        frame = self.func()
+        lasti = _testinternalcapi.iframe_getlasti(frame)
+        self.assertGreater(lasti, 0)
+        self.assertLess(lasti, len(self.func.__code__.co_code))
+
+    def test_line(self):
+        frame = self.func()
+        line = _testinternalcapi.iframe_getline(frame)
+        firstline = self.func.__code__.co_firstlineno
+        self.assertEqual(line, firstline + 2)
+
+
 SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
 
 class Test_Pep523API(unittest.TestCase):
diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c
index ea9b6e72b3c9..5802f1d4ff5f 100644
--- a/Modules/_testinternalcapi.c
+++ b/Modules/_testinternalcapi.c
@@ -12,6 +12,7 @@
 #define PY_SSIZE_T_CLEAN
 
 #include "Python.h"
+#include "frameobject.h"
 #include "pycore_atomic_funcs.h" // _Py_atomic_int_get()
 #include "pycore_bitutils.h"     // _Py_bswap32()
 #include "pycore_compile.h"      // _PyCompile_CodeGen, _PyCompile_OptimizeCfg, _PyCompile_Assemble
@@ -757,6 +758,38 @@ clear_extension(PyObject *self, PyObject *args)
     Py_RETURN_NONE;
 }
 
+static PyObject *
+iframe_getcode(PyObject *self, PyObject *frame)
+{
+    if (!PyFrame_Check(frame)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+        return NULL;
+    }
+    struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame;
+    return PyUnstable_InterpreterFrame_GetCode(f);
+}
+
+static PyObject *
+iframe_getline(PyObject *self, PyObject *frame)
+{
+    if (!PyFrame_Check(frame)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+        return NULL;
+    }
+    struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame;
+    return PyLong_FromLong(PyUnstable_InterpreterFrame_GetLine(f));
+}
+
+static PyObject *
+iframe_getlasti(PyObject *self, PyObject *frame)
+{
+    if (!PyFrame_Check(frame)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+        return NULL;
+    }
+    struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame;
+    return PyLong_FromLong(PyUnstable_InterpreterFrame_GetLasti(f));
+}
 
 static PyMethodDef module_functions[] = {
     {"get_configs", get_configs, METH_NOARGS},
@@ -781,6 +814,9 @@ static PyMethodDef module_functions[] = {
     _TESTINTERNALCAPI_ASSEMBLE_CODE_OBJECT_METHODDEF
     {"get_interp_settings", get_interp_settings, METH_VARARGS, NULL},
     {"clear_extension", clear_extension, METH_VARARGS, NULL},
+    {"iframe_getcode", iframe_getcode, METH_O, NULL},
+    {"iframe_getline", iframe_getline, METH_O, NULL},
+    {"iframe_getlasti", iframe_getlasti, METH_O, NULL},
     {NULL, NULL} /* sentinel */
 };
 
diff --git a/Python/frame.c b/Python/frame.c
index d792b92fa575..b84fd9b6a938 100644
--- a/Python/frame.c
+++ b/Python/frame.c
@@ -146,10 +146,10 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
 
 /* Unstable API functions */
 
-PyCodeObject *
+PyObject *
 PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame)
 {
-    PyCodeObject *code = frame->f_code;
+    PyObject *code = (PyObject *)frame->f_code;
     Py_INCREF(code);
     return code;
 }



More information about the Python-checkins mailing list