Skip to main content

Python/ceval.c (part 25)

Source:

cpython 3.14 @ ab2d84fe1023/Python/ceval.c

This annotation covers unpacking and f-string opcodes. See python_ceval24_detail for collection construction opcodes.

Map

LinesSymbolRole
1-80UNPACK_SEQUENCE_TWO_TUPLEUnpack exactly 2-element tuple fast path
81-160UNPACK_SEQUENCE_TUPLEUnpack a tuple of known length
161-260UNPACK_EXa, *b, c = iterable — left/right counts
261-360FORMAT_VALUEf'{x!r:.2f}' — apply conversion and format spec
361-400BUILD_STRINGConcatenate string parts for f-string result

Reading

UNPACK_SEQUENCE_TWO_TUPLE

// CPython: Python/ceval.c:4620 UNPACK_SEQUENCE_TWO_TUPLE
inst(UNPACK_SEQUENCE_TWO_TUPLE, (seq -- val1, val0)) {
DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE);
val0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0));
val1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1));
DECREF_SPECIALIZED(seq, _PyTuple_MaybeUntrack);
}

a, b = some_two_tuple avoids the generic unpacking loop. This specialization is critical for functions returning (result, error) pairs and for enumerate unpacking.

UNPACK_EX

// CPython: Python/ceval.c:4720 UNPACK_EX
inst(UNPACK_EX, (seq -- lefts[oparg & 0xFF], seq2, rights[oparg >> 8])) {
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
PyObject **top = stack_pointer + totalargs - 1;
/* Unpack left elements (before *), collect middle into list, unpack right */
int res = _PySequence_IterSearch(seq, (oparg & 0xFF), (oparg >> 8), top);
ERROR_IF(res < 0, error);
}

a, b, *c, d = iterable encodes left count (2) and right count (1) in oparg. The star target c collects everything in the middle as a list. The right elements are consumed from the end of the sequence.

FORMAT_VALUE

// CPython: Python/ceval.c:4820 FORMAT_VALUE
inst(FORMAT_VALUE, (value -- result)) {
/* oparg: conversion (none/!s/!r/!a) and format_spec flag */
int which_conversion = oparg & FVC_MASK;
int have_fmt_spec = (oparg & FVS_MASK) == FVS_HAVE_SPEC;
PyObject *fmt_spec = have_fmt_spec ? POP() : NULL;
/* Apply conversion */
if (which_conversion == FVC_STR) value = PyObject_Str(value);
else if (which_conversion == FVC_REPR) value = PyObject_Repr(value);
else if (which_conversion == FVC_ASCII) value = PyObject_ASCII(value);
/* Apply format spec */
result = PyObject_Format(value, fmt_spec);
}

f'{x!r:.3f}' pushes the format spec '.3f', then FORMAT_VALUE with FVC_REPR | FVS_HAVE_SPEC. PyObject_Format calls x.__format__(fmt_spec).

BUILD_STRING

// CPython: Python/ceval.c:4900 BUILD_STRING
inst(BUILD_STRING, (pieces[oparg] -- str)) {
str = _PyUnicode_JoinArray(&_Py_STR(empty), pieces, oparg);
ERROR_IF(str == NULL, error);
}

f'hello {name}!' compiles to LOAD_CONST 'hello ', FORMAT_VALUE name, LOAD_CONST '!', BUILD_STRING 3. _PyUnicode_JoinArray concatenates the pieces efficiently using one allocation.

gopy notes

UNPACK_SEQUENCE_TWO_TUPLE is vm.UnpackSequenceTwoTuple in vm/eval_specialize.go. UNPACK_EX is vm.UnpackEx using objects.IterSearch. FORMAT_VALUE calls objects.ObjectStr/Repr/ASCII then objects.ObjectFormat. BUILD_STRING uses strings.Join on the pieces.