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

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 or, for Flask/FastAPI, your SQLAlchemy mapped classes.

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.1.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.1.0
File Size Uploaded Provenance
lenscheck_contract-1.1.0.tar.gz 28.9 kB With provenance Details

Built distribution (wheel)

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

Total release size: 56.0 kB

Release files / lenscheck_contract-1.1.0.tar.gz

Download URL lenscheck_contract-1.1.0.tar.gz
Size 28.9 kB
Tags Source
SHA-256 checksum
How to use checksums
66460d6fd315bcc1029e14cc9dbf1f49ece7569e93f2a1e5a50e44d60e01ccbc
BLAKE2b-256 checksum
How to use checksums
dc94312487bb77cd918e16381ae69a4d41d5d42182927ba415c4ccac82e37550
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.1.0-py3-none-any.whl

Download URL lenscheck_contract-1.1.0-py3-none-any.whl
Size 27.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5a69284806b5c3b599b03adbc620ee4eb4882721c32c76c5deb832555accfcbf
BLAKE2b-256 checksum
How to use checksums
ffdd7ca80c263b3dd286130abb4abfac4c4afdcbf6041937d7ec7816bd408ae3
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.1.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.1.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