Skip to main content

Modules/_symtable.c

cpython 3.14 @ ab2d84fe1023/Modules/_symtable.c

_symtable.c is the thin C layer that makes CPython's compiler symbol table visible to Python code. It defines a single extension type, PySTEntry, and a module-level function symtable that drives the full scope analysis pass over a parsed AST. The higher-level Lib/symtable.py wraps these primitives into a friendlier public API.

Each PySTEntry object corresponds to one lexical scope: a module, class body, function, or comprehension. The entry carries the scope's name, a dictionary mapping identifier strings to flag integers, and a list of child entries for nested scopes. Consumers walk the tree to answer questions like "is x a free variable here?" or "does this function reference __class__?".

The file is intentionally small. The heavy lifting lives in Python/symtable.c, which builds the struct symtable and populates PySTEntry nodes. This module just wraps those structs with Python getset descriptors so that symtable.py can inspect them without calling into C directly.

Map

LinesSymbolRolegopy
1-40module init, includesBoilerplate and PySTEntry_Type forward declaration
41-90symtableentry_newAllocates a PySTEntry wrapper around an internal PySTEntryObject
91-130ste_get_typeGetset: returns the integer scope type (function, class, module)
131-170ste_get_id, ste_get_nameGetsets: id is the PyObject identity; name is the string scope name
171-220ste_get_symbolsGetset: returns a copy of the symbols dict (name -> flag int)
221-260ste_get_childrenGetset: returns list of nested PySTEntry objects
261-280PyInit__symtableModule init: registers PySTEntry type and symtable() function

Reading

Module entry point and type registration (lines 1 to 40)

cpython 3.14 @ ab2d84fe1023/Modules/_symtable.c#L1-40

The file opens with standard CPython module boilerplate. PySTEntry_Type is declared but defined later. The symtable module method table holds a single entry, symtable(), which takes a source string and a compile_mode flag and returns the root PySTEntry for the parsed code.

static PyMethodDef symtable_methods[] = {
{"symtable", symtable_symtable, METH_VARARGS, symtable_doc},
{NULL, NULL}
};

symtableentry_new (lines 41 to 90)

cpython 3.14 @ ab2d84fe1023/Modules/_symtable.c#L41-90

This function is the only constructor for PySTEntry objects. It receives a pointer to an internal PySTEntryObject, increments its reference count, and stores it in the wrapper. The wrapper does not copy any data: all reads go through the live internal struct, so the entry is only valid while the enclosing struct symtable is alive.

static PyObject *
symtableentry_new(PyTypeObject *type, PySTEntryObject *ste)
{
PySTEntryObject *self = (PySTEntryObject *)type->tp_alloc(type, 0);
if (self == NULL) return NULL;
Py_INCREF(ste);
self->ste_table = ste->ste_table;
/* ... copy remaining fields ... */
return (PyObject *)self;
}

Getset descriptors (lines 91 to 260)

cpython 3.14 @ ab2d84fe1023/Modules/_symtable.c#L91-260

Five read-only getset descriptors expose the fields that symtable.py needs. ste_get_symbols returns a fresh PyDict copy so callers cannot mutate the compiler's internal table. ste_get_children builds a PyList of PySTEntry wrappers on each access, which keeps the C struct unexposed while still allowing recursive traversal.

static PyObject *
ste_get_symbols(PySTEntryObject *self, void *Py_UNUSED(ignored))
{
return PyDict_Copy(self->ste_symbols);
}

Module initialization (lines 261 to 280)

cpython 3.14 @ ab2d84fe1023/Modules/_symtable.c#L261-280

PyInit__symtable follows the standard PyModuleDef pattern. It registers PySTEntry_Type under the name symtable_entry, adds the integer flag constants (SCOPE_LOCAL, SCOPE_FREE, etc.) to the module namespace, and returns the module object. The flag constants mirror those defined in Include/symtable.h so Python-level code can compare them symbolically.

gopy mirror

Not yet ported.