Skip to main content

Include/internal/pycore_tstate.h

Source:

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

PyThreadState (tstate) is the per-thread state object. Every thread has exactly one. The eval loop reads from it on every opcode: current frame, exception, recursion depth, and trace hooks.

Map

LinesSymbolRole
1-80Identity and interpreter linkthread_id, interp, next/prev
81-160Execution statecurrent_frame, recursion_depth, recursion_limit
161-220Exception stateexc_info, current_exception
221-300Tracingc_tracefunc, c_profilefunc, tracing, use_tracing

Reading

Identity fields

// CPython: Include/internal/pycore_tstate.h:24 PyThreadState (abridged)
struct _ts {
PyThreadState *prev, *next;
PyInterpreterState *interp;

/* Execution */
struct _PyInterpreterFrame *current_frame;
int recursion_depth;
int recursion_headroom;
int stackcheck_depth;
int recursion_limit;

/* Exceptions */
_PyErr_StackItem exc_state; /* innermost except block's exception */
_PyErr_StackItem *exc_info; /* pointer to innermost exc_state */
PyObject *current_exception; /* currently set exception value */

/* Tracing */
Py_tracefunc c_tracefunc;
Py_tracefunc c_profilefunc;
PyObject *c_traceobj;
PyObject *c_profileobj;
int tracing;
int tracing_what;
int use_tracing; /* fast flag: is any tracing active? */

/* Thread identity */
unsigned long thread_id;
uint64_t native_thread_id;
};

Recursion limit

// CPython: Include/internal/pycore_tstate.h:140 recursion check
static inline int
_Py_EnterRecursiveCallTstate(PyThreadState *tstate, const char *where)
{
return (tstate->recursion_depth++ >= tstate->recursion_limit)
? _Py_CheckRecursiveCall(tstate, where) : 0;
}

recursion_limit defaults to 1000. _Py_CheckRecursiveCall raises RecursionError when exceeded.

Exception info stack

// CPython: Include/internal/pycore_tstate.h:165 exc_info chain
/*
* exc_info points to the innermost _PyErr_StackItem.
* Each except block pushes a new item via PUSH_EXC_INFO;
* POP_EXCEPT restores the previous pointer.
*/

The exception stack supports re-entrant exception handling: entering a nested except block saves the current exception and sets the new one.

Tracing

// CPython: Include/internal/pycore_tstate.h:220 c_tracefunc
/*
* c_tracefunc is called by the eval loop for:
* - PyTrace_CALL (function entry)
* - PyTrace_LINE (each source line)
* - PyTrace_RETURN (function exit)
* - PyTrace_EXCEPTION (unhandled exception at a line)
*/

use_tracing is a combined flag: it is 1 if either c_tracefunc or c_profilefunc is set. The eval loop checks use_tracing (not the individual pointers) for speed.

gopy notes

vm.Frame in gopy holds RecursionDepth and checks against the interpreter's RecursionLimit. The exception state (exc_info) is a linked list of ExcInfo structs on vm.Frame. Tracing (c_tracefunc) is set by sys.settrace and drives pdb. gopy's trace hook is a Go interface field on vm.Frame.