Lightweight, contract-first data quality engine for modern data pipelines
Project description
dqflow
dqflow is a lightweight, contract-first data quality engine for modern data pipelines.
Define explicit expectations for your data (schema, validity, freshness) and fail fast when data breaks — before bad data reaches downstream systems.
Why dqflow?
Data quality issues are inevitable — silent failures are not.
Most teams rely on ad-hoc checks, fragile assertions, or heavyweight frameworks that are hard to maintain. dqflow takes a different approach:
- Contracts over checks — expectations are explicit and versionable
- Pipeline-first — designed for ETL, ELT, and streaming workflows
- Lightweight & Pythonic — minimal API, easy to embed
- Fail fast — break pipelines intentionally, not silently
Installation
pip install dqflow
Quick Example
import pandas as pd
from dqflow import Contract, Column
# Sample data with quality issues
df = pd.DataFrame({
"order_id": ["A001", None, "A003"], # Has null value
"amount": [100.0, -50.0, 75.0], # Has negative value
"currency": ["USD", "GBP", "EUR"], # GBP not in allowed list
})
# Define your data contract
contract = Contract(
name="orders",
columns={
"order_id": Column(str, not_null=True),
"amount": Column(float, min=0),
"currency": Column(str, allowed=["USD", "EUR"]),
},
rules=["row_count > 0"],
)
# Validate
result = contract.validate(df)
# Check results
print(result.summary())
Output:
Contract 'orders': 4/7 checks passed
Failed checks:
- not_null:order_id: Found 1 null values
- min:amount: Minimum value -50.0 is below 0
- allowed:currency: Found invalid values: {'GBP'}
Use in pipelines:
if not result.ok:
raise Exception(result.summary()) # Fail fast!
Full Example with All Features
import pandas as pd
from dqflow import Contract, Column
# Define contract with all constraint types
orders = Contract(
name="orders",
columns={
"order_id": Column(str, not_null=True),
"amount": Column(float, min=0, max=100000),
"currency": Column(str, allowed=["USD", "EUR"]),
"created_at": Column("timestamp", freshness_minutes=60),
},
rules=[
"row_count > 1000",
"null_rate(amount) < 0.01",
],
)
result = orders.validate(df)
# Programmatic access to results
print("Passed:", result.ok)
print("Failed checks:", [c.name for c in result.failed_checks])
# JSON output for logging/monitoring
import json
print(json.dumps(result.to_dict(), indent=2))
Output:
Passed: False
Failed checks: ['column_exists:created_at', 'not_null:order_id', 'min:amount', 'allowed:currency', 'rule:row_count > 1000', 'rule:null_rate(amount) < 0.01']
{
"checks": [
{
"details": {},
"message": "",
"name": "column_exists:order_id",
"passed": true
},
{
"details": {},
"message": "",
"name": "column_exists:amount",
"passed": true
},
{
"details": {},
"message": "",
"name": "column_exists:currency",
"passed": true
},
{
"details": {},
"message": "Column 'created_at' not found in DataFrame",
"name": "column_exists:created_at",
"passed": false
},
{
"details": {
"null_count": 1
},
"message": "Found 1 null values",
"name": "not_null:order_id",
"passed": false
},
{
"details": {
"actual_min": -50.0
},
"message": "Minimum value -50.0 is below 0",
"name": "min:amount",
"passed": false
},
{
"details": {
"actual_max": 100.0
},
"message": "",
"name": "max:amount",
"passed": true
},
{
"details": {
"invalid_values": [
"GBP"
]
},
"message": "Found invalid values: {'GBP'}",
"name": "allowed:currency",
"passed": false
},
{
"details": {},
"message": "Rule 'row_count > 1000' failed",
"name": "rule:row_count > 1000",
"passed": false
},
{
"details": {},
"message": "Failed to evaluate rule: name 'amount' is not defined",
"name": "rule:null_rate(amount) < 0.01",
"passed": false
}
],
"contract_name": "orders",
"failed": 6,
"ok": false,
"passed": 4,
"total_checks": 10
}
Features (v0.1 scope)
-
Contract-as-code (Python & YAML)
-
Column-level checks
- type validation
- not null
- min / max
- allowed values
-
Table-level checks
- row count
- freshness
-
Structured validation results (JSON-friendly)
-
Pandas engine
-
CLI support
YAML Contract
Define contracts in YAML for version control:
# contracts/orders.yaml
name: orders
description: E-commerce order data contract
columns:
order_id:
type: string
not_null: true
amount:
type: float
min: 0
max: 100000
currency:
type: string
allowed: ["USD", "EUR", "GBP"]
created_at:
type: timestamp
freshness_minutes: 1440
rules:
- row_count > 0
- "null_rate(amount) < 0.01"
Load in Python:
from dqflow import Contract
contract = Contract.from_yaml("contracts/orders.yaml")
result = contract.validate(df)
CLI Usage
# Validate data against a contract
dq validate contracts/orders.yaml data/orders.parquet
# Show contract details
dq show contracts/orders.yaml
# Infer contract from existing data
dq infer data/orders.csv contracts/orders.yaml
# JSON output for CI/CD
dq validate contracts/orders.yaml data/orders.parquet --output json --fail-fast
Supported engines
- ✅ Pandas
- 🚧 PySpark (planned)
- 🚧 SQL tables (planned)
Philosophy
- Explicit is better than implicit
- Bad data should break pipelines early
- Quality rules are part of your system design
dqflow is not a full data observability platform. It is a small, opinionated library meant to be embedded directly into pipelines.
Roadmap
- PySpark engine
- dbt / dlt integrations
- Incremental & backfill-aware validation
- Metrics export (Prometheus-compatible)
License
MIT
Status
🚧 Early development (v0.1.1)
APIs may change. Feedback and contributions are welcome.
Contributing
# Clone and install dev dependencies
git clone https://github.com/dqflow/dqflow.git
cd dqflow
pip install -e ".[dev]"
# Run tests
pytest
# Lint
ruff check .
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dqflow-0.1.3.tar.gz.
File metadata
- Download URL: dqflow-0.1.3.tar.gz
- Upload date:
- Size: 13.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef150dc65a4f0b311c4b112eb730130a76a7aad751c60a46a5a41a421c029137
|
|
| MD5 |
5684aaa93b3566f018c6da89e0b083b3
|
|
| BLAKE2b-256 |
55625e2043faac9d008340a90141fa0b9823dc8fb467c3209a8e6a3672c740f3
|
Provenance
The following attestation bundles were made for dqflow-0.1.3.tar.gz:
Publisher:
release.yml on dqflow/dqflow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dqflow-0.1.3.tar.gz -
Subject digest:
ef150dc65a4f0b311c4b112eb730130a76a7aad751c60a46a5a41a421c029137 - Sigstore transparency entry: 877765369
- Sigstore integration time:
-
Permalink:
dqflow/dqflow@833b7a9634cc3b91c2e97b88f3a038e5bf8b3206 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/dqflow
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@833b7a9634cc3b91c2e97b88f3a038e5bf8b3206 -
Trigger Event:
release
-
Statement type:
File details
Details for the file dqflow-0.1.3-py3-none-any.whl.
File metadata
- Download URL: dqflow-0.1.3-py3-none-any.whl
- Upload date:
- Size: 11.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98ad53d68e6e54b5ea63df2f23676819bb26ea69f2a5af371ca058dede1bcd22
|
|
| MD5 |
769f117f6c092a795dca1cca9dc27545
|
|
| BLAKE2b-256 |
f8dd2b844cb56241a9eaaaaf1fced6a4579fd5c9d5708d2ff959635b942e56f8
|
Provenance
The following attestation bundles were made for dqflow-0.1.3-py3-none-any.whl:
Publisher:
release.yml on dqflow/dqflow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dqflow-0.1.3-py3-none-any.whl -
Subject digest:
98ad53d68e6e54b5ea63df2f23676819bb26ea69f2a5af371ca058dede1bcd22 - Sigstore transparency entry: 877765449
- Sigstore integration time:
-
Permalink:
dqflow/dqflow@833b7a9634cc3b91c2e97b88f3a038e5bf8b3206 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/dqflow
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@833b7a9634cc3b91c2e97b88f3a038e5bf8b3206 -
Trigger Event:
release
-
Statement type: