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.7 — CSV/Parquet/SQLite/Postgres/MySQL sources, custom SQL +
expect, pattern, freshness, baselines, JUnit,--format json.
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.7)
- 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
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
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/Dagster operators
License
MIT
Known limitations (v0.7)
- Airflow/Dagster operators not shipped yet
examples/checks.yamlis a failing fixture;examples/clean.yaml/examples/postgres.yaml/examples/mysql.yamlare happy paths- Checks still run against a
source_dataview
Release files for duckcheck 0.7.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 | |
|---|---|---|---|
| duckcheck-0.7.0.tar.gz | 22.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| duckcheck-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:33.1 kB
Release files / duckcheck-0.7.0.tar.gz
| Download URL | duckcheck-0.7.0.tar.gz |
|---|---|
| Size | 22.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e14bc6ab142a4be35aa3614bd12ada77b08e201970dada1d600f8f07b14dd0b6
|
|
BLAKE2b-256 checksum How to use checksums |
8dc27f2c69a7b604f2cac23ef5c064708874c6c10e889246abf67349cf6d37c8
|
| 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.7.0-py3-none-any.whl
| Download URL | duckcheck-0.7.0-py3-none-any.whl |
|---|---|
| Size | 10.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e2b78ee720ff39854c09a06c667efe59bf6bcecca57e17448979d5041268e5cc
|
|
BLAKE2b-256 checksum How to use checksums |
1eaaf700db933da910c65996e48f7b5e77c3434fe3ce60076398add00f68bd5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|