astero.python¶
Python's grammar, its scope tables, and its namespaces. The one language that
ships with the library; everything else in astero is language-agnostic.
astero.python.lang ¶
Python's grammar, declared: CPython's node table plus the roles it omits.
Not a parser, and not a replacement for ast. ast already answers what
fields a production has and what each holds: Assign.targets is list[expr]
and Assign.value is expr. What it cannot answer is that the first
introduces a name and the second does not. To ast the two are both expr,
and the difference between them exists only in the language reference and in
the head of whoever writes the pass.
Supplying that difference is the whole content of this module. Of Python's 176
fields across 124 productions, 138 are child and have nothing to say; the
other 38 carry a role, and 22 positions introduce a variable name — from the
obvious Assign.targets to MatchMapping.rest, comprehension.target and
withitem.optional_vars. That list is what every Python tool re-derives by
hand, and what it misses one of.
The rest is machinery to avoid restating what CPython already records: sorts
and shapes come from the running interpreter, roles are authored here. The
stdlib cannot be annotated in place, so the role table is the one piece written
by hand, and tests/a_unit/test_lang_py.py gates it against CPython's own
parser.
Sorts and shapes are free from ast._field_types on 3.13 and later. On 3.11
and 3.12 only field names are exposed, so Field.sort is None there and the
identifier sorts are supplied by the same role table that names the positions.
That costs nothing here, because every derivation this module feeds keys off
roles rather than sorts.
defers_annotations ¶
Whether tree evaluates its annotations lazily, per PEP 649.
False before Python 3.14, and false for a module carrying from __future__
import annotations: PEP 563 makes every annotation a string, so nothing
is deferred and CPython compiles no __annotate__ at all.
annotation_blocks ¶
annotation_blocks(tree: AST) -> dict[str, Scope]
ANNOTATION_BLOCKS if they are in force for tree, otherwise none.
Pass the result as scope_tree's siblings. Whether the blocks exist is
a property of the whole module rather than of any node, which is why this
is a function of the tree and not an entry in a table.
mangle ¶
Python's private name mangling, as the compiler applies it.
An identifier written __x inside class C is compiled as _C__x. A
renaming pass that skips this renames a name the interpreter never sees.
build ¶
build(module: ModuleType = ast, name: str = '') -> Grammar
Python's grammar as this interpreter reports it, plus the roles above.
module is ast by default. A compiler that generates its own node
classes passes its module instead, and authors nothing: the roles are facts
about Python, and a generated class hierarchy holds the same fields under
the same names. What differs is which productions exist, so a host missing
one gets a grammar missing it too, and building that node fails at the
rewrite rather than downstream.