Skip to main content

pryti-contract

Your backend keeps a list of what it does. And it can't lie about it.

Companion library to pryti-semantic-reviewer. Pryti reads code from outside and guesses. This runs inside your app and knows.

What it does

While your app starts, it records:

  • every route (path, method, auth)
  • every model (fields, types, constraints)
  • every outside call (HTTP, email, payments)
  • every background job
  • how much of that it actually knows vs. couldn't figure out

Then you can ask your app one question and get an answer:

"What does this app actually do?"

Why it matters

AI opens a PR with 400 changed lines. You don't read them. You read this:

RISKY (4)
  POST /orders         auth: user -> public
  POST /orders         new effect: net:analytics.example.com
  shop.Customer.email  unique: True -> False
  shop.Customer.name   field removed (data loss)

Four lines instead of four hundred.

Install

pip install pryti-contract

Use it — three levels

Level 1: nothing to write

Works on an existing Django project with zero code changes.

pryti-contract export --settings myproject.settings -o contract.json

It reads Django's real router and real model registry. Routes built in loops, DRF routers, mixins — all found, because the app already resolved them at startup.

Coverage will be partial. That's reported, not hidden:

wrote contract.json: 9 routes, 6 models, 0 jobs, auth known on 5/9

Level 2: declare what matters

from pryti_contract import contract

@contract.route("POST /orders", auth="user")
@contract.effects("net:api.stripe.com")
def create_order(request):
    ...

Eight decorators total, max. AI writes these correctly first try, because they look like every Django decorator it has ever seen.

Level 3: enforce it

# settings.py
MIDDLEWARE = ["pryti_contract.middleware.ContractMiddleware", ...]

# conftest.py or apps.py
from pryti_contract import guard
guard.install(mode="error")   # off | record | warn | error

Now an undeclared call fails loudly:

shop.views.leaky_order performed undeclared effect 'net:api.stripe.com'.
Declared: none. Add @contract.effects('net:api.stripe.com') or remove the call.

This is the part no linter can do. The bad code doesn't ship.

Don't know what to declare? Run your tests in record mode and let it tell you:

guard.install(mode="record")
# ... run test suite ...
json.dump(guard.suggestions(), open("observed.json", "w"))
pryti-contract suggest observed.json     # prints the decorators to paste in

In CI

- run: pryti-contract export --settings myproject.settings -o head.json
- run: git checkout ${{ github.base_ref }}
- run: pryti-contract export --settings myproject.settings -o base.json
- run: pryti-contract diff base.json head.json --markdown --fail-on risky

--fail-on risky exits 1 on auth weakening, new outside calls, dropped fields, relaxed uniqueness, or changed relations.

How the effect guard works

One hook at the socket layer, not per-library. requests, httpx, urllib, boto3, stripe — all covered by the same code, including libraries that don't exist yet.

  • socket.getaddrinfo is checked before it runs, so a blocked call never leaves the process
  • socket.socket.connect catches direct-IP connections
  • smtplib.SMTP.sendmail catches email
  • localhost and unix sockets are never effects, so your database doesn't trip it

Patterns support wildcards: net:*.stripe.com, net:*, email.

What it does not catch

Worth being blunt about.

  • Business logic. If AI changes a discount from 10% to 90%, the contract is identical. Routes same, models same, effects same. Tests catch that; this doesn't.
  • Anything the router never sees. Dead code, unmounted views.
  • Dynamic hostnames are caught at runtime, not at export time. The contract records what you declared; the guard records what actually happened.

Structural mistakes: this. Logic mistakes: your tests. You need both.

Try it

git clone ... && cd pryti-contract
pip install -e ".[dev]"
pytest
cd examples/demo && pryti-contract export --settings settings --root . -o /tmp/base.json

MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pryti_contract-0.1.0.tar.gz (19.6 kB view details)

Uploaded Source

Built Distribution

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

pryti_contract-0.1.0-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

File details

Details for the file pryti_contract-0.1.0.tar.gz.

File metadata

  • Download URL: pryti_contract-0.1.0.tar.gz
  • Upload date:
  • Size: 19.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for pryti_contract-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fc4cc5ce42adfecddb3ebae79cc257d52dfa59958616f48b1e995e577d8906f7
MD5 08ed1de856eb787ee85bbad3ab5b1216
BLAKE2b-256 c923f76ad0679f8016608f6164c6373840d7ce5fa5cd3f3f75eb5546241662d0

See more details on using hashes here.

File details

Details for the file pryti_contract-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pryti_contract-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 18.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.12

File hashes

Hashes for pryti_contract-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cf9f3f06bfabe701b11fb1bdd684a7c460660a1e257ff19500859cbb9a0d0404
MD5 453ee524de94d3c9addd62d77b66ce22
BLAKE2b-256 a91b52b4bfb0a7e7ec9a5e98530c0289bb5d35e75a420be0904d8b9c887effaa

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page