"""# Summary Example pipeline family — port contracts (stage interfaces + root interface). Ports are declared as a DI tree: the root's annotated slots *are* the dependency decisions. The concrete subclass in ``impl.py`` pins the stage types. """ from abc import ABC, abstractmethod from typing import Any from pipelines.shared.ports import FinalizePort class IngestPort(ABC): """Obtain the raw input as text/bytes.""" @abstractmethod def materialize(self, source: Any) -> str: """Return the raw input materialized as text.""" class ReadPort(ABC): """Parse materialized input into records.""" @abstractmethod def read(self, content: str) -> list[dict[str, Any]]: """Return parsed records.""" class NormalizePort(ABC): """Normalize raw records into the target shape.""" @abstractmethod def build(self, records: list[dict[str, Any]]) -> list[dict[str, Any]]: """Return normalized records.""" class ExamplePipelinePort(ABC): """Root of the example family. Notes: The slot annotations below are contracts. A concrete subclass re-annotates them with stage implementations; the DI container instantiates the tree. """ ingest: IngestPort reader: ReadPort normalize: NormalizePort finalize: FinalizePort @abstractmethod async def run(self, source: Any) -> list[dict[str, Any]]: """Run the full stage chain and return the normalized records."""