Skip to main content

Objects/codeobject.c (part 5)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/codeobject.c

This annotation covers code object metadata encoding. See objects_codeobject4_detail for PyCodeObject fields, code.__new__, and co_qualname.

Map

LinesSymbolRole
1-80co_linetable encodingMap instruction offset to source line
81-180co_exceptiontable encodingMap instruction range to exception handler
181-280code.__eq__ / __hash__Code object equality
281-360code.replaceCreate a modified copy of a code object
361-500_PyCode_GetVarnamesExtract variable name sets

Reading

co_linetable encoding

// CPython: Objects/codeobject.c:380 scan_varint
/* co_linetable is a variable-length encoding:
Each entry encodes:
- number of bytecode words covered (1-8 words)
- line number delta (0 = same line, 1-12 = delta, 13 = next entry is absolute)
- column offset and end column for precise source locations (Python 3.11+)

Format: [start_line_delta | (code_unit_count - 1) << 3] [col_delta] ...
*/

The linetable is a compact byte sequence, not a simple array. Python 3.11 extended it to include column offsets, enabling precise error caret display (^^^^^). dis.findlinestarts() decodes it.

co_exceptiontable encoding

// CPython: Objects/codeobject.c:480 _PyCode_InitAddressRange
void
_PyCode_InitAddressRange(PyCodeObject *co, PyCodeAddressRange *bounds)
{
/* Initialize a cursor for scanning the exception table.
Each entry: [start_offset, end_offset, handler_offset, depth, lasti_flag]
All values are delta-encoded relative to the previous entry.
*/
bounds->opaque.lo_next = (const char *)PyBytes_AS_STRING(co->co_exceptiontable);
bounds->opaque.limit = bounds->opaque.lo_next +
PyBytes_GET_SIZE(co->co_exceptiontable);
}

The exception table replaces the old SETUP_FINALLY opcode. At runtime, when an exception occurs, the table is binary-searched to find the handler. This makes the normal (no-exception) path faster.

code.replace

// CPython: Objects/codeobject.c:680 code_replace_impl
static PyObject *
code_replace_impl(PyCodeObject *self, int co_argcount, ...)
{
/* Create a new code object identical to self but with some fields changed.
Used by functools.wraps-like utilities and by the test suite. */
return _PyCode_New(
co_argcount != -1 ? co_argcount : self->co_argcount,
...
self->co_code_adaptive, /* bytecode cannot be replaced */
...
);
}

code.replace(co_name='new_name') creates a modified copy. The bytecode itself (co_code) cannot be replaced via code.replace (it would invalidate the inline caches).

gopy notes

co_linetable is stored as objects.CodeObject.LineTable []byte in objects/code.go. The decoder is objects.LineTableIterator. co_exceptiontable is objects.CodeObject.ExceptionTable []byte, decoded by objects.ExceptionTableLookup. code.replace is objects.CodeReplace.