Skip to main content

duckcheck

Lightweight data quality checks powered by DuckDB — the anti–Great Expectations for teams who want pip install, one YAML file, and one command.

PyPI License: MIT Python 3.11+ CI

Status: v0.8 — CSV/Parquet/SQLite/Postgres/MySQL sources, custom SQL, baselines, JUnit/JSON, Airflow + Dagster operators.

60-second try

pip install duckcheck
duckcheck run examples/clean.yaml
# or with Docker:
docker compose run --rm run-example
# live Postgres / MySQL ATTACH examples:
docker compose run --rm run-postgres-example
docker compose run --rm run-mysql-example

Why this vs alternatives

Approach Strength Gap
duckcheck One YAML + DuckDB, local files, CI-friendly Not a full observability platform
Great Expectations Rich ecosystem Heavyweight setup for simple column checks
Soda Core Familiar check DSL Cloud-oriented workflow
Ad-hoc SQL in CI Zero new tools No standard report / JUnit / baselines

Problem

Data teams need to assert column quality in CI, but Great Expectations is heavyweight and Soda Core funnels to cloud. Ad-hoc SQL checks have no reporting standard.

Key features (v0.8)

  • YAML check definitions
  • DuckDB scans CSV, Parquet, SQLite, Postgres, and MySQL (ATTACH) — no separate DQ server
  • Checks: not_null, unique, accepted_values, custom_sql, pattern, freshness, row_count, row_count_delta
  • custom_sql ${column} / ${name} substitution; expect operators: 0, =N, >N, <N, >=N, <=N (default 0)
  • --format json and --junit for CI dashboards
  • ${ENV} in source URIs; --source-table for SQL ATTACH
  • Orchestration: DuckCheckOperator (Airflow) and duckcheck_op (Dagster) via optional extras

Architecture

duckcheck run checks.yaml
    └── SuiteSpec (Pydantic)
            └── DuckDB in-process
                    └── source_data view from CSV/Parquet/SQLite/Postgres/MySQL
Component Technology Why
Engine DuckDB Single dependency, scans files + SQL databases
CLI Click + Rich Simple, good terminal UX
Spec YAML + Pydantic Version-controllable checks

Installation

pip install duckcheck
pip install -e ".[dev]"

Usage

duckcheck health
duckcheck run examples/clean.yaml
duckcheck run examples/checks.yaml   # fixture with known failures
duckcheck run examples/clean.yaml --junit /tmp/duckcheck.xml
duckcheck run examples/clean.yaml --format json
duckcheck baseline update examples/clean.yaml

Example checks.yaml:

name: sample-suite
source: examples/sample.csv
checks:
  - name: id_not_null
    type: not_null
    column: id
  - name: status_values
    type: accepted_values
    column: status
    values: [active, inactive]
  - name: three_active
    type: custom_sql
    sql: "SELECT * FROM source_data WHERE status = 'active'"
    expect: "=3"

Postgres (compose hostname postgres; from the host set DUCKCHECK_PG_DSN):

name: postgres-suite
source: postgresql://duckcheck:duckcheck@postgres:5432/duckcheck
source_table: orders

MySQL (compose hostname mysql; from the host set DUCKCHECK_MYSQL_DSN):

name: mysql-suite
source: mysql://duckcheck:duckcheck@mysql:3306/duckcheck
source_table: orders

Docker

docker compose run --rm test
docker compose run --rm run-example
docker compose run --rm run-postgres-example
docker compose run --rm run-mysql-example

Airflow / Dagster

pip install 'duckcheck[airflow]'   # DuckCheckOperator
pip install 'duckcheck[dagster]'   # duckcheck_op(...)
from duckcheck.airflow_op import DuckCheckOperator

DuckCheckOperator(
    task_id="validate_orders",
    suite_path="checks/orders.yaml",
    source="postgresql://...",  # optional override
    junit_output="/tmp/duckcheck.xml",
)
from duckcheck.dagster_op import duckcheck_op

validate = duckcheck_op(name="validate", suite_path="checks/orders.yaml")

See examples/airflow/ and examples/dagster/.

Running tests

pytest tests/ -v
# live Postgres ATTACH (after compose postgres is up):
# compose publishes Postgres on host port 5433
DUCKCHECK_PG_DSN=postgresql://duckcheck:duckcheck@localhost:5433/duckcheck \
  pytest tests/test_runner.py::test_postgres_attach_live -v
# live MySQL ATTACH (compose publishes MySQL on host port 3307):
DUCKCHECK_MYSQL_DSN=mysql://duckcheck:duckcheck@127.0.0.1:3307/duckcheck \
  pytest tests/test_runner.py::test_mysql_attach_live -v

Roadmap

  • Freshness + row_count + custom_sql + JUnit
  • Row-count baseline delta store (duckcheck baseline update)
  • custom_sql expect operators + --format json
  • custom_sql ${column} / ${name} substitution + pattern checks
  • Live Postgres ATTACH (compose example + optional DUCKCHECK_PG_DSN test)
  • Live MySQL ATTACH (compose example + optional DUCKCHECK_MYSQL_DSN test)
  • Airflow DuckCheckOperator + Dagster duckcheck_op (optional extras)

License

MIT

Known limitations (v0.8)

  • Airflow/Dagster extras pull large frameworks — install only when needed
  • examples/checks.yaml is a failing fixture; examples/clean.yaml / examples/postgres.yaml / examples/mysql.yaml are happy paths
  • Checks still run against a source_data view

Download files

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

Source Distribution

duckcheck-0.8.0.tar.gz (25.1 kB view details)

Uploaded Source

Built Distribution

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

duckcheck-0.8.0-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file duckcheck-0.8.0.tar.gz.

File metadata

  • Download URL: duckcheck-0.8.0.tar.gz
  • Upload date:
  • Size: 25.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for duckcheck-0.8.0.tar.gz
Algorithm Hash digest
SHA256 82e574365e2140737700ecd49dd169e2ca006233a8e1a5df97b4bc3e0d5c8270
MD5 2a01885b685b0a7425daf97d68ab349e
BLAKE2b-256 7c0b16fdb28db5cbb9c157ec87d9e3ed68be0283ec6fe0cd1a2fdbfc9931dd70

See more details on using hashes here.

File details

Details for the file duckcheck-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: duckcheck-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 14.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for duckcheck-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cf3f96bb3c9d9696f68b98bac5e07c41793e79ec39b9e6b588821754a3c0c501
MD5 b61b44ac29c1e400aa24c4678c00b002
BLAKE2b-256 031eb96cd2c07968d46dc70e9959bb273a1ffc54e576ac8095adc4f25d6441b8

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

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