Use Case 1: YAML-First Contract Authoring¶
Scenario: you design the contract as a YAML spec, generate ABC Python source from it, hand-write concrete implementations, and prove the spec and code agree.
๐ฆ What's New?¶
| Component | Description |
|---|---|
parse_yaml |
Reads the spec into the neutral PortNode tree. |
generate_abc |
Emits _abc.py source (contracts only) from the tree. |
check_matches |
Verifies the spec and the ABC module describe the same tree. |
๐ Example¶
Start from the minimal sample spec (samples/minimal/sample.yaml):
YAML
ParentClass:
config_var1: str
config_var2: int
methods:
run:
args: [arg1, arg2]
kwargs: {kwarg1: int, kwarg2: str}
port1:
NonHexaChildClass1:
config_var11: str
config_var12: int
methods: {method1: {args: [arg1, arg2], kwargs: {kwarg1: int, kwarg2: str}}}
port2:
HexaChildClass2:
config_var21: str
config_var22: int
methods: {method1: {args: [arg1, arg2], kwargs: {kwarg1: int, kwarg2: str}}}
port1:
NonHexaPort1:
config_var11: str
config_var12: int
methods: {method1: {args: [arg1, arg2], kwargs: {kwarg1: int, kwarg2: str}}}
Generate the ABC module from the spec:
The generated file declares the contracts only โ attributes, port slots, and abstract methods:
Python
class ParentClass(ABC):
config_var1: str
config_var2: int
port1: NonHexaChildClass1
port2: HexaChildClass2
port3: HexaChildClass3
@abstractmethod
def run(self, arg1, arg2, *, kwarg1: int, kwarg2: str): ...
Now write the concrete implementation (hand-written โ behavior and slot pinning are human decisions):
Python
class ImplParentClass(ParentClass):
port1: NonHexaChildClass1
port2: HexaChildClass2
port3: HexaChildClass3
def run(self, arg1, arg2, *, kwarg1: int = 0, kwarg2: str = ""):
return {
"port1": self.port1.method1(arg1, arg2, kwarg1=kwarg1, kwarg2=kwarg2),
"port2": self.port2.method1(arg1, arg2, kwarg1=kwarg1, kwarg2=kwarg2),
"port3": self.port3.method1(arg1, arg2, kwarg1=kwarg1, kwarg2=kwarg2),
}
Finally, guard the two representations against drift:
๐ก Tips¶
generate_abcemits contracts only โ it never generatesImpl*/specialization files. Slot pinning and method bodies are your call.- The YAML grammar distinguishes a port (a mapping whose single key has a non-scalar body) from a nested struct (any other mapping). Keep exactly one class key per port slot.
check_matchescompares classes, attributes, slots, and@abstractmethodsignatures; missing keyword-only params are treated conservatively so minor refactors don't false-negative.- All generators are deterministic (walk order), so regenerating a spec from a module and diffing is stable.