Skip to content

Use Case 2: ABC-First Contract Authoring

Scenario: you prefer to author contracts as Python ABC modules, then emit โ€” and verify โ€” a YAML spec for them.


๐Ÿ“ฆ What's New?

Component Description
parse_abc Reflects an ABC module into the neutral PortNode tree.
generate_yaml Emits a YAML spec from the tree.
check_matches Verifies the two representations agree (reverse direction).

๐Ÿš€ Example

Author the contract in Python (see samples/minimal/abc.py). A nested-struct attribute is expressed as a @dataclass annotation on an ABC port; the dataclass fields expand into a nested attributes entry when parsed:

Python
from abc import ABC, abstractmethod


class NonHexaPort1(ABC):
    config_var11: str
    config_var12: int

    @abstractmethod
    def method1(self, arg1, arg2, *, kwarg1: int = 0, kwarg2: str = "") -> str: ...


class ParentClass(ABC):
    config_var1: str
    config_var2: int

    port1: NonHexaChildClass1
    port2: HexaChildClass2
    port3: HexaChildClass3

    @abstractmethod
    def run(self, arg1, arg2, *, kwarg1: int = 0, kwarg2: str = "") -> dict: ...

Emit the YAML spec from the module:

Bash
hexa generate-yaml samples/minimal/abc.py --out spec.yaml

Inspect the parsed tree to make sure the root was detected correctly:

Bash
hexa parse-abc samples/minimal/abc.py
# PortNode(name='ParentClass', port_cls='ParentClass', kind='parent', ...)

And verify the spec against the module:

Bash
hexa check spec.yaml samples/minimal/abc.py
# Matches.   (exit 0)

๐Ÿ’ก Tips

  • Root detection: parse_abc picks the class that is not referenced as any other slot; on ties it prefers names containing Pipeline, Root, or Parent.
  • parse_abc accepts a .py path, an import string (e.g. samples.minimal.abc), or an already-loaded module, so it composes well in scripts.
  • A @dataclass referenced from a port becomes a nested struct; a dataclass field that references an ABC becomes a nested port slot keyed under the struct.
  • YAML generated this way is exactly the grammar parse_yaml understands โ€” the two directions round-trip (node1.diff(node3) == [] in the repo's roundtrip tests).