Skip to content

Follow properties into generated parameters

Run the tutorial with --extended. It generates Point(x, y) and Size(x) from two class rows and five field rows. The same field spelling x belongs to two owners. A hidden field and an orphan row make incorrect selection visible.

Input rows

ClassId ClassName ClassOrder SlotNames
point Point 10 x, y
size Size 20 x
FieldId Owner FieldName FieldOrder Selected
point.y point y 20 True
point.x point x 10 True
point.hidden point hidden 30 False
size.x size x 10 True
orphan.z absent z 99 True

Fields uses FieldId as collection identity. The semantic property FieldName is populated with the generated constructor keyword field_name. Neither is the runtime field value, which is supplied later to Point(3, 4).

Selection, projection and destination

InitProduction(cls: Classes) retains the current class as context. Each from field: Fields enumerates candidates and where Owner == ClassId and Selected == True relates them to that class. This is explicit filtering, not an implicit keyed join. For Point, point.y and point.x survive; for Size, only size.x survives. No class can match orphan.z.

Params(cls: Classes, field: Fields) chooses Param. Its ident field_name = FieldName turns the chosen name into a Python parameter identifier. Assignment binds the same property as ident value and external slot for the outer parameter read and direct attribute target. Target params or stores resolves against /InitMethod within the nested production's mapped root.

Build index (ClassOrder, FieldOrder) identifies an insertion independently of its collection identity. order FieldOrder determines parameter/store sequence. The facts are deliberately inserted y before x, but output is x before y:

Source authority: tests/data/goldens/materialized/documentation_classes/extended_model.py.

class Point:
    __slots__ = ('x', 'y')
    pass

    def __init__(self, x, y):
        pass
        pass
        self.x = x
        self.y = y

class Size:
    __slots__ = ('x',)
    pass

    def __init__(self, x):
        pass
        pass
        self.x = x

M01–M10 contract map

Case Contract and executable evidence
M01 subset/properties Tutorial selects FieldName while identity/owner/order have separate roles
M02 inputs/combinations Tutorial contextual class + field filtering; standalone yidl_update_a_multi_source_from.py enumerates two sources; empty collection contributes nothing
M03 view/lookup dds_composite_identity_lookup.py: tuple key (TxKey, Phase), explicit projection; a computed view filters existing records
M04 ownership/ambiguity DDS Python API input.prop(property) is explicit; contribution syntax exposes visible semantic names and rejects duplicates; do not invent field.FieldName there
M05 selection test_matcher.py: broad/narrow, weight, default, no-match, overlap; contribution rules are separately checked
M06 evaluated fields matcher.evaluated_field(..., inputs=(left,right), value=callable, value_type=...) derives tuple entries; emitted evaluators require an importable/generated name
M07 projection dds_matcher_productions.py: match.record("field").prop(name) versus match.value(0); invalid tuple index/name diagnostics are tested
M08 context Tutorial retains cls; unavailable/duplicate visible properties reject rather than guessed qualification
M09 bindings/parameters Tutorial maps one property to parameter identifier, outer read and attribute path; captures/defaults remain consumer policy
M10 identity/order Tutorial numeric composite index avoids two-owner collisions; ClassId/FieldId, owner, name, index, order and target remain distinct

Original records versus extracted values

Generic matcher-result productions offer three different expressions:

match.resource()                       # selected generated value
match.record("field").prop(name)       # original input record's property
match.value(0)                         # positional extracted/evaluated tuple entry

The tuple schema includes fields demanded by conditions/evaluators, not every property of every input record. Inspect matcher.tuple_schema; never assume index zero means the first record's first declared property. This complete canonical example demonstrates the projection and generated operation snapshot:

Source authority: tests/data/gold_src/dds_matcher_productions.py.

from __future__ import annotations

from support.golden_case import run_case
from yidl.generation.data_def_sys import AddIfAbsent
from yidl.generation.data_def_sys import DataDefinitionSystem
from yidl.generation.data_def_sys import REQUIRED
from yidl.generation.data_def_sys import emit_container_runtime_source
from yidl.generation.data_def_sys import from_astichi_code
from yidl.generation.data_def_sys import match
from yidl.generation.data_def_sys import read


REQUIRED_TEMPLATE = from_astichi_code("{'param': 'required'}")
DEFAULTED_TEMPLATE = from_astichi_code("{'param': 'defaulted'}")


def _build_dds() -> DataDefinitionSystem:
    dds = DataDefinitionSystem()
    name = dds.property("Name", str, default=REQUIRED, storage_name="name")
    init = dds.property("Init", bool, default=True, storage_name="init")
    defaulted = dds.property(
        "Defaulted",
        bool,
        default=False,
        storage_name="defaulted",
    )
    order = dds.property("Order", int, default=0, storage_name="order")
    target_port = dds.property(
        "TargetPort",
        object,
        default=REQUIRED,
        storage_name="target_port",
    )
    template = dds.property(
        "Template",
        object,
        default=REQUIRED,
        storage_name="template",
    )
    field = dds.record("Field", name, init, defaulted, order)
    param = dds.record("ParamComponent", name, target_port, order, template, defaulted)
    fields = dds.collection("Fields", field, cardinality=dds.many, identity=name)
    init_fields = dds.computed_collection(
        "InitFields",
        source=fields,
        when=(init.eq(True),),
    )
    params = dds.collection("Params", param, cardinality=dds.many, identity=name)
    init_params = dds.port("Init.params", cardinality=dds.many)
    dds.port_index(target=target_port, order=order)

    matcher = dds.matcher("InitParamTemplate")
    field_input = matcher.input("field", init_fields)
    matcher.default(REQUIRED_TEMPLATE)
    matcher.rule(
        when=(field_input.prop(defaulted).eq(True),),
        resource=DEFAULTED_TEMPLATE,
        name="defaulted-param",
    )

    production = dds.production(
        "InitParamTemplateProvidesParam",
        source=matcher.results(),
        target=params,
        values={
            name: match.record("field").prop(name),
            target_port: init_params.of(("Example", "__init__")),
            order: match.record("field").prop(order),
            template: match.resource(),
            defaulted: match.value(0),
        },
        policy=AddIfAbsent,
    )
    dds.production_group("FieldChildren", production)
    return dds


def render_case() -> str:
    return emit_container_runtime_source(_build_dds())


def validate_case(source: str) -> None:
    namespace: dict[str, object] = {}
    exec(source, namespace)

    builder = namespace["new_builder"]()
    field = namespace["Field"]
    fields = namespace["FieldsCollection"]
    init_params = namespace["InitParamsPort"]

    builder.add(fields, field(name="count", init=True, defaulted=False, order=1))
    builder.add(fields, field(name="label", init=True, defaulted=True, order=2))
    builder.add(fields, field(name="skip", init=False, defaulted=True, order=0))
    container = namespace["build_container"](builder)

    runtime_params = init_params.of(("Example", "__init__"))
    children = container.children_at(runtime_params)

    assert [record.name for record in children] == ["count", "label"]
    assert [record.template for record in children] == [
        REQUIRED_TEMPLATE,
        DEFAULTED_TEMPLATE,
    ]
    assert [record.defaulted for record in children] == [False, True]
    assert "snapshot = builder._snapshot()" in source
    assert "snapshot.matchers.InitParamTemplate.sequence()" in source
    assert "source.resource" in source
    assert "source.records[0].name" in source
    assert "source.values[0]" in source
    assert source.index("from itertools import product") < source.index("_NameProperty")
    assert source.index("from yidl.generation.data_def_sys import") < source.index(
        "_NameProperty"
    )
    assert source.index("from_astichi_code") < source.index("_NameProperty")
    assert source.count("from itertools import product") == 1
    assert source.count("from yidl.generation.data_def_sys import") == 1
    assert "dds.matcher_production" not in source
    assert "dds.production(" not in source


if __name__ == "__main__":
    raise SystemExit(
        run_case("dds_matcher_productions.py", render_case, validate_case)
    )

For multi-input derived values, the evaluator receives inputs in its declared inputs order. Source emission cannot serialize an arbitrary closure. Conditions on the derived field can select a generated value; original records remain available separately. Matchers gives selection rules; consumer excerpts show real constructor/accessor uses.

A derived value from two original inputs

This exact existing test is the minimal in-memory Python-API recipe. Four combinations yield one selected result; original records remain ordered field, facade, while the extracted tuple contains only the derived boolean.

Source authority: tests/generation/test_matcher.py.

def test_evaluated_field_can_depend_on_multiple_inputs() -> None:
    dds = DataDefinitionSystem()
    field_name = dds.property("FieldName", str, default=REQUIRED, storage_name="field_name")
    facade_name = dds.property(
        "FacadeName",
        str,
        default=REQUIRED,
        storage_name="facade_name",
    )
    field_spec = dds.record("FieldSpec", field_name)
    facade_spec = dds.record("FacadeSpec", facade_name)
    fields = dds.collection(
        "Fields",
        field_spec,
        cardinality=dds.many,
        identity=field_name,
    )
    facades = dds.collection(
        "Facades",
        facade_spec,
        cardinality=dds.many,
        identity=facade_name,
    )
    matcher = dds.matcher("Visibility")
    field = matcher.input("field", fields)
    facade = matcher.input("facade", facades)
    visible = matcher.evaluated_field(
        "Visible",
        inputs=(field, facade),
        value=lambda field_record, facade_record: (
            field_record.field_name == "count"
            and facade_record.facade_name == "runtime"
        ),
        value_type=bool,
    )
    visible_resource = from_literal("visible")
    matcher.rule(
        name="visible",
        when=(visible.eq(True),),
        resource=visible_resource,
    )
    runtime = matcher.runtime()

    results = tuple(
        runtime.sequence(
            (
                field_spec.record(field_name="count"),
                field_spec.record(field_name="label"),
            ),
            (
                facade_spec.record(facade_name="runtime"),
                facade_spec.record(facade_name="debug"),
            ),
        )
    )

    assert len(results) == 1
    assert results[0].resource is visible_resource
    assert results[0].records[0].field_name == "count"
    assert results[0].records[1].facade_name == "runtime"
    assert results[0].values == (True,)

For emitted runtime source, use emit_matcher_runtime_source with explicit evaluator_names=((callable, "generated_name"),) and provide that callable under the declared name in the runtime namespace (or an import at the owning boundary). The canonical matcher_runtime.py fixture exercises this emission contract. A lambda in the in-memory example is not by itself an emitted evaluator.