Skip to main content

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

LinesSymbolRole
1-500call_trace, call_trace_protectedInvoke sys.settrace / sys.setprofile hooks
501-900Recursion limit check_Py_EnterRecursiveCall, _Py_LeaveRecursiveCall
901-1600_PyEval_EvalFrameDefault prologueFrame setup, locals initialisation
1601-6000Opcode 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.