Python/ceval.c (part 2)
cpython 3.14 @ ab2d84fe1023/Python/ceval.c
This annotation covers the tracing hooks, recursion limit, and the specialization dispatch
in Python/ceval.c. For the main eval loop dispatch table and opcode implementations
see the python_ceval_c annotation.
Map
| Lines | Symbol | Role |
|---|---|---|
| 1-500 | call_trace, call_trace_protected | Invoke sys.settrace / sys.setprofile hooks |
| 501-900 | Recursion limit check | _Py_EnterRecursiveCall, _Py_LeaveRecursiveCall |
| 901-1600 | _PyEval_EvalFrameDefault prologue | Frame setup, locals initialisation |
| 1601-6000 | Opcode implementations | (see python_ceval_c annotation for core opcodes) |
Reading
call_trace
call_trace is called at every line event when sys.settrace is active. It packages the
frame, event (call/line/return/exception), and argument into a tuple and calls the C-level
function pointer stored in tstate->c_tracefunc.
// CPython: Python/ceval.c:283 call_trace
static int
call_trace(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, _PyInterpreterFrame *frame,
int what, PyObject *arg)
{
...
int result = func(obj, &trace_frame, what, arg);
...
return result;
}
Recursion limit
// CPython: Python/ceval.c:760 _Py_EnterRecursiveCallTstate
int
_Py_EnterRecursiveCallTstate(PyThreadState *tstate, const char *where)
{
if (tstate->py_recursion_remaining-- <= 0) {
tstate->py_recursion_remaining = 0;
_PyErr_SetString(tstate, PyExc_RecursionError,
"maximum recursion depth exceeded" + where);
return -1;
}
return 0;
}
The default limit is 1000 (sys.setrecursionlimit). There are two separate counters:
py_recursion_remaining for Python frames and c_recursion_remaining for C calls, to
prevent C stack overflow from deeply recursive C extensions.
Specialization dispatch
In Python 3.12+ the eval loop uses a computed-goto dispatch table. Specialised opcode
variants (e.g. LOAD_ATTR_SLOT, BINARY_OP_ADD_INT) are contiguous entries in the same
table; the generic opcode falls through to the specialization trigger on the first few
calls.
gopy notes
vm/eval_simple.go handles the gopy equivalent of the eval loop. Trace hooks are not yet
implemented. The recursion limit is enforced by Go's goroutine stack depth rather than an
explicit counter. Specialization (adaptive interpreter) is not yet planned for gopy.