pipelantic 0.2.0__tar.gz → 0.3.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.
- {pipelantic-0.2.0 → pipelantic-0.3.0}/CHANGELOG.md +39 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/PKG-INFO +12 -11
- {pipelantic-0.2.0 → pipelantic-0.3.0}/README.md +9 -10
- {pipelantic-0.2.0 → pipelantic-0.3.0}/pyproject.toml +6 -1
- pipelantic-0.3.0/src/pipelantic/__init__.py +189 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/_version.py +1 -1
- pipelantic-0.3.0/src/pipelantic/capabilities.py +134 -0
- pipelantic-0.3.0/src/pipelantic/cli/__init__.py +285 -0
- pipelantic-0.3.0/src/pipelantic/cli/__main__.py +6 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/contracts.py +13 -10
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/diagnostics.py +39 -4
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/diff.py +78 -11
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/dtcs.py +17 -1
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/model.py +1 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/pipeline.py +33 -2
- pipelantic-0.3.0/src/pipelantic/plan/__init__.py +34 -0
- pipelantic-0.3.0/src/pipelantic/plan/artifacts.py +77 -0
- pipelantic-0.3.0/src/pipelantic/plan/explain.py +52 -0
- pipelantic-0.3.0/src/pipelantic/plan/model.py +315 -0
- pipelantic-0.3.0/src/pipelantic/plan/planner.py +570 -0
- pipelantic-0.3.0/src/pipelantic/plan/regions.py +73 -0
- pipelantic-0.3.0/src/pipelantic/plan/serialize.py +73 -0
- pipelantic-0.3.0/src/pipelantic/plan/slicing.py +81 -0
- pipelantic-0.3.0/src/pipelantic/policy.py +76 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/ports.py +23 -3
- pipelantic-0.3.0/src/pipelantic/profile.py +157 -0
- pipelantic-0.3.0/src/pipelantic/registry.py +215 -0
- pipelantic-0.3.0/src/pipelantic/reliability.py +277 -0
- pipelantic-0.3.0/src/pipelantic/schema_drift.py +345 -0
- pipelantic-0.3.0/src/pipelantic/schemas/__init__.py +32 -0
- pipelantic-0.3.0/src/pipelantic/schemas/pipeline-plan.schema.json +40 -0
- pipelantic-0.3.0/src/pipelantic/schemas/profile.schema.json +33 -0
- pipelantic-0.3.0/src/pipelantic/schemas/project-config.schema.json +16 -0
- pipelantic-0.3.0/src/pipelantic/secrets.py +40 -0
- pipelantic-0.3.0/src/pipelantic/symbols.py +117 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/transformation.py +9 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/validation.py +334 -10
- pipelantic-0.3.0/tests/cli/test_cli.py +68 -0
- pipelantic-0.3.0/tests/fixtures/sample_pipeline.py +18 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/interchange/test_regression_p0_2.py +4 -3
- pipelantic-0.3.0/tests/interchange/test_regression_p0_3.py +103 -0
- pipelantic-0.3.0/tests/model/__init__.py +0 -0
- pipelantic-0.3.0/tests/plan/__init__.py +0 -0
- pipelantic-0.3.0/tests/plan/test_planner.py +231 -0
- pipelantic-0.3.0/tests/plan/test_serialize.py +69 -0
- pipelantic-0.3.0/tests/profile/__init__.py +0 -0
- pipelantic-0.3.0/tests/profile/test_profile.py +41 -0
- pipelantic-0.3.0/tests/reliability/__init__.py +0 -0
- pipelantic-0.3.0/tests/reliability/test_reliability.py +27 -0
- pipelantic-0.3.0/tests/schema_drift/__init__.py +0 -0
- pipelantic-0.3.0/tests/schema_drift/test_schema_drift.py +53 -0
- pipelantic-0.3.0/tests/unit/__init__.py +0 -0
- pipelantic-0.3.0/tests/unit/test_data_facade.py +44 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/unit/test_package.py +5 -2
- pipelantic-0.3.0/tests/validation/__init__.py +0 -0
- pipelantic-0.3.0/tests/validation/test_phases.py +139 -0
- pipelantic-0.2.0/src/pipelantic/__init__.py +0 -81
- {pipelantic-0.2.0 → pipelantic-0.3.0}/.gitignore +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/LICENSE +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/SECURITY.md +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/exceptions.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/identity.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/inspection.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/__init__.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/bundle.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/diagnostics.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/dpcs.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/normalize.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/odcs.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/policy.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/provenance.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/interchange/security.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/mermaid.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/py.typed +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/src/pipelantic/refs.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/__init__.py +0 -0
- {pipelantic-0.2.0/tests/model → pipelantic-0.3.0/tests/cli}/__init__.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/conftest.py +0 -0
- {pipelantic-0.2.0/tests/unit → pipelantic-0.3.0/tests/fixtures}/__init__.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/interchange/test_acceptance_0_2.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/model/test_acceptance.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/unit/test_diagnostics.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/unit/test_graph_validation.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/unit/test_identity.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/unit/test_regression_p0.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/unit/test_subpipeline.py +0 -0
- {pipelantic-0.2.0 → pipelantic-0.3.0}/tests/unit/test_transformation.py +0 -0
|
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.0] - 2026-07-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `Data` as the preferred thin facade over ContractModel (`DataContractModel`
|
|
13
|
+
remains as a deprecated alias)
|
|
14
|
+
- Multi-phase validation (structural, reference, semantic, policy, capability)
|
|
15
|
+
with diagnostic actions and source symbols
|
|
16
|
+
- `Profile` templates, `SecretRef`, scoped registries, and capability negotiation
|
|
17
|
+
- Immutable secret-free `PipelinePlan` IR (`pipelantic.plan/1`) with slicing,
|
|
18
|
+
explain output, and canonical fingerprints
|
|
19
|
+
- Schema-drift models (`NormalizedSchema`, observations, changes, `DriftImpact`)
|
|
20
|
+
- Portable reliability/intent models (freshness, write/materialization intents,
|
|
21
|
+
idempotency, evidence schemas)
|
|
22
|
+
- JSON Schemas for profiles, project config, and PipelinePlan
|
|
23
|
+
- `pipelantic` CLI: `validate`, `inspect`, `plan`, and `plan explain`
|
|
24
|
+
(plus `plan --explain` alias, `--nodes`, and mutual exclusion for
|
|
25
|
+
`--run-one` / `--run-until`)
|
|
26
|
+
- `Pipeline.plan()` / `Pipeline.explain_plan()`
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `run_until` selects declaration-order prefix (not only upstream closure)
|
|
31
|
+
- Unknown plan selections fail closed with `PMPLAN501`
|
|
32
|
+
- Multi-engine regions and cross-engine durable artifact strategies
|
|
33
|
+
- Profile snapshot included in plan fingerprint
|
|
34
|
+
- Strict policy requires registered transformation implementations
|
|
35
|
+
- Subpipeline validation inherits parent planning context
|
|
36
|
+
- Plan JSON round-trip restores `secret_ref` and verifies fingerprints
|
|
37
|
+
- Schema-only DTCS ports map `datetime`/`decimal`/`binary` correctly
|
|
38
|
+
- Diff APIs return diagnostics for malformed toolkit input
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- Package version and public status advance to 0.3 (Validation and Pipeline Plan IR)
|
|
43
|
+
- Explicit `click` dependency for the CLI plan command group (Typer 0.15+ no
|
|
44
|
+
longer pulls it in transitively)
|
|
45
|
+
|
|
8
46
|
## [0.2.0] - 2026-07-16
|
|
9
47
|
|
|
10
48
|
### Added
|
|
@@ -48,5 +86,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
48
86
|
- uv + ruff toolchain, MkDocs documentation site, shared GitHub Actions
|
|
49
87
|
checks, and tag-triggered PyPI release
|
|
50
88
|
|
|
89
|
+
[0.3.0]: https://github.com/eddiethedean/pipelantic/releases/tag/v0.3.0
|
|
51
90
|
[0.2.0]: https://github.com/eddiethedean/pipelantic/releases/tag/v0.2.0
|
|
52
91
|
[0.1.0]: https://github.com/eddiethedean/pipelantic/releases/tag/v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pipelantic
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Typed, contract-driven data pipeline modeling for Python.
|
|
5
5
|
Project-URL: Homepage, https://github.com/eddiethedean/pipelantic
|
|
6
6
|
Project-URL: Documentation, https://github.com/eddiethedean/pipelantic/tree/main/docs
|
|
@@ -20,11 +20,13 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
22
|
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: click<9,>=8
|
|
23
24
|
Requires-Dist: contractmodel>=0.1.2
|
|
24
25
|
Requires-Dist: dpcs<1,>=0.13
|
|
25
26
|
Requires-Dist: dtcs<1,>=0.11
|
|
26
27
|
Requires-Dist: packaging>=24
|
|
27
28
|
Requires-Dist: pydantic<3,>=2.12
|
|
29
|
+
Requires-Dist: typer<1,>=0.15
|
|
28
30
|
Description-Content-Type: text/markdown
|
|
29
31
|
|
|
30
32
|

|
|
@@ -40,11 +42,11 @@ Typed, contract-driven data pipeline modeling for Python.
|
|
|
40
42
|
|
|
41
43
|
## Status
|
|
42
44
|
|
|
43
|
-
**0.
|
|
45
|
+
**0.3.0 — Validation and Pipeline Plan IR**
|
|
44
46
|
|
|
45
|
-
Pipelantic provides the typed modeling kernel
|
|
46
|
-
|
|
47
|
-
execution
|
|
47
|
+
Pipelantic provides the typed modeling kernel, contract interoperability, and
|
|
48
|
+
an immutable secret-free `PipelinePlan`. Planning is pure (no secret
|
|
49
|
+
resolution or execution). Local runtime arrives in later milestones.
|
|
48
50
|
|
|
49
51
|
See the [hosted documentation](https://pipelantic.readthedocs.io/) for the
|
|
50
52
|
full design,
|
|
@@ -78,8 +80,8 @@ the `dev` dependency group (pytest, ruff, mkdocs) by default.
|
|
|
78
80
|
Tag a version that matches `src/pipelantic/_version.py`, then push the tag:
|
|
79
81
|
|
|
80
82
|
```bash
|
|
81
|
-
git tag v0.
|
|
82
|
-
git push origin v0.
|
|
83
|
+
git tag v0.3.0
|
|
84
|
+
git push origin v0.3.0
|
|
83
85
|
```
|
|
84
86
|
|
|
85
87
|
GitHub Actions runs checks and publishes to PyPI using the `PYPI_API_TOKEN`
|
|
@@ -88,17 +90,16 @@ repository secret.
|
|
|
88
90
|
## Quick example
|
|
89
91
|
|
|
90
92
|
```python
|
|
91
|
-
from
|
|
92
|
-
from pipelantic import Input, Output, Pipeline, Sink, Source, Transformation
|
|
93
|
+
from pipelantic import Data, Input, Output, Pipeline, Sink, Source, Transformation
|
|
93
94
|
|
|
94
95
|
|
|
95
|
-
class RawCustomer(
|
|
96
|
+
class RawCustomer(Data):
|
|
96
97
|
customer_id: int
|
|
97
98
|
first_name: str
|
|
98
99
|
last_name: str
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
class Customer(
|
|
102
|
+
class Customer(Data):
|
|
102
103
|
customer_id: int
|
|
103
104
|
full_name: str
|
|
104
105
|
|
|
@@ -11,11 +11,11 @@ Typed, contract-driven data pipeline modeling for Python.
|
|
|
11
11
|
|
|
12
12
|
## Status
|
|
13
13
|
|
|
14
|
-
**0.
|
|
14
|
+
**0.3.0 — Validation and Pipeline Plan IR**
|
|
15
15
|
|
|
16
|
-
Pipelantic provides the typed modeling kernel
|
|
17
|
-
|
|
18
|
-
execution
|
|
16
|
+
Pipelantic provides the typed modeling kernel, contract interoperability, and
|
|
17
|
+
an immutable secret-free `PipelinePlan`. Planning is pure (no secret
|
|
18
|
+
resolution or execution). Local runtime arrives in later milestones.
|
|
19
19
|
|
|
20
20
|
See the [hosted documentation](https://pipelantic.readthedocs.io/) for the
|
|
21
21
|
full design,
|
|
@@ -49,8 +49,8 @@ the `dev` dependency group (pytest, ruff, mkdocs) by default.
|
|
|
49
49
|
Tag a version that matches `src/pipelantic/_version.py`, then push the tag:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
git tag v0.
|
|
53
|
-
git push origin v0.
|
|
52
|
+
git tag v0.3.0
|
|
53
|
+
git push origin v0.3.0
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
GitHub Actions runs checks and publishes to PyPI using the `PYPI_API_TOKEN`
|
|
@@ -59,17 +59,16 @@ repository secret.
|
|
|
59
59
|
## Quick example
|
|
60
60
|
|
|
61
61
|
```python
|
|
62
|
-
from
|
|
63
|
-
from pipelantic import Input, Output, Pipeline, Sink, Source, Transformation
|
|
62
|
+
from pipelantic import Data, Input, Output, Pipeline, Sink, Source, Transformation
|
|
64
63
|
|
|
65
64
|
|
|
66
|
-
class RawCustomer(
|
|
65
|
+
class RawCustomer(Data):
|
|
67
66
|
customer_id: int
|
|
68
67
|
first_name: str
|
|
69
68
|
last_name: str
|
|
70
69
|
|
|
71
70
|
|
|
72
|
-
class Customer(
|
|
71
|
+
class Customer(Data):
|
|
73
72
|
customer_id: int
|
|
74
73
|
full_name: str
|
|
75
74
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pipelantic"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Typed, contract-driven data pipeline modeling for Python."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -23,8 +23,13 @@ dependencies = [
|
|
|
23
23
|
"packaging>=24",
|
|
24
24
|
"dtcs>=0.11,<1",
|
|
25
25
|
"dpcs>=0.13,<1",
|
|
26
|
+
"typer>=0.15,<1",
|
|
27
|
+
"click>=8,<9",
|
|
26
28
|
]
|
|
27
29
|
|
|
30
|
+
[project.scripts]
|
|
31
|
+
pipelantic = "pipelantic.cli:run"
|
|
32
|
+
|
|
28
33
|
[project.urls]
|
|
29
34
|
Homepage = "https://github.com/eddiethedean/pipelantic"
|
|
30
35
|
Documentation = "https://github.com/eddiethedean/pipelantic/tree/main/docs"
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""Pipelantic — typed, contract-driven data pipeline modeling.
|
|
2
|
+
|
|
3
|
+
0.3 adds multi-phase validation, profiles, SecretRef, scoped registries,
|
|
4
|
+
schema-drift and reliability models, and an immutable PipelinePlan IR.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import warnings
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from pipelantic._version import __version__
|
|
13
|
+
from pipelantic.capabilities import CapabilityDecision, PluginCapabilities
|
|
14
|
+
from pipelantic.contracts import Data, load_data_contract, write_odcs
|
|
15
|
+
from pipelantic.diagnostics import (
|
|
16
|
+
Diagnostic,
|
|
17
|
+
DiagnosticAction,
|
|
18
|
+
Severity,
|
|
19
|
+
SourceLocation,
|
|
20
|
+
ValidationReport,
|
|
21
|
+
)
|
|
22
|
+
from pipelantic.exceptions import (
|
|
23
|
+
ModelDefinitionError,
|
|
24
|
+
PipelanticError,
|
|
25
|
+
PipelineValidationError,
|
|
26
|
+
)
|
|
27
|
+
from pipelantic.interchange import (
|
|
28
|
+
ArtifactProvenance,
|
|
29
|
+
ContractBundle,
|
|
30
|
+
ProvenanceKind,
|
|
31
|
+
diff_data_contracts,
|
|
32
|
+
diff_pipelines,
|
|
33
|
+
diff_transformations,
|
|
34
|
+
generate_contracts,
|
|
35
|
+
graphs_equivalent,
|
|
36
|
+
load_bundle,
|
|
37
|
+
normalize_pipeline,
|
|
38
|
+
write_contracts,
|
|
39
|
+
)
|
|
40
|
+
from pipelantic.model import Edge, LogicalGraph, Node, NodeKind
|
|
41
|
+
from pipelantic.pipeline import Pipeline, Sink, Source, SubpipelineInstance
|
|
42
|
+
from pipelantic.plan import (
|
|
43
|
+
ArtifactRef,
|
|
44
|
+
ArtifactStrategy,
|
|
45
|
+
PipelinePlan,
|
|
46
|
+
explain_plan,
|
|
47
|
+
plan_pipeline,
|
|
48
|
+
)
|
|
49
|
+
from pipelantic.policy import ValidationPolicy
|
|
50
|
+
from pipelantic.ports import Input, Output, Parameter
|
|
51
|
+
from pipelantic.profile import (
|
|
52
|
+
Profile,
|
|
53
|
+
development_profile,
|
|
54
|
+
load_profile,
|
|
55
|
+
production_profile,
|
|
56
|
+
resolve_profile,
|
|
57
|
+
test_profile,
|
|
58
|
+
write_profile,
|
|
59
|
+
)
|
|
60
|
+
from pipelantic.refs import OutputRef
|
|
61
|
+
from pipelantic.registry import (
|
|
62
|
+
BindingDescriptor,
|
|
63
|
+
ImplementationDescriptor,
|
|
64
|
+
PlanningContext,
|
|
65
|
+
PluginDescriptor,
|
|
66
|
+
RegistryBundle,
|
|
67
|
+
builtin_stub_registry,
|
|
68
|
+
)
|
|
69
|
+
from pipelantic.reliability import (
|
|
70
|
+
BackfillDeclaration,
|
|
71
|
+
FreshnessExpectation,
|
|
72
|
+
IdempotencyDeclaration,
|
|
73
|
+
MaterializationIntent,
|
|
74
|
+
MaterializationMode,
|
|
75
|
+
PartitionCompletenessExpectation,
|
|
76
|
+
ReconciliationDeclaration,
|
|
77
|
+
ReliabilityEvidence,
|
|
78
|
+
RepairDeclaration,
|
|
79
|
+
RetrySafetyDeclaration,
|
|
80
|
+
WriteIntent,
|
|
81
|
+
WriteMode,
|
|
82
|
+
)
|
|
83
|
+
from pipelantic.schema_drift import (
|
|
84
|
+
DriftImpact,
|
|
85
|
+
NormalizedSchema,
|
|
86
|
+
SchemaChange,
|
|
87
|
+
SchemaChangeSet,
|
|
88
|
+
SchemaObservation,
|
|
89
|
+
diff_contract_schemas,
|
|
90
|
+
diff_normalized_schemas,
|
|
91
|
+
normalize_schema_from_model,
|
|
92
|
+
)
|
|
93
|
+
from pipelantic.secrets import SecretRef
|
|
94
|
+
from pipelantic.transformation import ImplementationRecord, Step, Transformation
|
|
95
|
+
|
|
96
|
+
__all__ = [
|
|
97
|
+
"ArtifactProvenance",
|
|
98
|
+
"ArtifactRef",
|
|
99
|
+
"ArtifactStrategy",
|
|
100
|
+
"BackfillDeclaration",
|
|
101
|
+
"BindingDescriptor",
|
|
102
|
+
"CapabilityDecision",
|
|
103
|
+
"ContractBundle",
|
|
104
|
+
"Data",
|
|
105
|
+
"DataContractModel",
|
|
106
|
+
"Diagnostic",
|
|
107
|
+
"DiagnosticAction",
|
|
108
|
+
"DriftImpact",
|
|
109
|
+
"Edge",
|
|
110
|
+
"FreshnessExpectation",
|
|
111
|
+
"IdempotencyDeclaration",
|
|
112
|
+
"ImplementationDescriptor",
|
|
113
|
+
"ImplementationRecord",
|
|
114
|
+
"Input",
|
|
115
|
+
"LogicalGraph",
|
|
116
|
+
"MaterializationIntent",
|
|
117
|
+
"MaterializationMode",
|
|
118
|
+
"ModelDefinitionError",
|
|
119
|
+
"Node",
|
|
120
|
+
"NodeKind",
|
|
121
|
+
"NormalizedSchema",
|
|
122
|
+
"Output",
|
|
123
|
+
"OutputRef",
|
|
124
|
+
"Parameter",
|
|
125
|
+
"PartitionCompletenessExpectation",
|
|
126
|
+
"PipelanticError",
|
|
127
|
+
"Pipeline",
|
|
128
|
+
"PipelinePlan",
|
|
129
|
+
"PipelineValidationError",
|
|
130
|
+
"PlanningContext",
|
|
131
|
+
"PluginCapabilities",
|
|
132
|
+
"PluginDescriptor",
|
|
133
|
+
"Profile",
|
|
134
|
+
"ProvenanceKind",
|
|
135
|
+
"ReconciliationDeclaration",
|
|
136
|
+
"RegistryBundle",
|
|
137
|
+
"ReliabilityEvidence",
|
|
138
|
+
"RepairDeclaration",
|
|
139
|
+
"RetrySafetyDeclaration",
|
|
140
|
+
"SchemaChange",
|
|
141
|
+
"SchemaChangeSet",
|
|
142
|
+
"SchemaObservation",
|
|
143
|
+
"SecretRef",
|
|
144
|
+
"Severity",
|
|
145
|
+
"Sink",
|
|
146
|
+
"Source",
|
|
147
|
+
"SourceLocation",
|
|
148
|
+
"Step",
|
|
149
|
+
"SubpipelineInstance",
|
|
150
|
+
"Transformation",
|
|
151
|
+
"ValidationPolicy",
|
|
152
|
+
"ValidationReport",
|
|
153
|
+
"WriteIntent",
|
|
154
|
+
"WriteMode",
|
|
155
|
+
"__version__",
|
|
156
|
+
"builtin_stub_registry",
|
|
157
|
+
"development_profile",
|
|
158
|
+
"diff_contract_schemas",
|
|
159
|
+
"diff_data_contracts",
|
|
160
|
+
"diff_normalized_schemas",
|
|
161
|
+
"diff_pipelines",
|
|
162
|
+
"diff_transformations",
|
|
163
|
+
"explain_plan",
|
|
164
|
+
"generate_contracts",
|
|
165
|
+
"graphs_equivalent",
|
|
166
|
+
"load_bundle",
|
|
167
|
+
"load_data_contract",
|
|
168
|
+
"load_profile",
|
|
169
|
+
"normalize_pipeline",
|
|
170
|
+
"normalize_schema_from_model",
|
|
171
|
+
"plan_pipeline",
|
|
172
|
+
"production_profile",
|
|
173
|
+
"resolve_profile",
|
|
174
|
+
"test_profile",
|
|
175
|
+
"write_contracts",
|
|
176
|
+
"write_odcs",
|
|
177
|
+
"write_profile",
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def __getattr__(name: str) -> Any:
|
|
182
|
+
if name == "DataContractModel":
|
|
183
|
+
warnings.warn(
|
|
184
|
+
"DataContractModel is deprecated; use pipelantic.Data instead.",
|
|
185
|
+
DeprecationWarning,
|
|
186
|
+
stacklevel=2,
|
|
187
|
+
)
|
|
188
|
+
return Data
|
|
189
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Plugin capability declarations and negotiation results."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import StrEnum
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CapabilityDecision(StrEnum):
|
|
11
|
+
"""Outcome of comparing required vs available capabilities."""
|
|
12
|
+
|
|
13
|
+
SUPPORTED = "supported"
|
|
14
|
+
FALLBACK = "fallback"
|
|
15
|
+
UNSUPPORTED = "unsupported"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True, slots=True)
|
|
19
|
+
class PluginCapabilities:
|
|
20
|
+
"""Declared capabilities of a plugin or engine."""
|
|
21
|
+
|
|
22
|
+
engine: str
|
|
23
|
+
async_execution: bool = False
|
|
24
|
+
streaming: bool = False
|
|
25
|
+
transactions: bool = False
|
|
26
|
+
checkpoints: bool = False
|
|
27
|
+
sql: bool = False
|
|
28
|
+
spark: bool = False
|
|
29
|
+
dataframe: bool = True
|
|
30
|
+
secret_provider: bool = False
|
|
31
|
+
extras: frozenset[str] = field(default_factory=frozenset)
|
|
32
|
+
|
|
33
|
+
def supports(self, requirement: str) -> bool:
|
|
34
|
+
"""Return True when this capability set covers ``requirement``."""
|
|
35
|
+
known = {
|
|
36
|
+
"async": self.async_execution,
|
|
37
|
+
"async_execution": self.async_execution,
|
|
38
|
+
"streaming": self.streaming,
|
|
39
|
+
"transactions": self.transactions,
|
|
40
|
+
"checkpoints": self.checkpoints,
|
|
41
|
+
"sql": self.sql,
|
|
42
|
+
"spark": self.spark,
|
|
43
|
+
"dataframe": self.dataframe,
|
|
44
|
+
"secret_provider": self.secret_provider,
|
|
45
|
+
}
|
|
46
|
+
if requirement in known:
|
|
47
|
+
return known[requirement]
|
|
48
|
+
return requirement in self.extras
|
|
49
|
+
|
|
50
|
+
def to_dict(self) -> dict[str, Any]:
|
|
51
|
+
"""Serialize capabilities."""
|
|
52
|
+
return {
|
|
53
|
+
"engine": self.engine,
|
|
54
|
+
"async_execution": self.async_execution,
|
|
55
|
+
"streaming": self.streaming,
|
|
56
|
+
"transactions": self.transactions,
|
|
57
|
+
"checkpoints": self.checkpoints,
|
|
58
|
+
"sql": self.sql,
|
|
59
|
+
"spark": self.spark,
|
|
60
|
+
"dataframe": self.dataframe,
|
|
61
|
+
"secret_provider": self.secret_provider,
|
|
62
|
+
"extras": sorted(self.extras),
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True, slots=True)
|
|
67
|
+
class CapabilityNegotiation:
|
|
68
|
+
"""Record of a capability check for one requirement."""
|
|
69
|
+
|
|
70
|
+
requirement: str
|
|
71
|
+
engine: str
|
|
72
|
+
decision: CapabilityDecision
|
|
73
|
+
fallback_engine: str | None = None
|
|
74
|
+
message: str | None = None
|
|
75
|
+
|
|
76
|
+
def to_dict(self) -> dict[str, Any]:
|
|
77
|
+
"""Serialize negotiation record."""
|
|
78
|
+
return {
|
|
79
|
+
"requirement": self.requirement,
|
|
80
|
+
"engine": self.engine,
|
|
81
|
+
"decision": self.decision.value,
|
|
82
|
+
"fallback_engine": self.fallback_engine,
|
|
83
|
+
"message": self.message,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def negotiate_capabilities(
|
|
88
|
+
*,
|
|
89
|
+
requirements: list[str],
|
|
90
|
+
available: PluginCapabilities,
|
|
91
|
+
fallback: PluginCapabilities | None = None,
|
|
92
|
+
allow_fallback: bool = False,
|
|
93
|
+
) -> list[CapabilityNegotiation]:
|
|
94
|
+
"""Negotiate required capabilities against an available engine.
|
|
95
|
+
|
|
96
|
+
Unsupported requirements fail closed unless ``allow_fallback`` is True and
|
|
97
|
+
a fallback engine covers the requirement.
|
|
98
|
+
"""
|
|
99
|
+
results: list[CapabilityNegotiation] = []
|
|
100
|
+
for requirement in requirements:
|
|
101
|
+
if available.supports(requirement):
|
|
102
|
+
results.append(
|
|
103
|
+
CapabilityNegotiation(
|
|
104
|
+
requirement=requirement,
|
|
105
|
+
engine=available.engine,
|
|
106
|
+
decision=CapabilityDecision.SUPPORTED,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
continue
|
|
110
|
+
if allow_fallback and fallback is not None and fallback.supports(requirement):
|
|
111
|
+
results.append(
|
|
112
|
+
CapabilityNegotiation(
|
|
113
|
+
requirement=requirement,
|
|
114
|
+
engine=available.engine,
|
|
115
|
+
decision=CapabilityDecision.FALLBACK,
|
|
116
|
+
fallback_engine=fallback.engine,
|
|
117
|
+
message=(
|
|
118
|
+
f"Requirement {requirement!r} unsupported by "
|
|
119
|
+
f"{available.engine}; using fallback {fallback.engine}."
|
|
120
|
+
),
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
continue
|
|
124
|
+
results.append(
|
|
125
|
+
CapabilityNegotiation(
|
|
126
|
+
requirement=requirement,
|
|
127
|
+
engine=available.engine,
|
|
128
|
+
decision=CapabilityDecision.UNSUPPORTED,
|
|
129
|
+
message=(
|
|
130
|
+
f"Requirement {requirement!r} unsupported by {available.engine}."
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
return results
|