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

This commit is contained in:
Mark Shannon 2022-04-08 12:18:57 +01:00 committed by GitHub
parent f4b328e2bb
commit 5b4a4b6f09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 45 additions and 4 deletions

View 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`).

View 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.

View 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);

View 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()

View File

@ -0,0 +1,2 @@
Add ``PyFrame_GetLasti`` C-API function to access frame object's ``lasti``
attribute safely from C code.

View 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 */
};

View 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)
{