Astichi reference guide (snippets)¶
Condensed map: each topic is a directory under snippets/. Authored sources are *.py (optional # astichi-snippet: {…} metadata), builder bundles (snippet.json + parts), or recipe.py (multi-stage / non-JSON wiring). Materialized output is *_generated.py in the same directory as the authored file: flat foo.py → foo_generated.py; bundle directory bar/ → bar/bar_generated.py. Do not edit by hand.
Regenerate: from the astichi/ tree, run
uv run python scripts/generate_reference_snippet_outputs.py
API: astichi.compile → optional .bind / .bind_identifier → .describe() / .materialize(); builder bundles use astichi.build(), add, target, assign, bind_identifier, build(unroll=…).
| Topic | Forms covered | Authored | Materialized (sibling *_generated.py) |
|---|---|---|---|
| bind_external | tuple binding; mapping-style bind kwargs |
tuple_binding.py, mapping_binding.py | tuple_binding_generated.py, mapping_binding_generated.py |
| ref | string literal path; external= + bind; f-string + bound prefix; subscript into bound tuple; LHS/delete sentinel astichi_v / _ |
ref/ | e.g. literal_generated.py; same suffix pattern for other ref/*.py |
| unroll | astichi_for + ref in loop body (unroll=True) |
unroll/for_literal_ref/ | for_literal_ref_generated.py |
| keep | call-form astichi_keep |
call_keep.py | call_keep_generated.py |
| keep_identifier | __astichi_keep__ class/def suffix |
class_suffix.py | class_suffix_generated.py |
| arg | __astichi_arg__ resolved via bind_identifier |
resolved.py | resolved_generated.py |
| export | astichi_export (supply port; marker stripped) |
export_line.py | export_line_generated.py |
| import | astichi_import; explicit builder / arg_names threading; same-name immediate outer bind via outer_bind=True |
import/accumulator_step/ | accumulator_step_generated.py |
| pass | value-form only: walrus RHS (x := astichi_pass(y)), direct value / call / attribute use, LHS/delete sentinel astichi_v / _, and same-name immediate outer bind via outer_bind=True; bare statement-form astichi_pass(...) rejects |
pass/if_walrus_pass/ | if_walrus_pass_generated.py |
| funcargs | * hole: ordered positional payloads; ** hole: keyword payloads + edge order; call hole + fixed keywords: func(hole, fixed=…) with payload first, named=…, **extra; walrus + boundary markers: (out := seed), _=astichi_import(seed), _=astichi_export(out) with assign wiring; same with compile(..., arg_names=…) on the fragment instead of assign; astichi_pass as the walrus RHS; astichi_bind_external in the carrier + .bind() before materialize; parameterized keyword name + value identifier via astichi_funcargs(param__astichi_arg__=astichi_pass(value__astichi_arg__, outer_bind=True)) reused with per-instance arg_names={...} overlays |
funcargs/ (ordered_variadic/, named_variadic_kwargs/, plain_fixed_keywords_star_extra/, walrus_import_export_assign/, payload_compile_arg_names/, pass_walrus_export/, bind_external_arg/) |
e.g. ordered_variadic_generated.py; each subfolder has a matching *_generated.py |
| params | function signature holes via name__astichi_param_hole__; payload carrier def astichi_params(...): pass; defaults/annotations; variadic parameters; body snippets binding to inserted parameter names |
params/function_signature/ | function_signature_generated.py |
| comment | statement-only astichi_comment("..."); emit_commented() output with same-indentation multi-line comments, {__file__} / {__line__} expansion, and pass for comment-only suites |
comment/generated_comment/ | generated_comment_generated.py |
| expr | scalar expression hole + single insert; tuple/list starred * expansion with ordered expression entries; dict initializer ** expansion with dict payloads; scalar dict-key replacement |
expr/scalar_hole/, expr/tuple_list_expansion/, expr/dict_expansion/ | scalar_hole_generated.py, tuple_list_expansion_generated.py, dict_expansion_generated.py |
| statement | block hole + builder-generated insert synthesis; defaulted block hole fallback selected when unfilled; defaulted block hole replaced when filled | statement/block_hole/, statement/defaulted_block_hole_empty/, statement/defaulted_block_hole_filled/ | block_hole_generated.py, defaulted_block_hole_empty_generated.py, defaulted_block_hole_filled_generated.py |
| composition | Staged graphs: first build() produces a composable embedded in a later build(); unroll on a subsequent build(unroll=True) after indexed edges onto a loop carried in from the prior merge; indexed instance families: builder.add.Step[i](...), builder.Root.body.add.Step[i](...), and later reach-in via builder.Step[i]...; data-driven named builder API: builder.add("Root", ...), builder.instance("Root").target("body").add("Step"), direct builder.target(...), and named builder.assign(...) |
composition/staged_unroll_indexed_edges/ (recipe.py), composition/nested_three_stage_trace/, composition/indexed_instance_family/, composition/data_driven_builder_basic/, composition/data_driven_builder_nested/ | staged_unroll_indexed_edges_generated.py, nested_three_stage_trace_generated.py, indexed_instance_family_generated.py, data_driven_builder_basic_generated.py, data_driven_builder_nested_generated.py |
| descriptor_api | Self-description: discover external binds from describe() and bind them before materialize; descriptor-driven staged targets: use ComposableHole.with_root_instance(...) with builder.target(...) and descriptor identifier surfaces with bind_identifier(...); indexed unroll: resolve a descriptor target once, then use target[i] and indexed Step[i] sources for loop-expanded holes |
descriptor_api/external_bind_single/, descriptor_api/staged_descriptor_targets/, descriptor_api/unrolled_indexed_descriptor_targets/ | external_bind_single_generated.py, staged_descriptor_targets_generated.py, unrolled_indexed_descriptor_targets_generated.py |
| assembler_scope | Scope-driven candidate lookup: wrap composables, external values, and identifier names as resources; filter by demand name, build-path selectors such as ("Root", "GetterBody[1]"), and code-owner selectors; apply the selected candidate to an AssemblyScope |
assembler_scope/resource_candidates/ | resource_candidates_generated.py |
| scope | Hygiene / names: colliding locals across two inserts (total -> total__astichi_scoped_*); two named holes (setup then body); builder insert + astichi_keep (inner value renamed, outer name kept) |
scope/ | colliding_locals_two_inserts_generated.py, two_holes_ordered_generated.py, outer_hole_inner_insert_keep_generated.py |
Metadata line (optional, first line of a flat snippet):
# astichi-snippet: {"bind": {...}, "bind_identifier": {...}, "arg_names": {...}}
JSON lists in bind are normalized to tuples where needed.
Builder bundle (snippet.json):
instances: map of builder instance name → fragment filename or{ "file": "impl.py", "arg_names": { … } }(anyastichi.compilekwargs).wires: Python expressions evaluated withbuilderin scope (e.g.builder.Root.body.add.Step1(...)).unroll: passed tobuild(unroll=…).
Recipe bundle (recipe.py only, no snippet.json):
- Defines
def run() -> str:returning final generated source. - Use when the example needs multiple
build()calls,assignchains, indexedPipeline.Root.Loop.step[i]wiring, or an emission surface such asemit_commented()that is awkward to encode as JSON.
Long-form narrative docs: marker-overview.md, marker-comment.md, scoping-hygiene.md, using-the-api.md, builder-api.md (fluent and data-driven builder surfaces), descriptor-api.md (self-description and descriptor-driven composition), inventory-api.md (record/map discovery), assembler-scope.md (candidate lookup/application), addressing.md (paths + indexed edges).