Pipeline¶
Pipeline builds and runs a configured DAG. Extract, transform, join, union,
and load steps are declared in the manifest; connector classes remain
available separately for direct use.
Build and execute¶
from pycharter import Pipeline
pipeline = Pipeline.from_dict({
"name": "users",
"steps": [
{
"id": "source",
"type": "extract",
"source": {"type": "file", "path": "input.json", "format": "json"},
},
{
"id": "sink",
"type": "load",
"input": "source",
"target": {
"type": "file", "path": "output.json", "format": "json",
"mode": "overwrite",
},
},
],
})
result = pipeline.run() # Synchronous; call outside a running event loop.
print(f"Loaded {result.rows_loaded} rows")
Inside an async application, use result = await pipeline.arun() instead.
Load a manifest with Pipeline.from_file("pipelines/users/pipeline.yaml"),
or its directory with Pipeline.from_dir("pipelines/users"). Pass declared
variable overrides with variables={"OUTPUT_PATH": "/tmp/users.json"}.
API reference¶
Pipeline
¶
DAG-based ETL pipeline.
Pipelines are constructed from YAML config files or dicts and consist of a directed acyclic graph of steps (extract, transform, join, union, load).
Example::
pipeline = Pipeline.from_file("pipeline.yaml")
result = pipeline.run() # sync
result = await pipeline.arun() # inside a running event loop
Source code in src/pycharter/pipeline_generator/pipeline.py
from_dict
classmethod
¶
from_dict(
raw: dict[str, Any],
*,
variables: dict[str, str] | None = None,
) -> Pipeline
Create a pipeline from a config dict.
Accepts both step-based (steps: [...]) and section-based
(extract/transform/load) configs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
dict[str, Any]
|
Raw config dictionary. |
required |
variables
|
dict[str, str] | None
|
Additional variables. |
None
|
Returns:
| Type | Description |
|---|---|
Pipeline
|
A validated, ready-to-run pipeline. |
Source code in src/pycharter/pipeline_generator/pipeline.py
from_file
classmethod
¶
from_file(
path: str | Path,
*,
variables: dict[str, str] | None = None,
) -> Pipeline
Create a pipeline from a YAML config file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Path to the pipeline YAML file. |
required |
variables
|
dict[str, str] | None
|
Additional variables merged on top of the
file's |
None
|
Returns:
| Type | Description |
|---|---|
Pipeline
|
A validated, ready-to-run pipeline. |
Source code in src/pycharter/pipeline_generator/pipeline.py
from_dir
classmethod
¶
from_dir(
directory: str | Path,
*,
variables: dict[str, str] | None = None,
) -> Pipeline
Create a pipeline from a directory containing pipeline.yaml.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
str | Path
|
Path to the config directory. |
required |
variables
|
dict[str, str] | None
|
Additional variables. |
None
|
Returns:
| Type | Description |
|---|---|
Pipeline
|
A validated, ready-to-run pipeline. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If |
Source code in src/pycharter/pipeline_generator/pipeline.py
from_store
classmethod
¶
from_store(
ref: StoreRef | str,
*,
store: PipelineStoreClient | None = None,
variables: dict[str, str] | None = None,
) -> Pipeline
Create a pipeline from a config stored in a pipeline store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ref
|
StoreRef | str
|
A :class: |
required |
store
|
PipelineStoreClient | None
|
An open :class: |
None
|
variables
|
dict[str, str] | None
|
Additional variables merged on top of the
stored config's |
None
|
Returns:
| Type | Description |
|---|---|
Pipeline
|
A validated, ready-to-run pipeline. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If no config exists for |
Source code in src/pycharter/pipeline_generator/pipeline.py
preflight
¶
Run checks without source or destination connector I/O.
Walks every step's optional contract: block, resolves the
referenced contract (file path or contract-store ref), and
collects any resolution errors into a :class:PreflightResult.
The resolved contexts are then checked for privacy-clearance
problems. Extractors and loaders are not constructed; no records
flow.
Contract files and configured contract stores are read. For offline
validation, use file refs without settings.contract_store.
Use as a CI / orchestrator gate to fail before starting an actual run when contract refs are broken — typical signal:
result = pipeline.preflight()
if not result.success:
for issue in result.issues:
print(f"{issue.step_id}: {issue.message}")
sys.exit(1)
:meth:arun runs the same pass before the first step, so a
pipeline never starts in a state preflight would reject.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
privacy_secret
|
str | None
|
The org secret the run will use, if the
caller passes one explicitly. Honoured by the
|
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
PreflightResult
|
class: |
PreflightResult
|
per-step :class: |
Source code in src/pycharter/pipeline_generator/pipeline.py
run
¶
run(
*,
run_id: str | None = None,
fail_fast: bool | None = None,
on_error: str | None = None,
privacy_secret: str | None = None,
) -> PipelineResult
Execute the pipeline synchronously.
Convenience wrapper that calls asyncio.run() on the async
:meth:arun method (see it for the on_error / fail_fast /
privacy_secret semantics). Unusable inside a running event
loop — await :meth:arun there instead.
Source code in src/pycharter/pipeline_generator/pipeline.py
arun
async
¶
arun(
*,
run_id: str | None = None,
fail_fast: bool | None = None,
on_error: str | None = None,
progress: RunProgressEmitter | None = None,
extraction_sink_factory: Any | None = None,
grounded_emitter_factory: Any | None = None,
dry_run: bool = False,
io: PipelineIO | None = None,
privacy_secret: str | None = None,
) -> PipelineResult
Execute the pipeline (async; awaitable inside a running loop).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run_id
|
str | None
|
Optional identifier for this run. |
None
|
privacy_secret
|
str | None
|
Explicit org secret used to tokenize |
None
|
fail_fast
|
bool | None
|
Abort on the first step failure. Soft-deprecated —
prefer |
None
|
on_error
|
str | None
|
Run-level failure behaviour using the shared
vocabulary of step |
None
|
progress
|
RunProgressEmitter | None
|
Optional Phase-4 live-progress emitter. When
supplied, the runner publishes |
None
|
extraction_sink_factory
|
Any | None
|
Optional Theme-D production hook. A
callable that, given a :class: |
None
|
grounded_emitter_factory
|
Any | None
|
Optional KB-enrichment hook. A
callable that, given a :class: |
None
|
dry_run
|
bool
|
Read sources and run transforms and contract checks, suppressing normal and quarantine destination writes. This is an extraction preview, not offline planning or a sandbox for retained Python. Lineage/progress may still emit events. |
False
|
io
|
PipelineIO | None
|
Explicit run-local connector substitutions keyed by step ID.
Missing substitutions fail without configured fallbacks.
Auxiliary configured I/O and callbacks are rejected. See
:class: |
None
|
Returns:
| Type | Description |
|---|---|
PipelineResult
|
Aggregated pipeline result with per-step results. When |
PipelineResult
|
preflight reports errors, a failed result carrying those |
PipelineResult
|
messages is returned and no step runs. |
Source code in src/pycharter/pipeline_generator/pipeline.py
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | |
PipelineResult¶
PipelineResult
dataclass
¶
PipelineResult(
success: bool = True,
rows_extracted: int = 0,
rows_transformed: int = 0,
rows_loaded: int = 0,
rows_failed: int = 0,
rows_quarantined_extract: int = 0,
rows_quarantined_load: int = 0,
validation_errors_extract: list[str] = list(),
validation_errors_load: list[str] = list(),
start_time: datetime | None = None,
end_time: datetime | None = None,
duration_seconds: float | None = None,
batches_processed: int = 0,
batch_results: list[BatchResult] = list(),
errors: list[str] = list(),
pipeline_name: str | None = None,
run_id: str | None = None,
quality_report: Any = None,
privacy_report: Any = None,
lineage_events: list[dict[str, Any]] = list(),
step_results: list[StepResult] = list(),
rows_quarantined_other: int = 0,
record_outcomes: RecordOutcomes = RecordOutcomes(),
quality_summary: QualitySummary = QualitySummary(),
)
rows_failed counts loader-reported failures. Contract validation and routing
are reported separately in record_outcomes; total_quarantined counts
acknowledged DLQ writes. Each step retains its input, pre-contract, and output
cardinality, validation outcomes, and scoped quality reports, including partial
results after a failure. See record outcome accounting
and pipeline quality.
Failure behavior¶
The default on_error="fail" aborts on the first step failure. Use
on_error="warn" to continue with remaining runnable steps. Inspect the
returned result in either case:
result = await pipeline.arun(on_error="warn")
if not result.success:
for error in result.errors:
print(error)
This run-level policy is separate from per-record contract.on_error routing.
Every step contract selecting quarantine requires a declared DLQ destination.
A failed run can retain acknowledged destination writes; it does not imply a
rollback. A loader exception can leave delivery unknown.
Preview and offline validation¶
pipeline.preflight() resolves and checks contracts without constructing
source or destination connectors. A configured contract registry can still
require network reads; use local contract files for offline checks.
await pipeline.arun(dry_run=True) is an extraction preview. It reads sources
and executes transforms and contract checks while suppressing normal load and
quarantine destination writes. It reports quarantine candidates as
record_outcomes.would_quarantine. Retained Python runs with the process's
permissions; connector substitution does not sandbox it.
Use PipelineTestHarness to substitute every configured DAG source, destination, and quarantine connector with fixture and capture implementations, with per-step captures and no configured fallback.
Checkpoints and queue consumers¶
The native batch DAG does not persist incremental watermarks or automatically
acknowledge messaging extractor batches after loading. Keep checkpoint and
consumer acknowledgment ownership explicit in the scheduled job or use the
streaming runtime for its documented
broker lifecycle. source.incremental is rejected; standalone state helpers
do not establish checkpoint behavior in a DAG manifest.
settings.state_store and settings.plugins are retained configuration fields
awaiting native DAG integration. They do not automatically persist checkpoints
or import the specified connector classes. Standalone helpers remain available;
see retained settings
for their current scope. Installed entry-point connectors and explicit factory
registration remain separate supported mechanisms.