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.0.tar.gz (11.2 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.0-py3-none-any.whl (12.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: contractguard-0.2.0.tar.gz
  • Upload date:
  • Size: 11.2 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.0.tar.gz
Algorithm Hash digest
SHA256 402224d75746b2139cc522ecc6ffc2d8f153e8d11c13d6241c9abd58e818a6cc
MD5 100b440501935a0f2d16d8fb492c0693
BLAKE2b-256 08c35e4f70c34d31d0b0f1a7bec3b5d7069ef008108ce173244d95e1b77f826f

See more details on using hashes here.

File details

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

File metadata

  • Download URL: contractguard-0.2.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 09b37b899836826b118f87fedd0e951a9de30a77718f7a12eade94cea7168c85
MD5 7e1734f20de45f3f8efc227bc973ce1b
BLAKE2b-256 9d02ea834d35066abe0e8e8c6185eb9ec8a5e36cc42d6f5bb96eee51d7b35691

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