pipelantic 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. pipelantic-0.1.0/.gitignore +20 -0
  2. pipelantic-0.1.0/CHANGELOG.md +23 -0
  3. pipelantic-0.1.0/LICENSE +21 -0
  4. pipelantic-0.1.0/PKG-INFO +134 -0
  5. pipelantic-0.1.0/README.md +107 -0
  6. pipelantic-0.1.0/SECURITY.md +57 -0
  7. pipelantic-0.1.0/pyproject.toml +105 -0
  8. pipelantic-0.1.0/src/pipelantic/__init__.py +49 -0
  9. pipelantic-0.1.0/src/pipelantic/_version.py +3 -0
  10. pipelantic-0.1.0/src/pipelantic/contracts.py +42 -0
  11. pipelantic-0.1.0/src/pipelantic/diagnostics.py +89 -0
  12. pipelantic-0.1.0/src/pipelantic/exceptions.py +28 -0
  13. pipelantic-0.1.0/src/pipelantic/identity.py +47 -0
  14. pipelantic-0.1.0/src/pipelantic/inspection.py +18 -0
  15. pipelantic-0.1.0/src/pipelantic/mermaid.py +44 -0
  16. pipelantic-0.1.0/src/pipelantic/model.py +105 -0
  17. pipelantic-0.1.0/src/pipelantic/pipeline.py +723 -0
  18. pipelantic-0.1.0/src/pipelantic/ports.py +91 -0
  19. pipelantic-0.1.0/src/pipelantic/py.typed +0 -0
  20. pipelantic-0.1.0/src/pipelantic/refs.py +65 -0
  21. pipelantic-0.1.0/src/pipelantic/transformation.py +289 -0
  22. pipelantic-0.1.0/src/pipelantic/validation.py +406 -0
  23. pipelantic-0.1.0/tests/__init__.py +0 -0
  24. pipelantic-0.1.0/tests/conftest.py +31 -0
  25. pipelantic-0.1.0/tests/model/__init__.py +0 -0
  26. pipelantic-0.1.0/tests/model/test_acceptance.py +142 -0
  27. pipelantic-0.1.0/tests/unit/__init__.py +0 -0
  28. pipelantic-0.1.0/tests/unit/test_diagnostics.py +27 -0
  29. pipelantic-0.1.0/tests/unit/test_graph_validation.py +72 -0
  30. pipelantic-0.1.0/tests/unit/test_identity.py +32 -0
  31. pipelantic-0.1.0/tests/unit/test_package.py +19 -0
  32. pipelantic-0.1.0/tests/unit/test_regression_p0.py +175 -0
  33. pipelantic-0.1.0/tests/unit/test_subpipeline.py +48 -0
  34. pipelantic-0.1.0/tests/unit/test_transformation.py +49 -0
@@ -0,0 +1,20 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ .eggs/
7
+ dist/
8
+ build/
9
+ site/
10
+ .venv/
11
+ venv/
12
+ .env
13
+
14
+ # Tools
15
+ .pytest_cache/
16
+ .ruff_cache/
17
+ .mypy_cache/
18
+ .coverage
19
+ htmlcov/
20
+ .DS_Store
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-07-16
9
+
10
+ ### Added
11
+
12
+ - First public release as **Pipelantic** (PyPI package `pipelantic`)
13
+ - Typed modeling kernel for authoring pipelines without an execution backend
14
+ - `Transformation`, `Input`, `Output`, and `Parameter` port annotations
15
+ - `Pipeline`, `Source`, `Step`, `Sink`, and subpipeline composition
16
+ - Typed `OutputRef` wiring with stable node and port identities
17
+ - Structural validation diagnostics (cycles, missing refs, incompatible ports)
18
+ - Logical graph inspection and Mermaid diagram generation
19
+ - ContractModel integration boundary via `DataContractModel` alias
20
+ - uv + ruff toolchain, MkDocs documentation site, shared GitHub Actions
21
+ checks, and tag-triggered PyPI release
22
+
23
+ [0.1.0]: https://github.com/eddiethedean/pipelantic/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Odo Matthews
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: pipelantic
3
+ Version: 0.1.0
4
+ Summary: Typed, contract-driven data pipeline modeling for Python.
5
+ Project-URL: Homepage, https://github.com/eddiethedean/pipelantic
6
+ Project-URL: Documentation, https://github.com/eddiethedean/pipelantic/tree/main/docs
7
+ Project-URL: Repository, https://github.com/eddiethedean/pipelantic
8
+ Project-URL: Issues, https://github.com/eddiethedean/pipelantic/issues
9
+ Project-URL: Changelog, https://github.com/eddiethedean/pipelantic/blob/main/CHANGELOG.md
10
+ Author-email: Odo Matthews <odosmatthews@gmail.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: data-contracts,etl,pipelines,pydantic,typed
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: contractmodel>=0.1.2
24
+ Requires-Dist: packaging>=24
25
+ Requires-Dist: pydantic<3,>=2.12
26
+ Description-Content-Type: text/markdown
27
+
28
+ ![Pipelantic banner](docs/theme/assets/pipelantic-banner.png)
29
+
30
+ # Pipelantic
31
+
32
+ [![Documentation Status](https://readthedocs.org/projects/pipelantic/badge/?version=latest)](https://pipelantic.readthedocs.io/en/latest/?badge=latest)
33
+
34
+ Typed, contract-driven data pipeline modeling for Python.
35
+
36
+ > Define data, transformations, and pipelines with typed Python classes.
37
+ > Validate and plan them once. Execute them through interchangeable backends.
38
+
39
+ ## Status
40
+
41
+ **0.1.0 — Typed Modeling Kernel**
42
+
43
+ Pipelantic currently provides the authoring model, logical graph construction,
44
+ topology and compatibility diagnostics, graph inspection, and Mermaid output.
45
+ Planning, execution plugins, and contract serialization arrive in later
46
+ milestones.
47
+
48
+ See the [hosted documentation](https://pipelantic.readthedocs.io/) for the
49
+ full design,
50
+ [CHANGELOG.md](CHANGELOG.md) for release notes, and
51
+ [Roadmap](docs/11_DEVELOPMENT/ROADMAP.md) for sequencing.
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ pip install pipelantic
57
+ # or
58
+ uv add pipelantic
59
+ ```
60
+
61
+ ### Development
62
+
63
+ Requires [uv](https://docs.astral.sh/uv/).
64
+
65
+ ```bash
66
+ uv sync
67
+ uv run pytest
68
+ uv run ruff check .
69
+ uv run ruff format .
70
+ ```
71
+
72
+ `uv sync` creates `.venv`, installs the package in editable mode, and installs
73
+ the `dev` dependency group (pytest, ruff, mkdocs) by default.
74
+
75
+ ## Release
76
+
77
+ Tag a version that matches `src/pipelantic/_version.py`, then push the tag:
78
+
79
+ ```bash
80
+ git tag v0.1.0
81
+ git push origin v0.1.0
82
+ ```
83
+
84
+ GitHub Actions runs checks and publishes to PyPI using the `PYPI_API_TOKEN`
85
+ repository secret.
86
+
87
+ ## Quick example
88
+
89
+ ```python
90
+ from contractmodel import ContractModel as DataContractModel
91
+ from pipelantic import Input, Output, Pipeline, Sink, Source, Transformation
92
+
93
+
94
+ class RawCustomer(DataContractModel):
95
+ customer_id: int
96
+ first_name: str
97
+ last_name: str
98
+
99
+
100
+ class Customer(DataContractModel):
101
+ customer_id: int
102
+ full_name: str
103
+
104
+
105
+ class NormalizeCustomers(Transformation):
106
+ customers: Input[RawCustomer]
107
+ result: Output[Customer]
108
+
109
+
110
+ class CustomerPipeline(Pipeline):
111
+ raw: Source[RawCustomer] = Source(binding="customer_source")
112
+ normalized = NormalizeCustomers.step(customers=raw)
113
+ curated: Sink[Customer] = Sink(
114
+ input=normalized.result,
115
+ binding="customer_sink",
116
+ )
117
+
118
+
119
+ graph = CustomerPipeline.inspect()
120
+ report = CustomerPipeline.validate()
121
+ print(CustomerPipeline.to_mermaid())
122
+ ```
123
+
124
+ ## Documentation
125
+
126
+ - [Documentation site](https://pipelantic.readthedocs.io/)
127
+ - [Getting Started](docs/01_GETTING_STARTED/README.md)
128
+ - [Core Concepts](docs/02_FOUNDATIONS/CORE_CONCEPTS.md)
129
+ - [Architecture](docs/02_FOUNDATIONS/ARCHITECTURE.md)
130
+ - [Roadmap](docs/11_DEVELOPMENT/ROADMAP.md)
131
+
132
+ ## License
133
+
134
+ MIT
@@ -0,0 +1,107 @@
1
+ ![Pipelantic banner](docs/theme/assets/pipelantic-banner.png)
2
+
3
+ # Pipelantic
4
+
5
+ [![Documentation Status](https://readthedocs.org/projects/pipelantic/badge/?version=latest)](https://pipelantic.readthedocs.io/en/latest/?badge=latest)
6
+
7
+ Typed, contract-driven data pipeline modeling for Python.
8
+
9
+ > Define data, transformations, and pipelines with typed Python classes.
10
+ > Validate and plan them once. Execute them through interchangeable backends.
11
+
12
+ ## Status
13
+
14
+ **0.1.0 — Typed Modeling Kernel**
15
+
16
+ Pipelantic currently provides the authoring model, logical graph construction,
17
+ topology and compatibility diagnostics, graph inspection, and Mermaid output.
18
+ Planning, execution plugins, and contract serialization arrive in later
19
+ milestones.
20
+
21
+ See the [hosted documentation](https://pipelantic.readthedocs.io/) for the
22
+ full design,
23
+ [CHANGELOG.md](CHANGELOG.md) for release notes, and
24
+ [Roadmap](docs/11_DEVELOPMENT/ROADMAP.md) for sequencing.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install pipelantic
30
+ # or
31
+ uv add pipelantic
32
+ ```
33
+
34
+ ### Development
35
+
36
+ Requires [uv](https://docs.astral.sh/uv/).
37
+
38
+ ```bash
39
+ uv sync
40
+ uv run pytest
41
+ uv run ruff check .
42
+ uv run ruff format .
43
+ ```
44
+
45
+ `uv sync` creates `.venv`, installs the package in editable mode, and installs
46
+ the `dev` dependency group (pytest, ruff, mkdocs) by default.
47
+
48
+ ## Release
49
+
50
+ Tag a version that matches `src/pipelantic/_version.py`, then push the tag:
51
+
52
+ ```bash
53
+ git tag v0.1.0
54
+ git push origin v0.1.0
55
+ ```
56
+
57
+ GitHub Actions runs checks and publishes to PyPI using the `PYPI_API_TOKEN`
58
+ repository secret.
59
+
60
+ ## Quick example
61
+
62
+ ```python
63
+ from contractmodel import ContractModel as DataContractModel
64
+ from pipelantic import Input, Output, Pipeline, Sink, Source, Transformation
65
+
66
+
67
+ class RawCustomer(DataContractModel):
68
+ customer_id: int
69
+ first_name: str
70
+ last_name: str
71
+
72
+
73
+ class Customer(DataContractModel):
74
+ customer_id: int
75
+ full_name: str
76
+
77
+
78
+ class NormalizeCustomers(Transformation):
79
+ customers: Input[RawCustomer]
80
+ result: Output[Customer]
81
+
82
+
83
+ class CustomerPipeline(Pipeline):
84
+ raw: Source[RawCustomer] = Source(binding="customer_source")
85
+ normalized = NormalizeCustomers.step(customers=raw)
86
+ curated: Sink[Customer] = Sink(
87
+ input=normalized.result,
88
+ binding="customer_sink",
89
+ )
90
+
91
+
92
+ graph = CustomerPipeline.inspect()
93
+ report = CustomerPipeline.validate()
94
+ print(CustomerPipeline.to_mermaid())
95
+ ```
96
+
97
+ ## Documentation
98
+
99
+ - [Documentation site](https://pipelantic.readthedocs.io/)
100
+ - [Getting Started](docs/01_GETTING_STARTED/README.md)
101
+ - [Core Concepts](docs/02_FOUNDATIONS/CORE_CONCEPTS.md)
102
+ - [Architecture](docs/02_FOUNDATIONS/ARCHITECTURE.md)
103
+ - [Roadmap](docs/11_DEVELOPMENT/ROADMAP.md)
104
+
105
+ ## License
106
+
107
+ MIT
@@ -0,0 +1,57 @@
1
+ # Security Policy
2
+
3
+ Pipelantic 0.1 publishes the typed modeling kernel. Security reports
4
+ concerning the published package, documentation, proposed APIs, repository
5
+ automation, or later milestones are welcome.
6
+
7
+ ## Reporting a Vulnerability
8
+
9
+ Do not open a public issue for:
10
+
11
+ - credential exposure
12
+ - arbitrary code execution
13
+ - unsafe contract or configuration parsing
14
+ - path traversal
15
+ - server-side request forgery
16
+ - plugin supply-chain vulnerabilities
17
+ - injection vulnerabilities
18
+ - cross-tenant artifact or cache exposure
19
+
20
+ Until a private reporting address or GitHub private vulnerability reporting is
21
+ configured, contact the repository owner privately through an available
22
+ verified channel and include:
23
+
24
+ - affected component
25
+ - impact
26
+ - reproduction steps
27
+ - relevant configuration
28
+ - suggested mitigation, when known
29
+
30
+ Do not include production secrets, customer data, or regulated records.
31
+
32
+ ## Supported Versions
33
+
34
+ | Version | Supported |
35
+ |---|---|
36
+ | 0.1.x | Best-effort while the modeling kernel is in alpha |
37
+ | earlier | Not supported |
38
+
39
+ A formal security patch policy will be published before 1.0.
40
+
41
+ ## Security Model
42
+
43
+ The project threat model and production-readiness requirements are documented
44
+ in [docs/02_FOUNDATIONS/SECURITY.md](docs/02_FOUNDATIONS/SECURITY.md).
45
+
46
+ ## Disclosure
47
+
48
+ The project intends to use coordinated disclosure:
49
+
50
+ 1. Confirm receipt privately.
51
+ 2. Validate and assess severity.
52
+ 3. Develop and test a fix.
53
+ 4. Prepare an advisory and upgrade guidance.
54
+ 5. Release the fix before public technical details.
55
+
56
+ Response targets and a formal private reporting mechanism must be established
57
+ before Pipelantic is declared production-ready.
@@ -0,0 +1,105 @@
1
+ [project]
2
+ name = "pipelantic"
3
+ version = "0.1.0"
4
+ description = "Typed, contract-driven data pipeline modeling for Python."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.11"
8
+ authors = [{ name = "Odo Matthews", email = "odosmatthews@gmail.com" }]
9
+ keywords = ["pipelines", "etl", "data-contracts", "typed", "pydantic"]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Intended Audience :: Developers",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Typing :: Typed",
19
+ ]
20
+ dependencies = [
21
+ "contractmodel>=0.1.2",
22
+ "pydantic>=2.12,<3",
23
+ "packaging>=24",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/eddiethedean/pipelantic"
28
+ Documentation = "https://github.com/eddiethedean/pipelantic/tree/main/docs"
29
+ Repository = "https://github.com/eddiethedean/pipelantic"
30
+ Issues = "https://github.com/eddiethedean/pipelantic/issues"
31
+ Changelog = "https://github.com/eddiethedean/pipelantic/blob/main/CHANGELOG.md"
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "mkdocs>=1.6,<2",
36
+ "mkdocs-material>=9.6,<10",
37
+ "mkdocstrings[python]>=0.29,<1",
38
+ "pymdown-extensions>=10.14,<11",
39
+ "pytest>=8,<9",
40
+ "ruff>=0.9,<1",
41
+ ]
42
+
43
+ [build-system]
44
+ requires = ["hatchling"]
45
+ build-backend = "hatchling.build"
46
+
47
+ [tool.hatch.build.targets.wheel]
48
+ packages = ["src/pipelantic"]
49
+
50
+ [tool.hatch.build.targets.sdist]
51
+ include = [
52
+ "/src/pipelantic",
53
+ "/tests",
54
+ "/README.md",
55
+ "/LICENSE",
56
+ "/CHANGELOG.md",
57
+ "/SECURITY.md",
58
+ "/pyproject.toml",
59
+ ]
60
+
61
+ [tool.uv]
62
+ package = true
63
+ default-groups = ["dev"]
64
+
65
+ [tool.pytest.ini_options]
66
+ testpaths = ["tests"]
67
+ pythonpath = ["src"]
68
+
69
+ [tool.ruff]
70
+ line-length = 88
71
+ target-version = "py311"
72
+ src = ["src", "tests"]
73
+ exclude = [
74
+ ".git",
75
+ ".venv",
76
+ ".ruff_cache",
77
+ ".pytest_cache",
78
+ "dist",
79
+ "build",
80
+ ]
81
+
82
+ [tool.ruff.lint]
83
+ select = [
84
+ "E", # pycodestyle errors
85
+ "W", # pycodestyle warnings
86
+ "F", # pyflakes
87
+ "I", # isort
88
+ "UP", # pyupgrade
89
+ "B", # flake8-bugbear
90
+ "SIM", # flake8-simplify
91
+ "RUF", # ruff-specific
92
+ ]
93
+ ignore = [
94
+ "E501", # line length handled by formatter
95
+ ]
96
+
97
+ [tool.ruff.lint.isort]
98
+ known-first-party = ["pipelantic"]
99
+
100
+ [tool.ruff.format]
101
+ quote-style = "double"
102
+ indent-style = "space"
103
+ skip-magic-trailing-comma = false
104
+ line-ending = "auto"
105
+ docstring-code-format = true
@@ -0,0 +1,49 @@
1
+ """Pipelantic — typed, contract-driven data pipeline modeling.
2
+
3
+ 0.1 provides the authoring model, logical graph construction, topology and
4
+ compatibility diagnostics, inspection, and Mermaid output.
5
+
6
+ Data contracts are provided by ContractModel. This package re-exports
7
+ ``DataContractModel`` as an alias of ``contractmodel.ContractModel`` for
8
+ documentation-aligned imports.
9
+ """
10
+
11
+ from pipelantic._version import __version__
12
+ from pipelantic.contracts import DataContractModel
13
+ from pipelantic.diagnostics import Diagnostic, Severity, ValidationReport
14
+ from pipelantic.exceptions import (
15
+ ModelDefinitionError,
16
+ PipelanticError,
17
+ PipelineValidationError,
18
+ )
19
+ from pipelantic.model import Edge, LogicalGraph, Node, NodeKind
20
+ from pipelantic.pipeline import Pipeline, Sink, Source, SubpipelineInstance
21
+ from pipelantic.ports import Input, Output, Parameter
22
+ from pipelantic.refs import OutputRef
23
+ from pipelantic.transformation import ImplementationRecord, Step, Transformation
24
+
25
+ __all__ = [
26
+ "DataContractModel",
27
+ "Diagnostic",
28
+ "Edge",
29
+ "ImplementationRecord",
30
+ "Input",
31
+ "LogicalGraph",
32
+ "ModelDefinitionError",
33
+ "Node",
34
+ "NodeKind",
35
+ "Output",
36
+ "OutputRef",
37
+ "Parameter",
38
+ "PipelanticError",
39
+ "Pipeline",
40
+ "PipelineValidationError",
41
+ "Severity",
42
+ "Sink",
43
+ "Source",
44
+ "Step",
45
+ "SubpipelineInstance",
46
+ "Transformation",
47
+ "ValidationReport",
48
+ "__version__",
49
+ ]
@@ -0,0 +1,3 @@
1
+ """Package version."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,42 @@
1
+ """Data-contract integration boundary for ContractModel.
2
+
3
+ Pipelantic docs refer to ``DataContractModel``. The published ContractModel
4
+ package exposes ``ContractModel`` as the Pydantic authoring base. This module
5
+ aliases that type and provides helpers for identity and compatibility checks.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any, TypeAlias
11
+
12
+ from contractmodel import ContractModel
13
+
14
+ # Docs-aligned alias for the ContractModel Pydantic authoring base.
15
+ DataContractModel: TypeAlias = ContractModel
16
+
17
+ __all__ = [
18
+ "ContractModel",
19
+ "DataContractModel",
20
+ "is_data_contract_type",
21
+ "resolve_contract_type",
22
+ ]
23
+
24
+
25
+ def is_data_contract_type(obj: Any) -> bool:
26
+ """Return True when ``obj`` is a ContractModel-compatible data-contract class."""
27
+ return isinstance(obj, type) and issubclass(obj, ContractModel)
28
+
29
+
30
+ def resolve_contract_type(annotation: Any) -> type[Any] | None:
31
+ """Extract a data-contract class from a type annotation when possible.
32
+
33
+ Returns ``None`` when the annotation is not a concrete ContractModel subclass.
34
+ """
35
+ if is_data_contract_type(annotation):
36
+ return annotation
37
+ origin = getattr(annotation, "__origin__", None)
38
+ if origin is not None:
39
+ args = getattr(annotation, "__args__", ())
40
+ if len(args) == 1 and is_data_contract_type(args[0]):
41
+ return args[0]
42
+ return None
@@ -0,0 +1,89 @@
1
+ """Structured diagnostics and validation reports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable, Sequence
6
+ from dataclasses import dataclass, field
7
+ from enum import StrEnum
8
+ from typing import Any
9
+
10
+ from pipelantic.exceptions import PipelineValidationError
11
+
12
+
13
+ class Severity(StrEnum):
14
+ """Diagnostic severity levels."""
15
+
16
+ ERROR = "error"
17
+ WARNING = "warning"
18
+ INFO = "info"
19
+ HINT = "hint"
20
+
21
+
22
+ @dataclass(frozen=True, slots=True)
23
+ class Diagnostic:
24
+ """A structured finding from loading, inspection, or validation."""
25
+
26
+ code: str
27
+ severity: Severity
28
+ message: str
29
+ path: tuple[str, ...] = ()
30
+ help: str | None = None
31
+ related: tuple[tuple[str, ...], ...] = ()
32
+ metadata: dict[str, Any] = field(default_factory=dict)
33
+
34
+
35
+ @dataclass(frozen=True, slots=True)
36
+ class ValidationReport:
37
+ """Immutable collection of diagnostics for a validation pass."""
38
+
39
+ diagnostics: tuple[Diagnostic, ...] = ()
40
+
41
+ @property
42
+ def valid(self) -> bool:
43
+ """Return True when no error-severity diagnostics are present."""
44
+ return not any(d.severity is Severity.ERROR for d in self.diagnostics)
45
+
46
+ @property
47
+ def errors(self) -> tuple[Diagnostic, ...]:
48
+ """Return error-severity diagnostics."""
49
+ return tuple(d for d in self.diagnostics if d.severity is Severity.ERROR)
50
+
51
+ @property
52
+ def warnings(self) -> tuple[Diagnostic, ...]:
53
+ """Return warning-severity diagnostics."""
54
+ return tuple(d for d in self.diagnostics if d.severity is Severity.WARNING)
55
+
56
+ def filter(
57
+ self,
58
+ *,
59
+ severity: Severity | None = None,
60
+ code: str | None = None,
61
+ ) -> ValidationReport:
62
+ """Return a report containing only matching diagnostics."""
63
+ items = self.diagnostics
64
+ if severity is not None:
65
+ items = tuple(d for d in items if d.severity is severity)
66
+ if code is not None:
67
+ items = tuple(d for d in items if d.code == code)
68
+ return ValidationReport(diagnostics=items)
69
+
70
+ def raise_for_errors(self) -> None:
71
+ """Raise :class:`PipelineValidationError` if the report is invalid."""
72
+ if self.valid:
73
+ return
74
+ count = len(self.errors)
75
+ message = f"Pipeline validation failed with {count} error(s)."
76
+ raise PipelineValidationError(message, report=self)
77
+
78
+ def merge(self, other: ValidationReport) -> ValidationReport:
79
+ """Combine two reports, preserving order and uniqueness by identity."""
80
+ return ValidationReport(diagnostics=self.diagnostics + other.diagnostics)
81
+
82
+ @classmethod
83
+ def from_diagnostics(cls, diagnostics: Iterable[Diagnostic]) -> ValidationReport:
84
+ """Build a report from an iterable of diagnostics."""
85
+ return cls(diagnostics=tuple(diagnostics))
86
+
87
+ def codes(self) -> Sequence[str]:
88
+ """Return diagnostic codes in report order."""
89
+ return tuple(d.code for d in self.diagnostics)
@@ -0,0 +1,28 @@
1
+ """Public exception hierarchy for Pipelantic."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from pipelantic.diagnostics import ValidationReport
9
+
10
+
11
+ class PipelanticError(Exception):
12
+ """Base class for public Pipelantic exceptions."""
13
+
14
+
15
+ class ModelDefinitionError(PipelanticError):
16
+ """Raised when a class definition cannot form a usable model."""
17
+
18
+
19
+ class PipelineValidationError(PipelanticError):
20
+ """Raised when validation fails and the caller requested an exception."""
21
+
22
+ def __init__(self, message: str, *, report: ValidationReport) -> None:
23
+ super().__init__(message)
24
+ self.report = report
25
+
26
+
27
+ class InternalPipelanticError(PipelanticError):
28
+ """Raised when an internal invariant is violated."""