Skip to main content

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

LinesSymbolRole
1-50PyFrameObjectPython-visible frame object (sys._getframe() returns this)
51-120_PyInterpreterFrameInternal frame used by ceval
121-150Accessor macrosPyFrame_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.