1 line
23 KiB
JSON
1 line
23 KiB
JSON
{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"hexa","text":""},{"location":"#hexa","title":"hexa","text":""},{"location":"#hexa--summary","title":"Summary","text":"<p>Type-declared dependency injection for Python.</p> <p>Hexa is a pattern and utility library for structuring complex hierarchical pipelines. It rests on one core idea: a concrete class's annotated slot type is the dependency decision. Variation is expressed entirely as class annotations \u2014 never as imperative wiring, never as <code>__init__</code> parameters, never as a composition root that assembles objects by hand.</p> <p>All utilities serialize through a single shared model (<code>PortNode</code>):</p> <ul> <li><code>parse_yaml</code> / <code>generate_yaml</code> \u2014 between YAML specs and the model</li> <li><code>parse_abc</code> / <code>generate_abc</code> \u2014 between ABC Python modules and the model</li> <li><code>check_matches</code> \u2014 verify two representations describe the same tree</li> <li><code>build</code> \u2014 recursively instantiate a wired pipeline from annotations</li> </ul>"},{"location":"#hexa--installation","title":"Installation","text":"<p>Install using pip:</p> <pre><code>pip install hexa\n</code></pre>"},{"location":"#hexa--quick-start","title":"Quick start","text":"<p>Define contracts, pin concrete types on the slots, and let <code>build</code> wire the tree from annotations:</p> <pre><code>from hexa import build\n\nclass NumberPort(ABC):\n @abstractmethod\n def extract(self, line: str) -> float: ...\n\nclass ParserPort(ABC):\n number: NumberPort\n\n @abstractmethod\n def parse(self, text: str) -> dict: ...\n\nclass RegexNumberPort(NumberPort):\n def extract(self, line: str) -> float:\n ...\n\nclass StandardParserPort(ParserPort):\n number: RegexNumberPort # <-- the dependency decision\n\n def parse(self, text: str) -> dict:\n return {\"value\": self.number.extract(text)}\n\npipeline = build(StandardParserPort)\nprint(pipeline.parse(\"Value: 42\"))\n</code></pre>"},{"location":"#hexa--cli-usage","title":"CLI usage","text":"<p>Convert between YAML and ABC representations:</p> <pre><code>hexa parse-yaml spec.yaml\nhexa generate-abc spec.yaml --out spec_abc.py\nhexa parse-abc spec_abc.py\nhexa generate-yaml spec_abc.py --out spec.yaml\nhexa check spec.yaml spec_abc.py\n</code></pre>"},{"location":"#hexa--core-concepts","title":"Core concepts","text":""},{"location":"#hexa--shared-model","title":"Shared model","text":"<p><code>PortNode</code> \u2014 the neutral tree every parser and generator serializes through.</p>"},{"location":"#hexa--converters","title":"Converters","text":"<p>Parsers and generators that translate between YAML specs and ABC Python modules via the shared model.</p>"},{"location":"#hexa--verification","title":"Verification","text":"<p><code>check_matches</code> \u2014 machine-check that two representations agree.</p>"},{"location":"#hexa--container","title":"Container","text":"<p><code>build</code> \u2014 recursively instantiate a full pipeline from class annotations.</p>"},{"location":"#hexa--notes","title":"Notes","text":"<ul> <li>All utilities share a single model; any port tree fits.</li> <li>Generators emit source text only; hexa performs no runtime code generation.</li> </ul>"},{"location":"#hexa-classes","title":"Classes","text":""},{"location":"#hexa.MethodSpec","title":"MethodSpec <code>dataclass</code>","text":"<pre><code>MethodSpec(\n name: str,\n args: list[str] = list(),\n kwargs: dict[str, str] = dict(),\n)\n</code></pre> <p>Specification of a single abstract method.</p> <p>Attributes:</p> Name Type Description <code>name</code> <code>str</code> <p>Name of the method.</p> <code>args</code> <code>list[str]</code> <p>Positional parameter names.</p> <code>kwargs</code> <code>dict[str, str]</code> <p>Keyword-only parameter names mapped to their type expressions.</p>"},{"location":"#hexa.PortNode","title":"PortNode <code>dataclass</code>","text":"<pre><code>PortNode(\n name: str,\n port_cls: str,\n kind: str = \"leaf\",\n attributes: dict[str, ConfigValue] = dict(),\n children: list[PortNode] = list(),\n methods: dict[str, MethodSpec] = dict(),\n)\n</code></pre> <p>A node in the hexa port tree.</p> <p>Each node is a leaf (no children), a port (has nested port slots), or the parent root of the tree. Every node carries a concrete typed shape derived from an ABC class name.</p> <p>Attributes:</p> Name Type Description <code>name</code> <code>str</code> <p>Slot name, e.g. <code>\"parser\"</code>, <code>\"amount_balance\"</code>.</p> <code>port_cls</code> <code>str</code> <p>ABC class name that gives this port its shape, e.g. <code>\"TransactionParserPort\"</code>. Every port has a concrete typed shape.</p> <code>kind</code> <code>str</code> <p>One of <code>\"leaf\"</code>, <code>\"port\"</code>, or <code>\"parent\"</code>. - <code>\"leaf\"</code>: no children (e.g. <code>NumberPort</code>) - <code>\"port\"</code>: has children (e.g. <code>TransactionParserPort</code>) - <code>\"parent\"</code>: the root of the tree (e.g. <code>ExtractionPipeline</code>)</p> <code>attributes</code> <code>dict[str, ConfigValue]</code> <p>Annotated attributes: <code>name -> type_expr</code> for scalars, or a nested <code>dict</code> of sub-attributes (a struct) for grouped values.</p> <code>children</code> <code>list[PortNode]</code> <p>Nested port slots.</p> <code>methods</code> <code>dict[str, MethodSpec]</code> <p>Abstract methods keyed by method name.</p>"},{"location":"#hexa.PortNode-functions","title":"Functions","text":""},{"location":"#hexa.PortNode.diff","title":"diff","text":"<pre><code>diff(other: PortNode, path: str = '') -> list[str]\n</code></pre> <p>Return a list of human-readable difference strings.</p> <p>An empty list means the trees are equal.</p> <p>Parameters:</p> Name Type Description Default <code>other</code> <code>PortNode</code> <p>Tree to compare against.</p> required <code>path</code> <code>str</code> <p>Path prefix used when recursing into nested nodes; defaults to the root path.</p> <code>''</code> <p>Returns:</p> Type Description <code>list[str]</code> <p>list[str]: Human-readable difference strings, one per discrepancy.</p>"},{"location":"#hexa.PortNode.find","title":"find","text":"<pre><code>find(name: str) -> PortNode | None\n</code></pre> <p>Find a direct child by slot name.</p> <p>Parameters:</p> Name Type Description Default <code>name</code> <code>str</code> <p>Slot name of the child to look up.</p> required <p>Returns:</p> Type Description <code>PortNode | None</code> <p>PortNode | None: The matching child node, or <code>None</code> if no child has that name.</p>"},{"location":"#hexa.PortNode.walk","title":"walk","text":"<pre><code>walk() -> list[PortNode]\n</code></pre> <p>Return all nodes in depth-first order (self first).</p> <p>Returns:</p> Type Description <code>list[PortNode]</code> <p>list[PortNode]: All nodes of the tree, self first, depth-first.</p>"},{"location":"#hexa-functions","title":"Functions","text":""},{"location":"#hexa.build","title":"build","text":"<pre><code>build(\n cls: type[T],\n *,\n instances: dict[str, Any] | None = None,\n config: dict[str, Any] | None = None\n) -> T\n</code></pre> <p>Recursively instantiate the port tree from annotations.</p> <p>Parameters:</p> Name Type Description Default <code>cls</code> <code>type[T]</code> <p>The root class to build (e.g. <code>AxisExtractionPipeline</code>).</p> required <code>instances</code> <code>dict[str, Any] | None</code> <p><code>{slot_name: object}</code> injected verbatim into every node whose annotations contain that name (runtime values \u2014 repos, handlers, clients \u2014 that must not be re-constructed). Applied to the whole tree by name.</p> <code>None</code> <code>config</code> <code>dict[str, Any] | None</code> <p><code>{field_name: value}</code> propagated by name to every node's annotated config fields (non-port attributes), replacing the default <code>None</code>.</p> <code>None</code> <p>Returns:</p> Name Type Description <code>T</code> <code>T</code> <p>A fully wired instance of <code>cls</code> and all its nested ports.</p>"},{"location":"check_matches/","title":"Check Matches","text":""},{"location":"check_matches/#hexa.check_matches","title":"hexa.check_matches","text":""},{"location":"check_matches/#hexa.check_matches--summary","title":"Summary","text":"<p>Verify that two hexa representations agree.</p> <p><code>check_matches</code> parses two representations \u2014 one YAML spec and/or ABC Python module each \u2014 and reports whether they describe the same port tree.</p>"},{"location":"check_matches/#hexa.check_matches--notes","title":"Notes","text":"<ul> <li>Uses <code>PortNode.diff</code> to produce human-readable difference strings.</li> </ul>"},{"location":"check_matches/#hexa.check_matches-classes","title":"Classes","text":""},{"location":"check_matches/#hexa.check_matches-functions","title":"Functions","text":""},{"location":"check_matches/#hexa.check_matches.check_matches","title":"check_matches","text":"<pre><code>check_matches(\n path_a: str | Path, path_b: str | Path\n) -> bool | list[str]\n</code></pre> <p>Verify two representations agree.</p> <p>Parameters:</p> Name Type Description Default <code>path_a</code> <code>str | Path</code> <p>Path to a <code>.yaml</code> or <code>.py</code> file.</p> required <code>path_b</code> <code>str | Path</code> <p>Path to a <code>.yaml</code> or <code>.py</code> file.</p> required <p>Returns:</p> Type Description <code>bool | list[str]</code> <p>bool | list[str]: <code>True</code> if they match exactly, otherwise a list of difference strings.</p>"},{"location":"cli/","title":"Cli","text":""},{"location":"cli/#hexa.cli","title":"hexa.cli","text":""},{"location":"cli/#hexa.cli--summary","title":"Summary","text":"<p>Command-line interface for hexa utilities.</p>"},{"location":"cli/#hexa.cli--usage","title":"Usage","text":"<pre><code>hexa parse-yaml PATH\nhexa generate-abc PATH [--out FILE]\nhexa parse-abc PATH\nhexa generate-yaml PATH [--out FILE]\nhexa check PATH_A PATH_B\n</code></pre>"},{"location":"cli/#hexa.cli-functions","title":"Functions","text":""},{"location":"container/","title":"Container","text":""},{"location":"container/#hexa.container","title":"hexa.container","text":""},{"location":"container/#hexa.container--summary","title":"Summary","text":"<p>Container to build a wired hexa pipeline from annotations.</p> <p><code>build</code> recursively instantiates the port tree implied by a root class's annotations, injecting provided runtime instances and config values by name.</p>"},{"location":"container/#hexa.container--notes","title":"Notes","text":"<ul> <li>Runtime instances and config values are applied by name across the whole tree.</li> </ul>"},{"location":"container/#hexa.container-functions","title":"Functions","text":""},{"location":"container/#hexa.container.build","title":"build","text":"<pre><code>build(\n cls: type[T],\n *,\n instances: dict[str, Any] | None = None,\n config: dict[str, Any] | None = None\n) -> T\n</code></pre> <p>Recursively instantiate the port tree from annotations.</p> <p>Parameters:</p> Name Type Description Default <code>cls</code> <code>type[T]</code> <p>The root class to build (e.g. <code>AxisExtractionPipeline</code>).</p> required <code>instances</code> <code>dict[str, Any] | None</code> <p><code>{slot_name: object}</code> injected verbatim into every node whose annotations contain that name (runtime values \u2014 repos, handlers, clients \u2014 that must not be re-constructed). Applied to the whole tree by name.</p> <code>None</code> <code>config</code> <code>dict[str, Any] | None</code> <p><code>{field_name: value}</code> propagated by name to every node's annotated config fields (non-port attributes), replacing the default <code>None</code>.</p> <code>None</code> <p>Returns:</p> Name Type Description <code>T</code> <code>T</code> <p>A fully wired instance of <code>cls</code> and all its nested ports.</p>"},{"location":"generate_abc/","title":"Generate Abc","text":""},{"location":"generate_abc/#hexa.generate_abc","title":"hexa.generate_abc","text":""},{"location":"generate_abc/#hexa.generate_abc--summary","title":"Summary","text":"<p>Generate <code>_abc.py</code> source from a hexa PortNode tree.</p>"},{"location":"generate_abc/#hexa.generate_abc-classes","title":"Classes","text":""},{"location":"generate_abc/#hexa.generate_abc-functions","title":"Functions","text":""},{"location":"generate_abc/#hexa.generate_abc.generate_abc","title":"generate_abc","text":"<pre><code>generate_abc(\n node: PortNode, out_path: str | Path | None = None\n) -> str\n</code></pre> <p>Generate ABC Python source from a :class:<code>PortNode</code> tree.</p> <p>Parameters:</p> Name Type Description Default <code>node</code> <code>PortNode</code> <p>The root of the parsed YAML (or generated) tree.</p> required <code>out_path</code> <code>str | Path | None</code> <p>Optional file path to write the generated source to.</p> <code>None</code> <p>Returns:</p> Name Type Description <code>str</code> <code>str</code> <p>The generated Python source code.</p>"},{"location":"generate_yaml/","title":"Generate Yaml","text":""},{"location":"generate_yaml/#hexa.generate_yaml","title":"hexa.generate_yaml","text":""},{"location":"generate_yaml/#hexa.generate_yaml--summary","title":"Summary","text":"<p>Generate YAML spec (nested mapping form) from a hexa PortNode tree.</p>"},{"location":"generate_yaml/#hexa.generate_yaml-classes","title":"Classes","text":""},{"location":"generate_yaml/#hexa.generate_yaml-functions","title":"Functions","text":""},{"location":"generate_yaml/#hexa.generate_yaml.generate_yaml","title":"generate_yaml","text":"<pre><code>generate_yaml(\n node: PortNode, out_path: str | Path | None = None\n) -> str\n</code></pre> <p>Generate YAML source from a :class:<code>PortNode</code> tree.</p> <p>Parameters:</p> Name Type Description Default <code>node</code> <code>PortNode</code> <p>The root of the tree.</p> required <code>out_path</code> <code>str | Path | None</code> <p>Optional file path to write the generated source to.</p> <code>None</code> <p>Returns:</p> Name Type Description <code>str</code> <code>str</code> <p>The generated YAML source.</p>"},{"location":"model/","title":"Model","text":""},{"location":"model/#hexa.model","title":"hexa.model","text":""},{"location":"model/#hexa.model--summary","title":"Summary","text":"<p>Shared neutral model for hexa port trees.</p> <p>All utilities (<code>parse_yaml</code>, <code>generate_abc</code>, <code>parse_abc</code>, <code>generate_yaml</code>, <code>check_matches</code>) serialize through this single model.</p>"},{"location":"model/#hexa.model--notes","title":"Notes","text":"<ul> <li>The model is NOT tied to any sample \u2014 any port tree fits.</li> </ul>"},{"location":"model/#hexa.model-classes","title":"Classes","text":""},{"location":"model/#hexa.model.MethodSpec","title":"MethodSpec <code>dataclass</code>","text":"<pre><code>MethodSpec(\n name: str,\n args: list[str] = list(),\n kwargs: dict[str, str] = dict(),\n)\n</code></pre> <p>Specification of a single abstract method.</p> <p>Attributes:</p> Name Type Description <code>name</code> <code>str</code> <p>Name of the method.</p> <code>args</code> <code>list[str]</code> <p>Positional parameter names.</p> <code>kwargs</code> <code>dict[str, str]</code> <p>Keyword-only parameter names mapped to their type expressions.</p>"},{"location":"model/#hexa.model.PortNode","title":"PortNode <code>dataclass</code>","text":"<pre><code>PortNode(\n name: str,\n port_cls: str,\n kind: str = \"leaf\",\n attributes: dict[str, ConfigValue] = dict(),\n children: list[PortNode] = list(),\n methods: dict[str, MethodSpec] = dict(),\n)\n</code></pre> <p>A node in the hexa port tree.</p> <p>Each node is a leaf (no children), a port (has nested port slots), or the parent root of the tree. Every node carries a concrete typed shape derived from an ABC class name.</p> <p>Attributes:</p> Name Type Description <code>name</code> <code>str</code> <p>Slot name, e.g. <code>\"parser\"</code>, <code>\"amount_balance\"</code>.</p> <code>port_cls</code> <code>str</code> <p>ABC class name that gives this port its shape, e.g. <code>\"TransactionParserPort\"</code>. Every port has a concrete typed shape.</p> <code>kind</code> <code>str</code> <p>One of <code>\"leaf\"</code>, <code>\"port\"</code>, or <code>\"parent\"</code>. - <code>\"leaf\"</code>: no children (e.g. <code>NumberPort</code>) - <code>\"port\"</code>: has children (e.g. <code>TransactionParserPort</code>) - <code>\"parent\"</code>: the root of the tree (e.g. <code>ExtractionPipeline</code>)</p> <code>attributes</code> <code>dict[str, ConfigValue]</code> <p>Annotated attributes: <code>name -> type_expr</code> for scalars, or a nested <code>dict</code> of sub-attributes (a struct) for grouped values.</p> <code>children</code> <code>list[PortNode]</code> <p>Nested port slots.</p> <code>methods</code> <code>dict[str, MethodSpec]</code> <p>Abstract methods keyed by method name.</p>"},{"location":"model/#hexa.model.PortNode-functions","title":"Functions","text":""},{"location":"model/#hexa.model.PortNode.diff","title":"diff","text":"<pre><code>diff(other: PortNode, path: str = '') -> list[str]\n</code></pre> <p>Return a list of human-readable difference strings.</p> <p>An empty list means the trees are equal.</p> <p>Parameters:</p> Name Type Description Default <code>other</code> <code>PortNode</code> <p>Tree to compare against.</p> required <code>path</code> <code>str</code> <p>Path prefix used when recursing into nested nodes; defaults to the root path.</p> <code>''</code> <p>Returns:</p> Type Description <code>list[str]</code> <p>list[str]: Human-readable difference strings, one per discrepancy.</p>"},{"location":"model/#hexa.model.PortNode.find","title":"find","text":"<pre><code>find(name: str) -> PortNode | None\n</code></pre> <p>Find a direct child by slot name.</p> <p>Parameters:</p> Name Type Description Default <code>name</code> <code>str</code> <p>Slot name of the child to look up.</p> required <p>Returns:</p> Type Description <code>PortNode | None</code> <p>PortNode | None: The matching child node, or <code>None</code> if no child has that name.</p>"},{"location":"model/#hexa.model.PortNode.walk","title":"walk","text":"<pre><code>walk() -> list[PortNode]\n</code></pre> <p>Return all nodes in depth-first order (self first).</p> <p>Returns:</p> Type Description <code>list[PortNode]</code> <p>list[PortNode]: All nodes of the tree, self first, depth-first.</p>"},{"location":"parse_abc/","title":"Parse Abc","text":""},{"location":"parse_abc/#hexa.parse_abc","title":"hexa.parse_abc","text":""},{"location":"parse_abc/#hexa.parse_abc--summary","title":"Summary","text":"<p>Parse an ABC Python module into a hexa PortNode tree.</p> <p>ABC classes become port nodes. A nested-struct attribute is expressed as a <code>@dataclass</code> annotation on an ABC port (e.g. <code>source: ExtractionSource</code> where <code>ExtractionSource</code> is a <code>@dataclass</code>); the dataclass's fields are expanded into a nested <code>attributes</code> entry.</p>"},{"location":"parse_abc/#hexa.parse_abc--notes","title":"Notes","text":"<ul> <li>Accepts a file path, a module import string, or an already-loaded module.</li> </ul>"},{"location":"parse_abc/#hexa.parse_abc-classes","title":"Classes","text":""},{"location":"parse_abc/#hexa.parse_abc-functions","title":"Functions","text":""},{"location":"parse_abc/#hexa.parse_abc.parse_abc","title":"parse_abc","text":"<pre><code>parse_abc(module_or_path: str | Path | Any) -> PortNode\n</code></pre> <p>Parse an ABC Python module into a :class:<code>PortNode</code> tree.</p> <p>ABC classes become port nodes. A nested-struct attribute is expressed as a <code>@dataclass</code> annotation on an ABC port (e.g. <code>source: ExtractionSource</code> where <code>ExtractionSource</code> is a <code>@dataclass</code>); the dataclass's fields are expanded into a nested <code>attributes</code> entry.</p> <p>Parameters:</p> Name Type Description Default <code>module_or_path</code> <code>str | Path | Any</code> <p>A file path to a <code>.py</code> file, a module import string, or a loaded module.</p> required <p>Returns:</p> Name Type Description <code>PortNode</code> <code>PortNode</code> <p>The root node of the tree.</p>"},{"location":"parse_yaml/","title":"Parse Yaml","text":""},{"location":"parse_yaml/#hexa.parse_yaml","title":"hexa.parse_yaml","text":""},{"location":"parse_yaml/#hexa.parse_yaml--summary","title":"Summary","text":"<p>Parse a hexa YAML spec into a :class:<code>PortNode</code> tree.</p> <p>The YAML grammar uses a nested mapping format built from three member kinds:</p> <ul> <li>attribute: a <code>name: type</code> scalar (e.g. <code>source_type: str</code>)</li> <li>nested struct: a <code>name:</code> mapping of sub-attributes (e.g. <code>source:</code>)</li> <li>port: a <code>name:</code> mapping whose single key is a class whose value is a body, or a class key with a <code>...</code> body. A port carries a class shape.</li> </ul> Disambiguation of a mapping value <p>A mapping with exactly one key whose value is a non-scalar body (a mapping, <code>...</code>, or a list) is a port. Any other mapping (multiple keys, or all scalar-typed values) is a nested struct of attributes.</p>"},{"location":"parse_yaml/#hexa.parse_yaml--examples","title":"Examples","text":"<p>Parse a spec into a port tree:</p> <pre><code>```python\nfrom hexa import parse_yaml\n\nnode = parse_yaml(\"samples/minimal/sample.yaml\")\nprint(node)\n```\n</code></pre> <p>The YAML grammar:</p> <pre><code>```yaml\nRootClassName:\n attr: type # scalar attribute\n source: # nested struct (all scalar sub-attributes)\n source_type: str\n bank: str\n methods: # reserved block of functions\n run:\n args: [a, b]\n ingest: # port (single non-scalar class key)\n IngestPort:\n source_type: str\n methods: {Method: ...}\n```\n</code></pre>"},{"location":"parse_yaml/#hexa.parse_yaml--notes","title":"Notes","text":"<ul> <li>See <code>samples/minimal/sample.yaml</code> and <code>samples/extraction_pipeline/extraction_pipeline.yaml</code> for worked examples.</li> </ul>"},{"location":"parse_yaml/#hexa.parse_yaml-classes","title":"Classes","text":""},{"location":"parse_yaml/#hexa.parse_yaml-functions","title":"Functions","text":""},{"location":"parse_yaml/#hexa.parse_yaml.parse_yaml","title":"parse_yaml","text":"<pre><code>parse_yaml(path: str | Path) -> PortNode\n</code></pre> <p>Parse a hexa YAML spec file into a :class:<code>PortNode</code> tree.</p> <p>Parameters:</p> Name Type Description Default <code>path</code> <code>str | Path</code> <p>Path to the <code>.yaml</code> file.</p> required <p>Returns:</p> Name Type Description <code>PortNode</code> <code>PortNode</code> <p>The root node of the parsed tree.</p> <p>Raises:</p> Type Description <code>ValueError</code> <p>On structural errors in the YAML.</p>"}]} |