[Python-checkins] Add new PyFrame_GetLasti C-API function (GH-32413)

markshannon webhook-mailer at python.org
Fri Apr 8 07:19:02 EDT 2022


https://github.com/python/cpython/commit/5b4a4b6f0905c60514528b454af43aeea058b5a2
commit: 5b4a4b6f0905c60514528b454af43aeea058b5a2
branch: main
author: Mark Shannon <mark at hotpy.org>
committer: markshannon <mark at hotpy.org>
date: 2022-04-08T12:18:57+01:00
summary:

Add new PyFrame_GetLasti C-API function (GH-32413)

files:
A Misc/NEWS.d/next/C API/2022-04-08-11-29-36.bpo-40421.H0ORmT.rst
M Doc/c-api/frame.rst
M Doc/whatsnew/3.11.rst
M Include/cpython/frameobject.h
M Lib/test/test_capi.py
M Modules/_testcapimodule.c
M Objects/frameobject.c

diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst
index f6c682c1e6c1c..68e5dc6daeaec 100644
--- a/Doc/c-api/frame.rst
+++ b/Doc/c-api/frame.rst
@@ -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`).
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index bc4a1953c10c2..2da01d8105ac6 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -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.
diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h
index ffeb8bd04a46f..01cf6c9b89ae4 100644
--- a/Include/cpython/frameobject.h
+++ b/Include/cpython/frameobject.h
@@ -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);
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 3837f801b3c73..40e4774c6b8ed 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -1102,6 +1102,7 @@ def test_frame_getters(self):
         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
index 0000000000000..2e10c2394c933
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2022-04-08-11-29-36.bpo-40421.H0ORmT.rst	
@@ -0,0 +1,2 @@
+Add ``PyFrame_GetLasti`` C-API function to access frame object's ``lasti``
+attribute safely from C code.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 759656ae1f8a1..13dd29427aa2c 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -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 */
 };
 
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 07b610717d2a2..5bb8937009897 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -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)
 {



More information about the Python-checkins mailing list