Assemble classes and methods¶
Start with the desired Python shape: class shell, slots declaration, methods, then method parameters and statements. Expose a named surface for each part that varies. Keep field policy in input facts and matcher rules.
The first generator uses a ClassShell resource
and a nested InitProduction. Its class root binds the class-name identifier
and the already-normalized slot tuple. Field rows supply direct stores through
transparent attribute targets. There is no descriptor dispatch in those stores.
To add a property getter/setter, use an authored method/property resource, select it by field policy, bind its public field name and storage path, and insert it into the class body. Keep the public spelling deliberately; let unrelated locals be hygienically renamed. Consumer examples show specialized accessors selected from actual consumer facts.
Conditional omission¶
The tutorial's EnableInit selects InitContribution; the fallback selects an
empty-source resource. Empty source suppresses that contribution. The always
present ClassPass fills the mandatory class method hole so disabled-init output
still materializes. --empty instead keeps an actual zero-parameter constructor.
These outcomes differ: omitted init inherits Python's default construction;
empty init is a generated function.
Compound Python constructs¶
YIDL embeds Astichi Python resources rather than defining separate syntax for
if, try, with, decorators or comprehensions. An insertion must remain within
a legal Python construct boundary. Put statement holes inside braced YIDL
resources containing normal indented Python; use explicit pass contributions
when a selected suite is empty. Interacting exception/loop/decorator constructs
need their own SDK/version qualification. The first class does not establish all
Python syntax as supported. See the bridge for
known positions and limitations, and compatibility
for unqualified compound cases.
Execute interacting compound constructs¶
example/documentation/control_flow.py qualifies a statement-hole insertion
inside if nested in for/else, with, try/except/finally, with a
decorated function and comprehension in the same resource. Zero/one/two ordered
actions, empty input and a raised exception have explicit behavior assertions.
This is a CPython 3.12.12 / Astichi 1.1.3 Python-backend recipe; other Python
construct positions and version-specific syntax require separate checks.
Source authority: example/documentation/control_flow.py.
"""Compose ordered statements inside interacting Python compound constructs."""
from __future__ import annotations
import ast
from yidl.concept_parser import compile_yidl_files
from yidl.generation.data_def_sys import emit_concept_runtime_source
SOURCE = """
concept ControlFlow {
property Label: str
property Order: int
record Action { Label Order }
collection Actions: Action identity Label many
resource Root = code $[
from contextlib import nullcontext
def identity(function):
return function
@identity
def run(items, fail=False):
events = []
try:
with nullcontext():
for item in items:
if item > 1:
astichi_hole(actions)
elif item == 1:
events.append("one")
else:
events.append("other")
else:
events.append("loop-else")
if fail:
raise ValueError("requested")
except ValueError:
events.append("caught")
finally:
events.append("finally")
return events, [item * item for item in items]
]$ { keep run }
resource ActionCode = template $[
astichi_pass(events, outer_bind=True).append((astichi_pass(item, outer_bind=True), astichi_bind_external(label)))
]$
resource Empty = template `pass`
contribution ActionContribution = ActionCode {
as Action
index Order
order Order
target actions { build /Root }
external label = Label
}
contribution EmptyContribution = Empty {
as Empty
target actions { build /Root }
}
matcher Selected(action: Actions) -> contribution {
default -> ActionContribution
}
matcher EmptySelection() -> contribution {
default -> EmptyContribution
}
production RootProduction -> composable {
root Root = Root
apply empty using EmptySelection
apply actions from action: Actions using Selected
}
assembly Module = RootProduction
}
"""
def run() -> str:
concept = compile_yidl_files({"control.yidl": SOURCE}, "control.yidl").concepts[
"ControlFlow"
]
runtime = emit_concept_runtime_source(
concept.plan.build_data_definition(),
resources=concept.resources,
assembly_plan=concept,
)
schema: dict[str, object] = {}
exec(runtime, schema)
sources = []
for labels in ((), ("first",), ("first", "second")):
builder = schema["new_builder"]()
for order, label in reversed(tuple(enumerate(labels))):
builder.add(
schema["ActionsCollection"], schema["Action"](label=label, order=order)
)
tree = schema["build_Module"](builder.freeze()).to_executable_ast()
namespace: dict[str, object] = {}
exec(compile(tree, "control_model.py", "exec"), namespace)
events, squares = namespace["run"]([2, 1, 0])
assert events == [(2, label) for label in labels] + [
"one",
"other",
"loop-else",
"finally",
]
assert squares == [4, 1, 0]
events, squares = namespace["run"]([3], fail=True)
assert events == [(3, label) for label in labels] + [
"loop-else",
"caught",
"finally",
]
assert squares == [9]
assert namespace["run"]([]) == (["loop-else", "finally"], [])
sources.append(ast.unparse(tree) + "\n")
return sources[-1]
if __name__ == "__main__":
print(run(), end="")
Source authority: tests/data/goldens/materialized/documentation_classes/control_flow_model.py.
from contextlib import nullcontext
def identity(function):
return function
@identity
def run(items, fail=False):
events = []
try:
with nullcontext():
for item in items:
if item > 1:
pass
events.append((item, 'first'))
events.append((item, 'second'))
elif item == 1:
events.append('one')
else:
events.append('other')
else:
events.append('loop-else')
if fail:
raise ValueError('requested')
except ValueError:
events.append('caught')
finally:
events.append('finally')
return (events, [item * item for item in items])