.. 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`).
* 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
----------------------
* ``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.
PyAPI_FUNC(PyObject *) PyFrame_GetBuiltins(PyFrameObject *frame);
PyAPI_FUNC(PyObject *) PyFrame_GetGenerator(PyFrameObject *frame);
+PyAPI_FUNC(int) PyFrame_GetLasti(PyFrameObject *frame);
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()
--- /dev/null
+Add ``PyFrame_GetLasti`` C-API function to access frame object's ``lasti``
+attribute safely from C code.
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 *);
{"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 */
};
// 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)];
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)
{