pyspark-contracts
Lightweight schema and data quality validation for PySpark DataFrames.
Validates at runtime — before your pipeline runs — with a structured JSON report queryable in CloudWatch Logs Insights, Datadog, or any log aggregator.
Install
pip install pyspark-contracts
PySpark is assumed to be on your classpath (Glue, Databricks, EMR).
For local development: pip install pyspark-contracts[pyspark]
Quick start
from pyspark.sql.types import DateType, FloatType, StringType
from pyspark_contracts import Contract, Field
class OdometerContract(Contract):
vin = Field(StringType(), nullable=False, min_length=17, max_length=17)
odometerStart = Field(FloatType(), nullable=False, min_value=0)
odometerEnd = Field(FloatType(), nullable=False, min_value=0)
readingDate = Field(DateType())
# Hard fail (default) — logs ERROR + raises ContractViolationError
OdometerContract().validate(df)
# Soft fail — logs WARNING + returns ViolationReport
report = OdometerContract().validate(df, mode="soft")
if report:
print(f"{len(report.violations)} violation(s) found")
Validation modes
| Mode | On violation | Return |
|---|---|---|
"hard" (default) |
Logs ERROR (JSON), raises ContractViolationError |
— |
"soft" |
Logs WARNING (JSON), continues |
ViolationReport |
Lazy vs fail-fast
By default, "hard" mode stops at the first violation found (fewer Spark actions,
faster feedback), while "soft" mode collects every violation so you get the full
picture in one run. Override either with lazy:
# Hard mode, but collect every violation before raising
OdometerContract().validate(df, lazy=True)
# Soft mode, but stop at the first violation
report = OdometerContract().validate(df, mode="soft", lazy=False)
Disabling validation
PYSPARK_CONTRACTS_ENABLED=false # skips all validation, no code changes needed
Useful when performance is critical and the schema is already trusted — e.g. the read right after a validated write.
Bringing your own logger
import logging
logger = logging.getLogger("my-etl-job")
OdometerContract().validate(df, logger=logger)
If no logger is passed, the library emits JSON to stdout.
Field options
| Option | Type | Description |
|---|---|---|
nullable |
bool |
Default True. Set False to fail on null values. |
min_value |
numeric / date | Minimum allowed value. |
max_value |
numeric / date | Maximum allowed value. |
min_length |
int |
Minimum string length. |
max_length |
int |
Maximum string length. |
regex |
str |
Regex pattern (must match entire value via rlike). |
allowed_values |
list |
Allowlist of valid values. |
Log output
Both modes emit the same structured JSON before acting:
{
"level": "ERROR",
"message": "contract violation — job aborted",
"contract": "OdometerContract",
"mode": "hard",
"violations": [
{"kind": "missing_column", "column": "odometerStart", "expected_type": "FloatType"},
{
"kind": "value_out_of_range",
"column": "odometerEnd",
"constraint": "min_value=0",
"row_pct": 2.3,
"failure_count": 42,
"sample_values": [-1.0, -5.5, -12.0]
}
],
"row_count": 1842
}
CloudWatch Logs Insights
-- All contract violations in the last 7 days
filter message = "contract violation — job aborted"
| stats count(*) as failures by contract
-- Violations by column across all runs
filter ispresent(violations)
| flatten violations
| stats count(*) by violations.kind, violations.column
Violation kinds
| Kind | Trigger |
|---|---|
missing_column |
Column declared in contract is absent from the DataFrame |
type_mismatch |
Column exists but type differs from declared type |
null_violation |
nullable=False column contains null values |
value_out_of_range |
Value below min_value or above max_value |
length_out_of_range |
String length below min_length or above max_length |
regex_mismatch |
Value does not match regex pattern |
value_not_allowed |
Value not in allowed_values list |
Performance note
Each quality constraint triggers a Spark action (filter + count).
For DataFrames with many constraints, consider caching before validation:
df = df.cache()
OdometerContract().validate(df)
Metadata
Release files for pyspark-contracts 0.2.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 | |
|---|---|---|---|
| pyspark_contracts-0.2.0.tar.gz | 13.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyspark_contracts-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.3 kB
Release files / pyspark_contracts-0.2.0.tar.gz
| Download URL | pyspark_contracts-0.2.0.tar.gz |
|---|---|
| Size | 13.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2ca66ce0ec6389645ad4029bfb3df0cadbb319faa94b1c0e1f9c2b07a2338dd
|
|
BLAKE2b-256 checksum How to use checksums |
7a8f9ae30f3a2f8abe7f87583556d3df7deed2a152ee5355f95c836375dd6462
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / pyspark_contracts-0.2.0-py3-none-any.whl
| Download URL | pyspark_contracts-0.2.0-py3-none-any.whl |
|---|---|
| Size | 15.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4ab317f7498283624648155d31c90b597b5c34d686507f9c4791feac9e91e8f5
|
|
BLAKE2b-256 checksum How to use checksums |
9b9265f0eb4f8141a24aee5e19df4c6a154bbdaad8d1f0a0a4965ee04bff00af
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency log