How YIDL works¶
Learning path · Step 1 of 4. Start with this overview; no installation or commands are needed on this page. Next comes Generation stages.
YIDL helps you write a Python code generator: a program that builds other Python classes and functions from structured input. Use it when several related classes need different combinations of fields, methods or initialization steps, and you want reusable code pieces plus explicit rules for combining them.
The first tutorial generates a plain Point class with slots x and y and a
constructor that stores its arguments directly. More elaborate generators can
select property accessors, default/factory calls and ordered method statements.
The compatibility table bounds the demonstrated
language, Python and backend combinations; this is not a promise that every
Python construct can be inserted in every position.
What you supply¶
Think of writing a generator as separating a Python recipe from its inputs:
| You supply | In the Point tutorial | Purpose |
|---|---|---|
| The shape of the generator's inputs | A class row and field rows with names, owners and order | Describe what information the generator can read |
| Reusable Python code pieces | A class shell, constructor, parameter and assignment | Describe the output shapes you want |
| Selection and placement rules | Select a field's parameter/store and place it in the constructor | Decide which pieces to use and where |
| Python code that supplies input and runs generation | generate.py |
Supply the particular class/field rows and execute the result |
The .yidl file holds the first three parts. The Python driver supplies the
fourth. A project using YIDL this way is called a consumer: it is an
application of the generator compiler. Lifecycle, Dataclass and Undoable are
larger consumers; the Point tutorial needs none of those packages.
Two generated products, then user objects¶
YIDL first compiles your .yidl definitions into a generator library. In the
tutorial this is runtime.py. It defines Python classes for input rows, a place
to collect those rows, and functions that assemble the requested application
code. Importing this library does not by itself create Point instances.
You then supply rows describing the particular class to generate. Running the
library's assembly function combines the selected code pieces into Python code
for that class. The tutorial saves a readable copy as model.py and executes
the generated Python tree to obtain the Point class. Finally, ordinary
application code calls Point(3, 4) to create an object.
flowchart LR
D[Authored definitions and Python code pieces] --> L[Generated generator library]
L --> G[Run generation]
F[Input rows describing Point and its fields] --> G
G --> P[Generated Point class code]
P --> C[Execute code to create Point class]
C --> O[Call Point with 3 and 4 to create an object]
| Item | What it describes | When it exists |
|---|---|---|
classes.yidl |
How to generate this family of classes | Generator authoring |
runtime.py |
Row types and assembly functions for that generator | Generator preparation |
A field row containing field_name="x" |
A name the generator should put in code | One generation's input |
model.py / the generated Python tree |
Code defining the selected Point class | Generation output |
Point |
The executable Python class | After executing generated code |
Point(3, 4) |
An ordinary instance whose x and y values are 3 and 4 | Application execution |
In particular, a fact is an input value describing what to generate. The fact
that a field is named x is different from the value 3 stored in an object's
x slot. Generating a class and constructing an instance are separate actions.
Names for the pieces¶
The declaration names describe the roles you just saw. Use this as a glossary; you do not need to memorize every term before continuing. The tutorial shows how the pieces work together:
- A concept groups a generator's definitions.
- A property names one kind of fact, such as
FieldName. It has a Python value type and a storage spelling. The generated row constructor usesfield_name; YIDL declarations refer to the semantic nameFieldName. - A record groups properties into a row shape.
FieldRowis the generated Python type used to create field facts. A collection is a named group of those rows, such asFields. - DDS means Data Definition System. It is YIDL's input-schema and data container layer: it defines the row shapes, validates input and provides queries. It does not decide an application's field or transaction policies.
- A resource is a reusable generated value, commonly an authored Python code piece with named gaps. A hole is a gap where other code can be inserted.
- A matcher uses input rows to select a resource or contribution. For example, enabled fields get constructor parameters; ignored fields do not.
- A binding supplies a varying name, path or literal to a code piece. Binding
the field-name fact
xcan produce a parameterxand the assignmentself.x = x. - A contribution combines a code piece with its bindings and destination. A production groups the assembly steps, such as building a constructor and filling its parameter/body holes. A production's root is the code piece to which those child pieces are attached.
- An assembly gives a production a callable entrypoint in the generator
library.
assembly Moduleproduces the tutorial'sbuild_Modulefunction.
Data productions and operations are a separate use of the data layer: they create derived facts before code assembly. You will meet them after the basic class tutorial; no callable-dependency analysis is required for Point.
YIDL, Astichi and your application¶
Your generator/application decides policy: which fields exist, which are constructor arguments, which defaults are valid and what behavior the resulting classes should have. The tutorial's Python driver supplies names and an explicit slot-name tuple; the data layer does not infer those slots from field rows.
YIDL gives those inputs declared shapes, selects code pieces through rules, and supplies their bindings, destinations and ordering.
Astichi, a separate package used by YIDL, combines Python code structurally.
It works with an abstract syntax tree (AST): Python data objects representing
statements and expressions. It also manages scope and hygiene, which means
renaming local identifiers where needed to prevent accidental clashes when
separate pieces are combined. A composable is Astichi's intermediate code
object with gaps and bindings; materialization resolves it into a final
Python tree ready for Python's compile function. It is not a Point instance.
The generated class then runs as ordinary Python. If a generated method calls an application callback, that callback belongs to application execution; selecting a code piece does not imply running that callback during generation.
Choose the right place to make a change¶
To generate a field named z, change the supplied field facts and the matching
slot-name input together, then update the tutorial's constructor call and checks
to express the new expected result. To give every selected field a different assignment
pattern, change the authored resource/rules in classes.yidl and regenerate the
library. To store a different value in an existing Point, call Point with
different arguments. Editing model.py only changes a derived output and will
be lost on the next generation.
Next: Generation stages — follow the authored definitions through to a usable class before installing the example environment.