Skip to content

Commit

Permalink
bpo-46836: Move PyFrameObject to pycore_frame.h (GH-31530)
Browse files Browse the repository at this point in the history
Move the PyFrameObject type definition (struct _frame) to the
internal C API pycore_frame.h header file.
  • Loading branch information
vstinner authored and asvetlov committed Feb 26, 2022
1 parent c6d0b5e commit ed1e6d6
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 49 deletions.
12 changes: 10 additions & 2 deletions Doc/c-api/veryhigh.rst
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,16 @@ the same library that the Python runtime is using.
.. c:type:: PyFrameObject
The C structure of the objects used to describe frame objects. The
fields of this type are subject to change at any time.
The C structure of the objects used to describe frame objects.
The structure is only part of the internal C API: fields should not be
access directly. Use getter functions like :c:func:`PyFrame_GetCode` and
:c:func:`PyFrame_GetBack`.
Debuggers and profilers can use the limited C API to access this structure.
.. versionchanged:: 3.11
The structure moved to the internal C API headers.
.. c:function:: PyObject* PyEval_EvalFrame(PyFrameObject *f)
Expand Down
68 changes: 34 additions & 34 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -837,40 +837,40 @@ Porting to Python 3.11
which are not available in the limited C API.
(Contributed by Victor Stinner in :issue:`46007`.)

* Changes of the private :c:type:`PyFrameObject` structure members.
* The :c:type:`PyFrameObject` structure member has been moved to the internal C
API headers.

While the documentation notes that the fields of ``PyFrameObject`` are
subject to change at any time, they have been stable for a long time
and were used in several popular extensions.
In Python 3.11, the frame struct was reorganized to allow performance
optimizations. Rather than reading the fields directly, extensions should
use functions:
While the documentation notes that the :c:type:`PyFrameObject` fields are
subject to change at any time, they have been stable for a long time and were
used in several popular extensions.

* ``f_code``: removed, use :c:func:`PyFrame_GetCode` instead.
Warning: the function returns a :term:`strong reference`, need to call
:c:func:`Py_DECREF`.
* ``f_back``: changed (see below), use :c:func:`PyFrame_GetBack`.
* ``f_builtins``: removed,
use ``PyObject_GetAttrString((PyObject*)frame, "f_builtins")``.
* ``f_globals``: removed,
use ``PyObject_GetAttrString((PyObject*)frame, "f_globals")``.
* ``f_locals``: removed,
use ``PyObject_GetAttrString((PyObject*)frame, "f_locals")``.
* ``f_lasti``: removed,
use ``PyObject_GetAttrString((PyObject*)frame, "f_lasti")``.
Code using ``f_lasti`` with ``PyCode_Addr2Line()`` should use
In Python 3.11, the frame struct was reorganized to allow performance
optimizations. Some fields were removed entirely, as they were details of the
old implementation.

:c:type:`PyFrameObject` fields:

* ``f_back``: use :c:func:`PyFrame_GetBack`.
* ``f_blockstack``: removed.
* ``f_builtins``: use ``PyObject_GetAttrString((PyObject*)frame, "f_builtins")``.
* ``f_code``: use :c:func:`PyFrame_GetCode`.
* ``f_gen``: removed.
* ``f_globals``: use ``PyObject_GetAttrString((PyObject*)frame, "f_globals")``.
* ``f_iblock``: removed.
* ``f_lasti``: use ``PyObject_GetAttrString((PyObject*)frame, "f_lasti")``.
Code using ``f_lasti`` with ``PyCode_Addr2Line()`` must use
:c:func:`PyFrame_GetLineNumber` instead.

The following fields were removed entirely, as they were details
of the old implementation:

* ``f_valuesstack``
* ``f_stackdepth``
* ``f_gen``
* ``f_iblock``
* ``f_state``
* ``f_blockstack``
* ``f_localsplus``
* ``f_lineno``: use :c:func:`PyFrame_GetLineNumber`
* ``f_locals``: use ``PyObject_GetAttrString((PyObject*)frame, "f_locals")``.
* ``f_stackdepth``: removed.
* ``f_state``: no public API (renamed to ``f_frame.f_state``).
* ``f_trace``: no public API.
* ``f_trace_lines``: use ``PyObject_GetAttrString((PyObject*)frame, "f_trace_lines")``
(it also be modified).
* ``f_trace_opcodes``: use ``PyObject_GetAttrString((PyObject*)frame, "f_trace_opcodes")``
(it also be modified).
* ``f_localsplus``: no public API (renamed to ``f_frame.localsplus``).
* ``f_valuestack``: removed.

The Python frame object is now created lazily. A side effect is that the
``f_back`` member must not be accessed directly, since its value is now also
Expand All @@ -897,9 +897,9 @@ Porting to Python 3.11
}
#endif

Or use `the pythoncapi_compat project
<https://github.com/pythoncapi/pythoncapi_compat>`__ to get these APIs
on older Python versions.
Or use the `pythoncapi_compat project
<https://github.com/pythoncapi/pythoncapi_compat>`__ to get these two
functions on older Python versions.

* Changes of the :c:type:`PyThreadState` structure members:

Expand Down
13 changes: 0 additions & 13 deletions Include/cpython/frameobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,6 @@
# error "this header file must not be included directly"
#endif

struct _frame {
PyObject_HEAD
PyFrameObject *f_back; /* previous frame, or NULL */
struct _interpreter_frame *f_frame; /* points to the frame data */
PyObject *f_trace; /* Trace function */
int f_lineno; /* Current line number. Only valid if non-zero */
char f_trace_lines; /* Emit per-line trace events? */
char f_trace_opcodes; /* Emit per-opcode trace events? */
char f_owns_frame; /* This frame owns the frame */
/* The frame data, if this frame object owns the frame */
PyObject *_f_frame_data[1];
};

/* Standard object interface */

PyAPI_DATA(PyTypeObject) PyFrame_Type;
Expand Down
12 changes: 12 additions & 0 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ extern "C" {

#include <stdbool.h>

struct _frame {
PyObject_HEAD
PyFrameObject *f_back; /* previous frame, or NULL */
struct _interpreter_frame *f_frame; /* points to the frame data */
PyObject *f_trace; /* Trace function */
int f_lineno; /* Current line number. Only valid if non-zero */
char f_trace_lines; /* Emit per-line trace events? */
char f_trace_opcodes; /* Emit per-opcode trace events? */
char f_owns_frame; /* This frame owns the frame */
/* The frame data, if this frame object owns the frame */
PyObject *_f_frame_data[1];
};

/* runtime lifecycle */

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Move the :c:type:`PyFrameObject` type definition (``struct _frame``) to the
internal C API ``pycore_frame.h`` header file. Patch by Victor Stinner.

0 comments on commit ed1e6d6

Please sign in to comment.