diff options
author | Mark Shannon <mark@hotpy.org> | 2022-04-08 11:18:57 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-08 11:18:57 (GMT) |
commit | 5b4a4b6f0905c60514528b454af43aeea058b5a2 (patch) | |
tree | ab395cfccfecb94af2db5055f7477ddb39a9ea3e | |
parent | f4b328e2bbbcc1096a28e903f07868b425397767 (diff) | |
download | cpython-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.rst | 11 | ||||
-rw-r--r-- | Doc/whatsnew/3.11.rst | 6 | ||||
-rw-r--r-- | Include/cpython/frameobject.h | 1 | ||||
-rw-r--r-- | Lib/test/test_capi.py | 1 | ||||
-rw-r--r-- | Misc/NEWS.d/next/C API/2022-04-08-11-29-36.bpo-40421.H0ORmT.rst | 2 | ||||
-rw-r--r-- | Modules/_testcapimodule.c | 16 | ||||
-rw-r--r-- | Objects/frameobject.c | 12 |
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) { |