lens-contract
The shared contract surface for the lens analyser family.
Every Python member exposes the same manifest, the same GET /health + GET /manifest
routes, and the same serve / manifest CLI subcommands. This package is that
common surface, so a contract change is one release here instead of an edit in every
analyser.
It is infrastructure, not an analyser (role: library): it does not appear in
the family table and is not auto-routable.
What it provides
| Helper | Replaces the per-repo… |
|---|---|
make_manifest(...) |
_version() try/except + MANIFEST dict in manifest.py |
add_contract_routes(app, MANIFEST) |
hand-written GET /health + GET /manifest |
make_app(MANIFEST, analyse) |
the whole api.py for the path-only common case |
upload_tempfile(content, filename) |
the POST /analyse upload→tempfile→cleanup dance |
run_contract_subcommands(...) |
the serve (+--reload) + manifest dispatch in cli.py |
add_cors(app, env_prefix=…) |
per-repo CORS (one impl; env-driven; never * by default) |
add_rate_limit(app, env_prefix=…) |
per-repo slowapi setup (opt-in; needs lens-contract[ratelimit]) |
Making any member Electron/browser-ready
add_cors lets any member front a browser or Electron app via env vars, no code change:
{PREFIX}_MODE=desktop→ allowlocalhost/127.0.0.1/file://origins (Electron)- otherwise → allow
{PREFIX}_ALLOWED_ORIGINS(comma-separated; defaults to the common dev ports)
add_rate_limit is opt-in via {PREFIX}_RATE_LIMIT_ENABLED=true (limit from {PREFIX}_RATE_LIMIT, e.g. 60/minute).
What it deliberately does not provide
Per the family principle share the contract, not forced abstraction: the
analyse() business logic, the human-readable CLI output (rich), and the rich
analyse-CLI flags stay in each repo. rich is not even a dependency here.
Usage
# manifest.py
from lens_contract import make_manifest
MANIFEST = make_manifest(
name="name-analyser", accepts=["..."], produces="NameAnalysis",
extensions=[".ext"], auto_routable=True,
)
# api.py — common case
from lens_contract import make_app
from name_analyser import NameAnalyser
app = make_app(MANIFEST, lambda path: NameAnalyser().analyse(path))
# cli.py
from lens_contract import run_contract_subcommands
def main():
if run_contract_subcommands(MANIFEST, app_path="name_analyser.api:app",
default_port=8010, env_prefix="NAME_ANALYSER"):
return
... # bespoke analyse argparse + human output
Members needing extra /analyse form fields (e.g. --llm) build their own app with
add_contract_routes + upload_tempfile — see conversation-analyser as the
reference adopter.
Develop
uv venv && uv pip install -e '.[dev]'
uv run pytest
Metadata
Release files for lens-contract 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lens_contract-0.3.1.tar.gz | 70.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lens_contract-0.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 81.0 kB
Release files / lens_contract-0.3.1.tar.gz
| Download URL | lens_contract-0.3.1.tar.gz |
|---|---|
| Size | 70.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d1da147ecdade5f4f04ceed75188a0c9a954e13a755ee08707036e1e47c991b2
|
|
BLAKE2b-256 checksum How to use checksums |
07b19c4f9ca2f480091ed18ee6006ef286d1ddb3ae1e94a60b008c5d7bb051e3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.0
|
Release files / lens_contract-0.3.1-py3-none-any.whl
| Download URL | lens_contract-0.3.1-py3-none-any.whl |
|---|---|
| Size | 10.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
88b4fd8cfa9fc94f83eab8080d5a1a08d0713f3ef7943b4c34e3386191990ea1
|
|
BLAKE2b-256 checksum How to use checksums |
03eec1372eca1a3062cf14332d03db659e85271ff542a51a304a212e1354a59a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.0
|