summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2022-04-08 11:18:57 (GMT)
committerGitHub <noreply@github.com>2022-04-08 11:18:57 (GMT)
commit5b4a4b6f0905c60514528b454af43aeea058b5a2 (patch)
treeab395cfccfecb94af2db5055f7477ddb39a9ea3e
parentf4b328e2bbbcc1096a28e903f07868b425397767 (diff)
downloadcpython-5b4a4b6f0905c60514528b454af43aeea058b5a2.zip
cpython-5b4a4b6f0905c60514528b454af43aeea058b5a2.tar.gz
cpython-5b4a4b6f0905c60514528b454af43aeea058b5a2.tar.bz2
Add new PyFrame_GetLasti C-API function (GH-32413)
-rw-r--r--Doc/c-api/frame.rst11
-rw-r--r--Doc/whatsnew/3.11.rst6
-rw-r--r--Include/cpython/frameobject.h1
-rw-r--r--Lib/test/test_capi.py1
-rw-r--r--Misc/NEWS.d/next/C API/2022-04-08-11-29-36.bpo-40421.H0ORmT.rst2
-rw-r--r--Modules/_testcapimodule.c16
-rw-r--r--Objects/frameobject.c12
7 files changed, 45 insertions, 4 deletions
diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst
index f6c682c..68e5dc6 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 bc4a195..2da01d8 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 ffeb8bd..01cf6c9 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 3837f80..40e4774 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -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
index 0000000..2e10c23
--- /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 759656a..13dd294 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 07b6107..5bb8937 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)
{