Skip to main content

PyCharter

PyCharter enforces data contracts where your code runs.

Define a contract once — from a Pydantic model (pycharter contract from-model) or versioned YAML — and the same artifact validates API payloads in-process, gates Kafka consumers record by record, and checks ETL batches at stage boundaries. Deterministic, Pydantic-native, in your process: no warehouse round-trip, no separate CI engine deciding after the fact. The contract is what actually runs.

Most contract tooling stops earlier in the lifecycle: datacontract-cli — the ODCS reference implementation — verifies contracts in CI and batch; Soda and Great Expectations scan the warehouse; dbt tests run after materialisation. PyCharter is the runtime half of that ecosystem: it executes contracts inside the producing service, and round-trips ODCS (v3.1.0 export, v3.0.x / v3.1.x import) so the contracts you enforce at runtime slot into the tools you already use.

Full documentation: https://optophi.github.io/pycharter/

Python 3.11+ License: MIT Ruff

Where PyCharter fits vs datacontract-cli, Pydantic, Pandera, GX, Soda, dbt →


Validate a record in 30 seconds

pip install pycharter
from pycharter import Validator

validator = Validator.from_dict({
    "type": "object",
    "version": "1.0.0",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer"},
    },
    "required": ["name", "age"],
})

result = validator.validate({"name": "Alice", "age": 30})
print(result.is_valid)   # True

result = validator.validate({"name": "Bob", "age": "N/A"})
print(result.errors[0])  # Column 'age': expected integer, found string 'N/A'

That's the library at its smallest — no database, no setup. In production you'd load the same contract from a file (Validator.from_file("contract.yaml")) or a shared store. Failures come back as structured diagnostics (row, column, expected type, offending value), and the generated model validates as fast as a hand-written Pydantic model (~0.7 µs/record — measured).

Prefer the shell? The same check works on a whole file:

pycharter validate contract.yaml data.jsonl   # colorized report, exit 0/1

Why PyCharter

Teams often maintain a Pydantic model at the API, a Pandera schema in the pipeline, a Great Expectations suite for quality, and an ODCS YAML as the "contract" — and they drift. PyCharter unifies authoring and enforcement so the contract is what actually runs, where it runs being the point:

Tool Where enforcement runs Object Dataset Quality Contract Versioned
Pydantic In-process (single objects)
Pandera In-process (DataFrames)
Great Expectations Warehouse / batch jobs
Soda Warehouse-side, scheduled scans
dbt tests Warehouse, post-materialisation
datacontract-cli (ODCS ref. impl.) CI / batch
PyCharter In-process runtime: APIs, streams, ETL

● full · ◐ partial · ○ none — markers reflect scope, not quality; see the full comparison

  • One definition, every surface — model or YAML → API gate, stream gate, ETL steps, quality jobs, ODCS round-trip.
  • Enforces, not just authors — rejects or coerces bad records before they land; warehouse-side engines measure what did land.
  • Deterministic and auditable — versioned contracts, reproducible runs, thread-safe shared validators.

Choose Pydantic alone for one-service API shape checks, Pandera for DataFrame validation in one process, GX/Soda for warehouse-centric batch quality, datacontract-cli for ODCS authoring and CI gates — and PyCharter when the contract must be the enforced single source of truth across services. Full narrative and per-tool sections: PyCharter vs. the alternatives.


Gate a FastAPI endpoint

pip install pycharter[fastapi]
from fastapi import Depends, FastAPI
from pycharter.contrib.fastapi import contract_dependency

app = FastAPI()
validate_order = contract_dependency("contracts/orders.yaml")  # built once

@app.post("/orders")
async def create_order(order: dict = Depends(validate_order)) -> dict:
    # `order` is the coerced + validated payload.
    return {"status": "accepted", "order_id": order["order_id"]}

Contract violations return a structured 422; the same YAML also drives your pipelines and workers. Recipe: Gate a FastAPI endpoint.

Validate a Kafka topic, per record

import asyncio
from pycharter import kafka

async def main():
    async for record in kafka.consume(
        topic="orders",
        bootstrap_servers="localhost:9092",
        group_id="my-service",
        contract_dir="contracts/orders",   # or contract_store=...
    ):
        process(record.payload)   # only records that passed the contract

asyncio.run(main())

Invalid records route to a DLQ with a structured envelope; detect / shadow / enforce modes support safe rollouts, and pycharter stream run runs it as a worker with Prometheus metrics. Start at Streaming — start here.

Gate contract evolution in CI

pycharter contract diff old.yaml new.yaml --fail-on-breaking

Classifies every field/type/required change as breaking or safe (backward-compat semantics) — drop into pre-commit or CI so a contract change can't ship without review.


Concepts

Concept What it is When you use it
Schema The shape of the data (JSON Schema): types, required fields, nested objects. When you only need structure.
Data contract Schema + coercion rules ("30"30) + validation rules (min/max, allowed values) + optional metadata (ownership, governance). One artifact for structure, transforms, and business rules.
Contract store A database (SQLite, PostgreSQL, …) holding versioned contracts so many apps reuse them. Multiple services needing one source of truth.
Enforcement point Where the contract executes: API dependency, stream consumer, ETL step flags (coerce / validate / quality). Wherever bad data must be stopped.
  Option A: no database   schema/contract in code or YAML → Validator → validate(data)
  Option B: with store    contract in DB → Validator(store=...)       → validate(data)
  Option C: pipelines     extract → [contract] → transform → [contract] → load

Start with Option A; add the store when contracts need versioned sharing; add pipeline/stream enforcement where data moves. Deep dives: Concepts · Contract → ETL → quality journey · Cookbook.


Installation

pip install pycharter

That's all you need for in-process validation. Optional extras add capabilities — install only what you use:

Extra Adds Install
[fastapi] contract_dependency request gate pip install pycharter[fastapi]
[streaming] / [kafka] Streaming validation engine / Kafka source pip install pycharter[kafka]
[api] REST API server (FastAPI + Uvicorn) pip install pycharter[api]
[ui] Pre-built Web UI (no Node.js required) pip install pycharter[ui]
[postgres] PostgreSQL contract store driver pip install pycharter[postgres]
[mcp] MCP server for AI agents pip install pycharter[mcp]
[otel] OpenTelemetry tracing instruments pip install pycharter[otel]
[observability] Prometheus exporter + webhook sinks pip install pycharter[observability]
[airflow] / [dagster] Orchestrator operators pip install pycharter[airflow]
[all] The common stack: api, ui, worker, pipeline, postgres, streaming, messaging, extraction, lineage, semantic-export, collab, cli, docs (not the optional mcp / otel / observability / airflow / dagster integrations — install those explicitly) pip install pycharter[all]

Database setup (only for the contract store, API, or UI)

pycharter db init     # creates schema (default: sqlite:///pycharter.db)
pycharter db seed     # loads reference data

Skip this entirely if you only validate dicts or YAML contracts directly. PostgreSQL/SQLite have managed Alembic migrations; see Store backend tiers and the Configuration Guide.


ODCS and the contract ecosystem

PyCharter round-trips the Open Data Contract Standard: to_odcs() emits v3.1.0 documents that validate against the published schema; from_odcs() imports v3.0.x / v3.1.x documents authored anywhere — so contracts written with datacontract-cli enforce at runtime here, and contracts authored here flow back into the ODCS toolchain. PyCharter is not a rival standard; it is an execution engine for the standard.

from pycharter.contract_io import from_odcs, to_odcs
from pycharter import Validator

contract = from_odcs(odcs_document)   # any v3.0.x / v3.1.x doc
validator = Validator(contract)       # enforce it immediately

How PyCharter composes with datacontract-cli, Soda, GX, and dbt: ODCS integration and interop.

Agents and MCP

PyCharter ships an MCP server (python -m pycharter.mcp, stdio JSON-RPC) that serves governed context packs — concepts, relationships, and the contract fields bound to them — to Claude Code, Claude Desktop, or any MCP client, with role/workspace policy filtering. The context an agent receives is bound to the same contracts PyCharter enforces at runtime, so what the agent is told about your data is what your validators actually check.

pip install pycharter[mcp]
export PYCHARTER_DATABASE_URL=postgresql://localhost/pycharter
python -m pycharter.mcp

Tools, client config, and the governance model: Agent integration (MCP).


Architecture at a glance

Service Input Output Journey stage
Contract Parser Contract files (YAML/JSON) ContractMetadata Specification → Parsing
Contract Builder Separate artifacts or store Consolidated contract Storage → Consolidation
Contract Store ContractMetadata Stored metadata (DB) Parsing → Storage
Pydantic Generator JSON Schema Pydantic models Storage → Model generation
JSON Schema Converter Pydantic models JSON Schema (Bidirectional)
Runtime Validator Pydantic models + data ValidationResult Model generation → Validation
ETL Pipelines Config files or code PipelineResult Extract → Transform → Load
Quality Assurance Contract + data QualityReport Validation → Quality monitoring

Each service is independent yet composable. The full tour — including the REST API and Web UI wrappers — lives in the Core services reference; the layered design philosophy is in ARCHITECTURE.md.

Picking the right API

The canonical path is the Validator class — create one from a contract file, dict, directory, or store, and call validate(record) / validate_batch(records). It compiles the Pydantic model once, is thread-safe to share, and is what production code should use.

Use case Approach Example
Production service, many validations Validator class Validator.from_file("c.yaml").validate(data)
Pandas DataFrame, split good/bad rows validate_dataframe() validate_dataframe(df, "c.yaml", on_error="quarantine")
Shell / CI gate on a data file pycharter validate CLI pycharter validate c.yaml data.jsonl
Batch processing Validator.validate_batch() validator.validate_batch(rows)
You already have a Pydantic model Low-level function (deprecated at top level) from pycharter.runtime_validator import validate

ETL flows use Pipeline; quality checks use QualityCheck; contract storage is pluggable via ContractStoreClient implementations. Built-in coercions/validations and custom registration: reference.


Documentation

Development and testing

python -m venv venv && source venv/bin/activate
pip install -e ".[dev]" && pre-commit install
pytest tests/unit            # fast suite; `pytest -m integration` needs Docker
./scripts/ci.sh              # full GHA-parity gate before opening a PR

See CONTRIBUTING.md for the workflow and AGENTS.md for AI-assisted contributions.

Contributing

Contributions are welcome — see CONTRIBUTING.md. Report security issues per SECURITY.md; community expectations are in CODE_OF_CONDUCT.md.

License

MIT — see LICENSE.

Links

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pycharter-0.0.62.tar.gz (6.9 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pycharter-0.0.62-py3-none-any.whl (7.2 MB view details)

Uploaded Python 3

File details

Details for the file pycharter-0.0.62.tar.gz.

File metadata

  • Download URL: pycharter-0.0.62.tar.gz
  • Upload date:
  • Size: 6.9 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pycharter-0.0.62.tar.gz
Algorithm Hash digest
SHA256 8c48adeb97ac9288d52e364d72a65e7998c11fdf39f7042c8026c40f056961a1
MD5 e8b7e4963e3808c27d976ba700d20ada
BLAKE2b-256 3ad0b85174b671a6e415f9428fd3e49a2576f56e24c0717cb090697ea71cd110

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycharter-0.0.62.tar.gz:

Publisher: publish.yml on optophi/pycharter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pycharter-0.0.62-py3-none-any.whl.

File metadata

  • Download URL: pycharter-0.0.62-py3-none-any.whl
  • Upload date:
  • Size: 7.2 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pycharter-0.0.62-py3-none-any.whl
Algorithm Hash digest
SHA256 26e536da2c0794fc372a46cacbae606899ef34ed473fe773a20f739360c127e8
MD5 2161d91ac0e152a218302ea7a62c49b5
BLAKE2b-256 5633070d46e1c2f82763d9d00620a7660003ee60a3efb5811b4d09d06b905eae

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycharter-0.0.62-py3-none-any.whl:

Publisher: publish.yml on optophi/pycharter

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.0.67

2 files

0.0.66

2 files

0.0.65

2 files

0.0.64

2 files

0.0.63

2 files

This release

0.0.62 This release

2 files

0.0.61

2 files

0.0.60

2 files

0.0.59

2 files

0.0.58

2 files

0.0.57

2 files

0.0.56

2 files

0.0.54

2 files

0.0.45

2 files

0.0.40

2 files

0.0.35

2 files

0.0.30

2 files

0.0.25

2 files

0.0.20

2 files

0.0.10

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page