]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Add new PyFrame_GetLasti C-API function (GH-32413)
authorMark Shannon <mark@hotpy.org>
Fri, 8 Apr 2022 11:18:57 +0000 (12:18 +0100)
committerGitHub <noreply@github.com>
Fri, 8 Apr 2022 11:18:57 +0000 (12:18 +0100)
Doc/c-api/frame.rst
Doc/whatsnew/3.11.rst
Include/cpython/frameobject.h
Lib/test/test_capi.py
Misc/NEWS.d/next/C API/2022-04-08-11-29-36.bpo-40421.H0ORmT.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Objects/frameobject.c

index f6c682c1e6c1caff3042c75591f7328fa5d0239f..68e5dc6daeaec7ee966af6d3a2afed3e65671ca6 100644 (file)
@@ -78,6 +78,17 @@ See also :ref:`Reflection <reflection>`.
    .. versionadded:: 3.11
 
 
+.. c:function:: int PyFrame_GetLasti(PyFrameObject *frame)
+
+   Get the *frame*'s ``f_lasti`` attribute (:class:`dict`).
+
+   Returns -1 if ``frame.f_lasti`` is ``None``.
+
+   *frame* must not be ``NULL``.
+
+   .. versionadded:: 3.11
+
+
 .. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame)
 
    Get the *frame*'s ``f_locals`` attribute (:class:`dict`).
index bc4a1953c10c22a7c8b07db87bd790854f9ada79..2da01d8105ac6b805de59f3b296494253eef09c5 100644 (file)
@@ -1121,7 +1121,7 @@ New Features
 
 * Add new functions to get frame object attributes:
   :c:func:`PyFrame_GetBuiltins`, :c:func:`PyFrame_GetGenerator`,
-  :c:func:`PyFrame_GetGlobals`.
+  :c:func:`PyFrame_GetGlobals`, :c:func:`PyFrame_GetLasti`.
 
 Porting to Python 3.11
 ----------------------
@@ -1246,9 +1246,9 @@ Porting to Python 3.11
   * ``f_gen``: use :c:func:`PyFrame_GetGenerator`.
   * ``f_globals``: use :c:func:`PyFrame_GetGlobals`.
   * ``f_iblock``: removed.
-  * ``f_lasti``: use ``PyObject_GetAttrString((PyObject*)frame, "f_lasti")``.
+  * ``f_lasti``: use :c:func:`PyFrame_GetLasti`.
     Code using ``f_lasti`` with ``PyCode_Addr2Line()`` should use
-    :c:func:`PyFrame_GetLineNumber` instead.
+    :c:func:`PyFrame_GetLineNumber` instead; it may be faster.
   * ``f_lineno``: use :c:func:`PyFrame_GetLineNumber`
   * ``f_locals``: use :c:func:`PyFrame_GetLocals`.
   * ``f_stackdepth``: removed.
index ffeb8bd04a46f4b6bf8e3e0607dc3547edb61f91..01cf6c9b89ae434e00e3e6afe4d5e3102e818906 100644 (file)
@@ -29,3 +29,4 @@ PyAPI_FUNC(PyObject *) PyFrame_GetGlobals(PyFrameObject *frame);
 PyAPI_FUNC(PyObject *) PyFrame_GetBuiltins(PyFrameObject *frame);
 
 PyAPI_FUNC(PyObject *) PyFrame_GetGenerator(PyFrameObject *frame);
+PyAPI_FUNC(int) PyFrame_GetLasti(PyFrameObject *frame);
index 3837f801b3c738f819d85df3cf8496ba9fd96df8..40e4774c6b8ed249b547bc9f44287b56ab097957 100644 (file)
@@ -1102,6 +1102,7 @@ class Test_FrameAPI(unittest.TestCase):
         self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
         self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
         self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
+        self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))
 
     def test_frame_get_generator(self):
         gen = self.getgenframe()
diff --git a/Misc/NEWS.d/next/C API/2022-04-08-11-29-36.bpo-40421.H0ORmT.rst b/Misc/NEWS.d/next/C API/2022-04-08-11-29-36.bpo-40421.H0ORmT.rst
new file mode 100644 (file)
index 0000000..2e10c23
--- /dev/null
@@ -0,0 +1,2 @@
+Add ``PyFrame_GetLasti`` C-API function to access frame object's ``lasti``
+attribute safely from C code.
index 759656ae1f8a1516ed641fb41f81359733d88d8d..13dd29427aa2ca8fb22faddfa7e29e8be2abd40d 100644 (file)
@@ -5893,6 +5893,21 @@ frame_getbuiltins(PyObject *self, PyObject *frame)
     return PyFrame_GetBuiltins((PyFrameObject *)frame);
 }
 
+static PyObject *
+frame_getlasti(PyObject *self, PyObject *frame)
+{
+    if (!PyFrame_Check(frame)) {
+        PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+        return NULL;
+    }
+    int lasti = PyFrame_GetLasti((PyFrameObject *)frame);
+    if (lasti < 0) {
+        assert(lasti == -1);
+        Py_RETURN_NONE;
+    }
+    return PyLong_FromLong(lasti);
+}
+
 
 static PyObject *negative_dictoffset(PyObject *, PyObject *);
 static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
@@ -6186,6 +6201,7 @@ static PyMethodDef TestMethods[] = {
     {"frame_getglobals", frame_getglobals, METH_O, NULL},
     {"frame_getgenerator", frame_getgenerator, METH_O, NULL},
     {"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
+    {"frame_getlasti", frame_getlasti, METH_O, NULL},
     {NULL, NULL} /* sentinel */
 };
 
index 07b610717d2a21f53aca55950932e4878893a24d..5bb8937009897c662e7d69a5a780c73c927cc2e3 100644 (file)
@@ -880,7 +880,7 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)
     // This only works when opcode is a non-quickened form:
     assert(_PyOpcode_Deopt[opcode] == opcode);
     int check_oparg = 0;
-    for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code); 
+    for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code);
          instruction < frame->prev_instr; instruction++)
     {
         int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)];
@@ -1135,6 +1135,16 @@ PyFrame_GetBuiltins(PyFrameObject *frame)
     return frame_getbuiltins(frame, NULL);
 }
 
+int
+PyFrame_GetLasti(PyFrameObject *frame)
+{
+    int lasti = _PyInterpreterFrame_LASTI(frame->f_frame);
+    if (lasti < 0) {
+        return -1;
+    }
+    return lasti*2;
+}
+
 PyObject *
 PyFrame_GetGenerator(PyFrameObject *frame)
 {