Skip to main content

Include/cpython/pystate.h: Thread and Interpreter State

pystate.h is the canonical layout file for per-thread and per-interpreter runtime state. Almost every subsystem touches these structs. The 3.14 cycle reorganised several fields that were previously in Include/cpython/pystate.h into the internal header Include/internal/pycore_tstate.h, so the public header is now a thinner view.

Map

LinesSymbolKindNotes
1-30guards, includesmacroStandard #ifndef guard plus pyframe.h include
31-55Py_tracefunctypedefCallback type for sys.settrace and sys.setprofile
56-90_PyErr_StackItemstructSingly-linked list node holding one active exception
91-160PyThreadStatestructFull per-thread state (frame pointer, exc chain, GIL counter)
161-200PyInterpreterStatestructMinimal public view; full layout lives in pycore_interp.h
201-230thread-state accessorsinline/macroPyThreadState_GET, _PyThreadState_GET
231-260GIL helpersfunction declPyEval_SaveThread, PyEval_RestoreThread
261-300misc flagsmacroPy_TPFLAGS_* overflow, gilstate_counter semantics

Reading

PyThreadState core fields

struct _ts {
struct _ts *prev;
struct _ts *next;
PyInterpreterState *interp;

struct _PyInterpreterFrame *current_frame;

_PyErr_StackItem exc_info; /* current exception */

int gilstate_counter;
/* ... many internal fields follow in pycore_tstate.h ... */
};

current_frame replaced the older frame field in 3.11 when the frame representation switched to _PyInterpreterFrame. In 3.14 the remaining c_tracefunc / c_profilefunc fields moved to the internal header. gilstate_counter tracks the nesting depth for PyGILState_Ensure calls on this thread.

_PyErr_StackItem exception chain

typedef struct _PyErr_StackItem {
PyObject *exc_value;
struct _PyErr_StackItem *previous_item;
} _PyErr_StackItem;

Each try block pushes one node onto the thread's exception stack via _PyErr_StackPush. The previous_item pointer links back to the enclosing handler. exc_value holds the live exception object (or NULL when none is active). This replaced the old triple (type, value, traceback) in 3.11.

Py_tracefunc typedef

typedef int (*Py_tracefunc)(
PyObject *obj, /* trace object passed to sys.settrace */
PyFrameObject *frame,
int what, /* PyTrace_CALL, PyTrace_LINE, etc. */
PyObject *arg
);

The what constants (PyTrace_CALL, PyTrace_RETURN, PyTrace_LINE, PyTrace_EXCEPTION, PyTrace_OPCODE) are defined in Include/cpython/pystate.h alongside this typedef. In gopy these callbacks are surfaced through the TraceFunc interface in vm/.

gopy notes

  • PyThreadState maps to vm.ThreadState (or the EvalContext struct in vm/eval_gen.go). The exc_info chain is replicated as ExcInfo in objects/ using a Go linked-list node.
  • gilstate_counter has no direct equivalent because gopy does not implement the GIL. The field is tracked as a no-op counter for C-extension compatibility stubs.
  • The 3.14 split between the public header and pycore_tstate.h is useful for gopy: only public fields need stable Go counterparts; internal fields can be kept unexported.