Skip to main content

Structural drift detection for nested JSON / dict data. Learn the shape of your payloads, freeze it as a contract, catch breaking changes before they hit your code.

Project description

contractguard

Structural drift detection for nested JSON / dict data.

Learn the shape of your nested payloads from real samples, freeze it as a contract, and catch breaking structural changes before they silently break your code.

import contractguard as cg

# 1. Learn the shape from known-good samples
contract = cg.learn([sample_a, sample_b, sample_c])
contract.save("api_contract.json")

# 2. Later: check new payloads against the frozen contract
report = contract.check(new_payload)
if report.drifted:
    print(report)
3 change(s) detected:
  - TypeChanged: items[0].price  (float -> str)
  - TypeChanged: user.age  (int -> str)
  - NewKey: user.phone  (unexpected str)

Why this exists

APIs and config files break silently. A backend renames a field, flips an int to a str, drops a key, or turns a list into an object — your code doesn't crash immediately, but something downstream quietly goes wrong, and you lose an afternoon finding it.

contractguard learns the structure of your data from real examples and tells you, in plain language, exactly what changed and where.

How it's different

Tool What it does What contractguard does
genson Infers a JSON schema from data Infers it and enforces it over time
data-drift-detector Statistical drift on flat dataframes Structural drift on nested JSON
pydantic / jsonschema You hand-write the schema It learns the schema from real data

The key gap it fills: every statistical drift tool assumes flat rows and columns. contractguard walks arbitrarily nested dicts and lists, so it works on real API payloads, event streams, and config files.

Features

  • Zero dependencies — pure standard library.
  • Nested-aware — reports dotted paths like user.address.zip.
  • Lenient by default — fields missing from some learning samples are treated as optional, so you don't get false alarms.
  • Nullable-aware — if null was seen during learning, null is allowed.
  • No cascade noise — a list -> dict change reports one root cause, not a flood of child errors.
  • Saveable contracts — freeze a contract to JSON, commit it, check against it in CI.

Install

pip install contractguard

What it detects

  • TypeChanged — a value's type changed (int -> str)
  • KeyMissing — a required key disappeared
  • NewKey — a key appeared that wasn't in the learned shape
  • CardinalityChanged — a list element's type changed, or a container's kind changed (list -> dict)

Command-line usage

contractguard ships a CLI, so you can use it without writing any Python:

# Learn a contract from sample payloads
contractguard learn sample1.json sample2.json -o contract.json

# Check a new payload against it
contractguard check payload.json --against contract.json

check exits with status 1 when drift is found and 0 when clean, so it drops straight into CI pipelines:

contractguard check response.json --against contract.json || echo "API changed!"

Add --strict to learn to mark every observed field as required.

pytest integration

Guard against API shape changes inside your own test suite:

from contractguard import assert_no_drift

def test_user_endpoint_shape(client):
    response = client.get("/api/user/1").json()
    assert_no_drift("contracts/user.json", response)

If the response structure drifts, the test fails with a readable breakdown of exactly what changed. The contract argument accepts a saved-contract path, a Contract instance, or a list of sample payloads to learn from on the fly.

Strict vs lenient

By default, learning is lenient: a field missing from some samples is treated as optional and won't trigger drift later. Pass strict=True (or --strict on the CLI) to require every field that was ever observed:

contract = cg.learn(samples, strict=True)

Roadmap

  • HTML / JSON report output
  • GitHub Action for CI
  • Configurable type coercion (e.g. treat int and float as compatible)

License

MIT

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

contractguard-0.2.1.tar.gz (11.3 kB view details)

Uploaded Source

Built Distribution

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

contractguard-0.2.1-py3-none-any.whl (12.1 kB view details)

Uploaded Python 3

File details

Details for the file contractguard-0.2.1.tar.gz.

File metadata

  • Download URL: contractguard-0.2.1.tar.gz
  • Upload date:
  • Size: 11.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for contractguard-0.2.1.tar.gz
Algorithm Hash digest
SHA256 f2dfe6496b63c04816a906ea749d44243b66a092278d2a5277e60ddc2ca48d0a
MD5 a307a69effc42c808dc445afce17c324
BLAKE2b-256 7f32db0efb4fb7de3d943225af2bb9438b5f5823ccdf3e3fb947f1ab9390c04b

See more details on using hashes here.

File details

Details for the file contractguard-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: contractguard-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 12.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for contractguard-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bde105779f679d04ab5a41a94ba5fda82a918866f01752c3628a6b976acf01d3
MD5 7e8c65d169e5150ac1f299afdfc5528a
BLAKE2b-256 32ab19438d25bc6d4d03570c84d4378496c841fe3202471debcd67386857355d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page