Include/cpython/frameobject.h
Source:
cpython 3.14 @ ab2d84fe1023/Include/cpython/frameobject.h
In CPython 3.11 the frame representation was split: _PyInterpreterFrame is the lightweight internal frame (stack-allocated in the ceval loop); PyFrameObject is the heap-allocated Python-visible wrapper created on demand.
Map
| Lines | Symbol | Role |
|---|---|---|
| 1-50 | PyFrameObject | Python-visible frame object (sys._getframe() returns this) |
| 51-120 | _PyInterpreterFrame | Internal frame used by ceval |
| 121-150 | Accessor macros | PyFrame_GetCode, PyFrame_GetLineNumber, PyFrame_GetBack |
Reading
_PyInterpreterFrame (internal, 3.11+)
// CPython: Include/cpython/frameobject.h:55 _PyInterpreterFrame
typedef struct _PyInterpreterFrame {
PyObject *f_executable; /* code object or callable */
struct _PyInterpreterFrame *previous;
PyObject *f_funcobj; /* function being executed */
PyObject *f_globals;
PyObject *f_builtins;
PyObject *f_locals; /* NULL until materialized */
PyFrameObject *frame_obj; /* heap object, NULL until needed */
_Py_CODEUNIT *instr_ptr; /* current instruction pointer */
int stackdepth;
int nlocalsplus;
char owner; /* FRAME_OWNED_BY_* */
PyObject *localsplus[1]; /* fast locals array (variable size) */
} _PyInterpreterFrame;
localsplus holds locals, cells, and free variables in one contiguous array. The array is indexed by co_localsplusnames.
PyFrameObject (Python-visible)
// CPython: Include/cpython/frameobject.h:18 PyFrameObject
struct PyFrameObject {
PyObject_HEAD
PyFrameObject *f_back;
struct _PyInterpreterFrame *f_frame; /* points to the internal frame */
PyObject *f_trace;
int f_lineno; /* only valid when frame is not executing */
char f_trace_lines;
char f_trace_opcodes;
char f_fast_as_locals;
};
PyFrameObject is a thin shell around _PyInterpreterFrame. The f_locals dict is built lazily by PyFrame_FastToLocalsWithError when Python code accesses frame.f_locals.
FRAME_OWNED_BY_* constants
// CPython: Include/cpython/frameobject.h:92 ownership
#define FRAME_OWNED_BY_THREAD 0
#define FRAME_OWNED_BY_GENERATOR 1
#define FRAME_OWNED_BY_FRAME_OBJECT 2
#define FRAME_OWNED_BY_CSTACK 3
When owner == FRAME_OWNED_BY_GENERATOR, the frame is suspended in a generator object and lives on the heap. Other frames are normally stack-allocated.
gopy notes
vm.Frame in gopy merges _PyInterpreterFrame and PyFrameObject into a single Go struct. f_locals materialization (FastToLocals/LocalsToFast) is done in vm/eval_call.go. The f_back chain is navigated by sys._getframe and traceback construction.