duckcheck
Lightweight data quality checks powered by DuckDB — the anti–Great Expectations for teams who want pip install, one YAML file, and one command.
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;expectoperators:0,=N,>N,<N,>=N,<=N(default0)--format jsonand--junitfor CI dashboards${ENV}in source URIs;--source-tablefor SQL ATTACH- Orchestration:
DuckCheckOperator(Airflow) andduckcheck_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
expectoperators +--format json - custom_sql
${column}/${name}substitution +patternchecks - Live Postgres ATTACH (compose example + optional
DUCKCHECK_PG_DSNtest) - Live MySQL ATTACH (compose example + optional
DUCKCHECK_MYSQL_DSNtest) - Airflow
DuckCheckOperator+ Dagsterduckcheck_op(optional extras)
License
MIT
Known limitations (v0.8)
- Airflow/Dagster extras pull large frameworks — install only when needed
examples/checks.yamlis a failing fixture;examples/clean.yaml/examples/postgres.yaml/examples/mysql.yamlare happy paths- Checks still run against a
source_dataview
Metadata
Release files for duckcheck 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| duckcheck-0.8.0.tar.gz | 25.1 kB | Without provenance | Details for duckcheck-0.8.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| duckcheck-0.8.0-py3-none-any.whl | Python 3 | none | any | Details for duckcheck-0.8.0-py3-none-any.whl |
Total release size: 39.1 kB
Release files / duckcheck-0.8.0.tar.gz
| Download URL | duckcheck-0.8.0.tar.gz |
|---|---|
| Size | 25.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
82e574365e2140737700ecd49dd169e2ca006233a8e1a5df97b4bc3e0d5c8270
|
|
BLAKE2b-256 checksum How to use checksums |
7c0b16fdb28db5cbb9c157ec87d9e3ed68be0283ec6fe0cd1a2fdbfc9931dd70
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / duckcheck-0.8.0-py3-none-any.whl
| Download URL | duckcheck-0.8.0-py3-none-any.whl |
|---|---|
| Size | 14.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cf3f96bb3c9d9696f68b98bac5e07c41793e79ec39b9e6b588821754a3c0c501
|
|
BLAKE2b-256 checksum How to use checksums |
031eb96cd2c07968d46dc70e9959bb273a1ffc54e576ac8095adc4f25d6441b8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Security for duckcheck 0.8.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.