Skip to content

Reference overview

Concepts

Grammar: productions of fields. Each field has a shape, a sort and a role.

Shape: ONE, OPT, SEQ, or UNKNOWN when the source did not report it (ast._field_types supplies shapes from Python 3.13 on; earlier interpreters expose only names).

Sort: what a slot holds (another production, an identifier, or plain data).

Role: what the position does with what it holds. See Roles.

Namespace: which family of names a role is about. def(vals) and def(slots) keep a pass over SSA values away from a mutable slot, even though both fields hold the same type.

Condition: Present(field) or Absent(field), and nothing else. A field or a scope may carry one, so a role can depend on a sibling: import a.b binds a while import a.b as c binds c alone. Deliberately not a callable.

Trait: a free-form label on a production, for facts no field determines (pure, terminator).

The queries at a glance

question call
which fields bind a name? positions(Kind.DEF, ns)
which read one? operands(ns)
which introduce one? definitions(ns)
which hold a bare identifier? ident_slots(ns)
which does a traversal descend into? children(prod)
which can actually appear? concrete(base)
which carry a trait? with_trait(name)
what does this node read? reads(node, ns)
what does this node bind? binds(node, ns)
which class builds this production? constructor(name)
is the declaration well formed? check()

Static versus instance

positions, operands, definitions and ident_slots span every node of a production, so a conditionally-roled field is reported under both its roles. reads and binds take one node and apply the conditions.

A pass holding a node wants the second. Writing that walk inline is the sign a query is missing, which is how reads and binds came to exist.