Skip to main content

Python/ceval.c (part 7)

Source:

cpython 3.14 @ ab2d84fe1023/Python/ceval.c

This annotation covers closure creation, string formatting, structural pattern matching, and async/generator opcodes. See parts 1-6 for the core dispatch loop, attribute access, calls, and container operations.

Map

LinesSymbolRole
1-200MAKE_FUNCTIONCreate a function object from code object + defaults
201-400BUILD_SLICEa:b:c slice literal
401-600FORMAT_VALUEf-string slot formatting with conversion (!r, !s, !a)
601-900COPY_FREE_VARSCopy enclosing scope cells into new frame
901-1200RESUMEFrame entry check (tracing, signals)
1201-1500SENDSend a value into a generator or coroutine
1501-1800YIELD_VALUESuspend generator, return value to caller
1801-2200MATCH_* opcodesStructural pattern matching (3.10+)
2201-2600BEFORE_ASYNC_WITHasync with entry
2601-3000GET_AITER, GET_ANEXTasync for protocol

Reading

MAKE_FUNCTION

// CPython: Python/ceval.c:2840 MAKE_FUNCTION
inst(MAKE_FUNCTION, (qualname, codeobj -- func)) {
PyObject *func = PyFunction_New(codeobj, GLOBALS());
PyFunction_SetQualname(func, qualname);
/* flags indicate which optional components follow on stack */
if (oparg & 0x08) { /* closure */
PyObject *closure = POP();
PyFunction_SetClosure(func, closure);
}
if (oparg & 0x04) { /* annotations */
PyObject *annotations = POP();
PyFunction_SetAnnotations(func, annotations);
}
if (oparg & 0x02) { /* kwdefaults */
PyObject *kwdefaults = POP();
PyFunction_SetKwDefaults(func, kwdefaults);
}
if (oparg & 0x01) { /* defaults */
PyObject *defaults = POP();
PyFunction_SetDefaults(func, defaults);
}
PUSH(func);
}

MAKE_FUNCTION pops the code object and qualified name, then pops optional components based on oparg flags.

COPY_FREE_VARS

// CPython: Python/ceval.c:940 COPY_FREE_VARS
inst(COPY_FREE_VARS, (--)) {
/* Copy free variables from enclosing frame into this frame's fast locals */
PyCodeObject *co = frame->f_code;
PyObject *closure = frame->f_func->func_closure;
int offset = co->co_nlocalsplus - co->co_nfreevars;
for (int i = 0; i < co->co_nfreevars; i++) {
PyObject *o = PyTuple_GET_ITEM(closure, i);
frame->localsplus[offset + i] = Py_NewRef(o);
}
}

Closures store free variable cell objects in func_closure. COPY_FREE_VARS copies those cells into the new frame's localsplus array.

RESUME

// CPython: Python/ceval.c:1180 RESUME
inst(RESUME, (--)) {
/* Check for pending signals, async exceptions, tracing */
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
if (_Py_HandlePending(tstate) != 0) goto error;
}
/* Also used as generator/coroutine re-entry point */
}

RESUME is emitted at function entry and at every yield/await resume point. It handles the "eval breaker" — signals, GIL drops, tracing hooks.

YIELD_VALUE

// CPython: Python/ceval.c:1560 YIELD_VALUE
inst(YIELD_VALUE, (retval --)) {
assert(oparg == _PyFrame_IsIncomplete(frame));
frame->instr_ptr = next_instr; /* save resume point */
tstate->exc_info = frame->previous_exc_info;
_PyFrame_SetStackPointer(frame, stack_pointer);
PyGenObject *gen = _PyFrame_GetGenerator(frame);
gen->gi_frame_state = FRAME_SUSPENDED;
_Py_LeaveRecursiveCallPy(tstate);
return retval; /* return to caller's eval loop */
}

The frame is left intact (suspended). next(gen) resumes by calling _PyEval_EvalFrameDefault with the same frame.

SEND

// CPython: Python/ceval.c:1320 SEND
inst(SEND, (receiver, v -- receiver, retval)) {
/* Send v into receiver (generator, coroutine, or async generator) */
PyObject *retval;
if (PyGen_CheckExact(receiver) || PyCoro_CheckExact(receiver)) {
retval = _PyGen_Send((PyGenObject *)receiver, v);
} else {
/* General: call receiver.send(v) */
retval = PyObject_CallMethodOneArg(receiver, &_Py_ID(send), v);
}
if (retval == NULL) {
/* StopIteration or StopAsyncIteration: extract value */
...
JUMPBY(oparg); /* jump past the YIELD_VALUE / RESUME pair */
}
}

FORMAT_VALUE (f-string)

// CPython: Python/ceval.c:380 FORMAT_VALUE
inst(FORMAT_VALUE, (value[1], fmt_spec[oparg & FVS_MASK] -- result)) {
/* Apply conversion: !r, !s, or !a */
int which_conversion = oparg & FVC_MASK;
if (which_conversion == FVC_REPR)
value = PyObject_Repr(value);
else if (which_conversion == FVC_STR)
value = PyObject_Str(value);
else if (which_conversion == FVC_ASCII)
value = PyObject_ASCII(value);
/* Apply format spec */
result = (fmt_spec == NULL)
? PyObject_Format(value, _empty_string)
: PyObject_Format(value, fmt_spec);
}

MATCH_MAPPING / MATCH_SEQUENCE

// CPython: Python/ceval.c:2000 MATCH_MAPPING
inst(MATCH_MAPPING, (subject -- subject, res)) {
int match = PyType_HasFeature(Py_TYPE(subject), Py_TPFLAGS_MAPPING);
res = match ? Py_True : Py_False;
Py_INCREF(res);
}

// CPython: Python/ceval.c:2050 MATCH_SEQUENCE
inst(MATCH_SEQUENCE, (subject -- subject, res)) {
int match = PyType_HasFeature(Py_TYPE(subject), Py_TPFLAGS_SEQUENCE);
res = match ? Py_True : Py_False;
Py_INCREF(res);
}

gopy notes

MAKE_FUNCTION is in vm/eval_gen.go, calling objects.NewFunction and setting closure/defaults. COPY_FREE_VARS copies cell objects into the frame's locals slice. RESUME checks vm.evalBreaker. YIELD_VALUE suspends the generator frame. FORMAT_VALUE calls objects.Repr/Str/ASCII then objects.Format. Pattern matching opcodes use objects.Type flags.