Skip to main content

Include/cpython/eval.h: Frame Evaluation and Code Execution

eval.h (cpython variant) declares the handful of functions that sit at the boundary between the compiler output and the interpreter main loop. It is one of the smallest headers in CPython but one of the most performance-critical: every Python call ultimately routes through one of these entry points.

Map

LinesSymbolKindNotes
1-8guardmacro#ifndef Py_CPYTHON_EVAL_H
9-15Py_eval_inputconst intCompile mode: expression (used by eval())
16-18Py_file_inputconst intCompile mode: module/file (used by exec())
19-21Py_single_inputconst intCompile mode: interactive single statement
22-35_PyEval_EvalFrameDefaultfunction declThe main interpreter loop entry point
36-45PyEval_EvalCodefunction declExecute a code object in a given frame/globals
46-52PyEval_EvalCodeExfunction declExtended form with explicit args, kw, globals, locals
53-57PyEval_CallObjectfunction declDeprecated alias for PyObject_Call
58-60_Py_MakeRecursionErrorfunction declRaise RecursionError when the call stack is full

Reading

_PyEval_EvalFrameDefault

PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(
PyThreadState *tstate,
struct _PyInterpreterFrame *frame,
int throwflag
);

This is the real interpreter loop, defined in Python/ceval.c. It is not part of the stable ABI. The throwflag parameter is non-zero when the frame was resumed by generator.throw(), which causes the loop to raise the pending exception immediately rather than executing the next opcode. frame->prev_instr points to the last executed instruction on entry; the loop advances it at the top of each iteration.

PyEval_EvalCode and PyEval_EvalCodeEx

PyAPI_FUNC(PyObject *) PyEval_EvalCode(
PyObject *co,
PyObject *globals,
PyObject *locals
);

PyAPI_FUNC(PyObject *) PyEval_EvalCodeEx(
PyObject *co,
PyObject *globals,
PyObject *locals,
PyObject **args, int argcount,
PyObject **kws, int kwcount,
PyObject **defs, int defcount,
PyObject *kwdefs,
PyObject *closure
);

EvalCode is the public API used by exec() and compile() consumers. It builds a new _PyInterpreterFrame on the C stack (or the frame freelist) and calls _PyEval_EvalFrameDefault. EvalCodeEx is the older extended form that accepts pre-split argument arrays; it is still used internally by function_call machinery for keyword-heavy calls.

Compile-mode constants and _Py_MakeRecursionError

#define Py_eval_input 258
#define Py_file_input 257
#define Py_single_input 256

PyAPI_FUNC(PyObject *) _Py_MakeRecursionError(void);

The three input-mode constants are passed to PyCompile_OpcodeStackEffect and Py_CompileString. Their numeric values correspond to token grammar non-terminals in the PEG grammar. _Py_MakeRecursionError constructs (but does not raise) a RecursionError instance with a pre-formatted message; callers set it with PyErr_SetRaisedException and return NULL.

gopy notes

  • _PyEval_EvalFrameDefault corresponds to the evalFrame function in vm/eval_gen.go. The throwflag behaviour is handled by the throw parameter in vm/eval_unwind.go when resuming generators.
  • PyEval_EvalCode maps to pythonrun.RunCode in pythonrun/runstring.go, which wraps the Go eval entry point with globals/locals setup.
  • The three compile-mode constants are defined in compile/compiler.go as ModeEval, ModeExec, and ModeSingle.
  • _Py_MakeRecursionError is approximated by the recursion-depth check in vm/eval_call.go; gopy uses a configurable depth limit rather than the CPython C_RECURSION_LIMIT constant.
  • PyEval_CallObject is not ported; all call sites in gopy go through the objects/protocol.go call protocol helpers directly.