Skip to main content

lenscheck-contract

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

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

Works with Django, Flask, and FastAPI — it reads whichever framework's own runtime state, so routers, blueprints, loops, and dependency trees are all seen exactly as the app resolved them. Models come from Django or SQLAlchemy.

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 lenscheck-contract

Use it — three levels

Level 1: nothing to write

Works on an existing project with zero code changes. Point it at your app:

# Django — reads DJANGO_SETTINGS_MODULE, the URLconf, and the model registry
lenscheck-contract export --settings myproject.settings -o contract.json

# Flask / FastAPI — point at the app object (module:attr, or a create_app factory)
lenscheck-contract export --no-django --app myapp.main:app -o contract.json
lenscheck-contract export --no-django --app "myapp:create_app" -o contract.json

Celery jobs are found automatically when Celery is installed and your tasks are imported; point at a specific app with --celery myapp.celery:app, or skip discovery with --no-celery.

It reads the framework's real router — routes built in loops, DRF routers, mixins, blueprints, and FastAPI dependency trees are all found, because the app already resolved them at startup. Auth is read from Django permissions/mixins/login_required, Flask auth decorators (flask_login, flask_jwt_extended, …), and FastAPI security schemes and Depends(...). Models come from Django's registry, your SQLAlchemy mapped classes (Flask/FastAPI), and Pydantic request/response schemas (FastAPI) — so a field going optional or disappearing from an API body shows up in the diff. Celery background jobs (tasks + beat schedules) are discovered too, so a new nightly job — or a task that starts calling Stripe — is caught the same way a route change is.

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 lenscheck_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

Install the request hook for your framework (so every view is watched, not just decorated ones), then turn the guard on:

# Django — settings.py
MIDDLEWARE = ["lenscheck_contract.middleware.ContractMiddleware", ...]

# Flask
from lenscheck_contract import install_flask
install_flask(app)

# FastAPI
from lenscheck_contract import install_fastapi
install_fastapi(app)

# then, anywhere at startup (conftest.py, apps.py, main.py):
from lenscheck_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"))
lenscheck-contract suggest observed.json     # prints the decorators to paste in

In CI

- run: lenscheck-contract export --settings myproject.settings -o head.json
- run: git checkout ${{ github.base_ref }}
- run: lenscheck-contract export --settings myproject.settings -o base.json
- run: lenscheck-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 lenscheck-contract
pip install -e ".[dev]"
pytest
cd examples/demo && lenscheck-contract export --settings settings --root . -o /tmp/base.json

MIT.

Metadata

Release files for lenscheck-contract 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lenscheck-contract 1.2.0
File Size Uploaded Provenance
lenscheck_contract-1.2.0.tar.gz 33.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for lenscheck-contract 1.2.0
File Interpreter ABI Platform
lenscheck_contract-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 65.0 kB

Release files / lenscheck_contract-1.2.0.tar.gz

Download URL lenscheck_contract-1.2.0.tar.gz
Size 33.7 kB
Tags Source
SHA-256 checksum
How to use checksums
9d01b1aa576c5c665fea90afdafcebf27a574f37c1e74999c0e642e72126ff56
BLAKE2b-256 checksum
How to use checksums
6f690dc827a3e331660b590fcd9aed409dfe63f6c5a45aa3a96976cd55b0d59f
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 Sep 22, 2026.

Transparency log

Release files / lenscheck_contract-1.2.0-py3-none-any.whl

Download URL lenscheck_contract-1.2.0-py3-none-any.whl
Size 31.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0443dacd50fb4d0007e9ef678345eb1d3521c23e87ac6167e467b70aa9709e82
BLAKE2b-256 checksum
How to use checksums
feeeefe93e5a462ebd067315ea74e94d1e108eee30a4cea232b7cfbb6cd1dfd5
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 Sep 22, 2026.

Transparency log

Security for lenscheck-contract 1.2.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for lenscheck_contract-1.2.0.tar.gz

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 Sep 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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