Skip to main content

Include/internal/pycore_interp.h

cpython 3.14 @ ab2d84fe1023/Include/internal/pycore_interp.h

In CPython 3.14 the PyInterpreterState struct definition was moved out of pycore_interp.h and into the generated pycore_interp_structs.h. This file is now a thin umbrella that includes the structs header plus pycore_interp_structs.h, then declares the handful of functions that operate on the interpreter before the runtime is fully initialized.

The file you want to read for the full struct layout is Include/internal/pycore_interp_structs.h (994 lines). The present file documents only what is left here after the refactor: the init/teardown declarations and a few helper macros.

PyInterpreterState is the unit of sub-interpreter isolation. Each sub-interpreter has its own module table, its own __builtins__, its own GC generations, and its own thread-state list. The _PyRuntime singleton holds a singly-linked list of all live interpreters via the next field.

Map

LinesSymbolRolegopy
1-30includes + guardPulls in pycore_interp_structs.h, pycore_gc.h, pycore_context.h.n/a
31-50_PyInterpreterState_Init / _PyInterpreterState_Clear / _PyInterpreterState_DeleteLifecycle: allocate, clear, free an interpreter.pythonrun/interp.go
51-65_PyInterpreterState_IDInitref / _PyInterpreterState_IDIncref / _PyInterpreterState_IDDecrefReference-count the interpreter ID used by _interpreters.get_current().pythonrun/interp.go
66-80_PyInterpreterState_Get / _PyInterpreterState_GETFast accessor: current interpreter from the calling thread state.pythonrun/interp.go
81-95_PyInterpreterState_GetMainModule / _PyInterpreterState_SetRunningMainModule and main-thread bookkeeping helpers.pythonrun/interp.go
96-109_PyInterpreterState_HasFeatureTests bits in interp->features (e.g. Py_TPFLAGS_BASETYPE isolation).pythonrun/interp.go

Reading

PyInterpreterState fields (in pycore_interp_structs.h)

The struct is defined in Include/internal/pycore_interp_structs.h. The most important fields for gopy are:

struct _is {
PyInterpreterState *next; // linked list via _PyRuntime.interpreters
struct pyruntimestate *runtime; // back-pointer to _PyRuntimeState

int64_t id; // unique interpreter ID
int64_t id_refcount; // refcount from _interpreters module

PyObject *modules; // sys.modules (a dict)
PyObject *modules_by_index; // list indexed by def->m_base.m_index
PyObject *builtins; // __builtins__ dict
PyObject *sysdict; // sys module dict
PyObject *importlib; // importlib._bootstrap module

struct _gc_runtime_state gc; // embedded GC state for this interpreter

PyObject *warnings_filters; // warnings.filters list
int warnings_filters_version;

PyThreadState *tstate_head; // head of the per-thread list
int _initialized;
};

modules_by_index is a list where modules_by_index[def->m_base.m_index] gives back the module object for a given PyModuleDef. The index is assigned once per PyModuleDef across all interpreters; when a new sub-interpreter imports the module, the list is extended to accommodate the index. This is the mechanism that lets multi-phase init modules (PEP 451) store per-interpreter state without a hash lookup.

GC state embedding (struct _gc_runtime_state)

Each interpreter carries a full struct _gc_runtime_state inline (not a pointer). The GC generations (gen0, gen1, gen2) and the garbage list are per-interpreter so that sub-interpreters can collect independently. The collector is triggered from _PyEval_EvalFrameDefault via _Py_HandlePending which checks interp->gc.collecting and interp->gc.enabled.

In gopy there is no GC cycle collector (Go's runtime handles cycles), so struct _gc_runtime_state is represented as an empty stub in pythonrun/interp.go and the track/untrack calls are no-ops.

_PyInterpreterState_GET (lines 66 to 80)

cpython 3.14 @ ab2d84fe1023/Include/internal/pycore_interp.h#L66-80

/* Macro for speed-critical paths — no function call overhead. */
#define _PyInterpreterState_GET() \
(_PyThreadState_GET()->interp)

_PyInterpreterState_GET() (uppercase) is an inlined macro used in hot paths like LOAD_GLOBAL. The lowercase _PyInterpreterState_Get() is a proper function that validates that a thread state exists before dereferencing it. Both map to vm.CurrentInterp() in gopy's eval loop.