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.2.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.2-py3-none-any.whl (12.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: contractguard-0.2.2.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.2.tar.gz
Algorithm Hash digest
SHA256 d8192ee699feb366ed1e81f9a7d9fb64f2dcaa6b82411a9abe86765b60083531
MD5 a05c472a3ad1a7e2d436b4a34c6cc864
BLAKE2b-256 7da608ff923c40c3be65a1c7af96b65f7d2116454e8d16bd582251c3f79d925f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: contractguard-0.2.2-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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 5bec44dd621eabd1a1e512a5effedcec33c1ac99af3088f996e9495d271386f0
MD5 3ad11139a6f7aa416227217a41def67a
BLAKE2b-256 e4b3e806af48f87f4811bf88328c5f5ab026e5e822b394407453131ca43fdeb4

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