Skip to main content
[AVAILABLE]python/, v1.0.0. 26 passing tests, 0 placeholders. ruff/black/mypy all pass clean.

Install

pip install -e ./python
py.typed is shipped — this is a PEP 561 typed distribution, confirmed by installing into a clean venv and checking parmana.__file__’s directory for the marker.

Models are generated, not hand-maintained

Every model in python/parmana/models/*.py is generated directly from the TypeScript AST of packages/shared/src/domain/*.ts (and CryptoAlgorithms.ts) by python/scripts/generate_models.ts — not hand-aligned copies. A drift guard (npm run check:python-models, wired into CI) regenerates into memory and fails the build if the committed output would change:
$ ./node_modules/.bin/tsx python/scripts/generate_models.ts --check
generate_models --check: python/parmana/models/*.py are up to date.
Demonstrated to actually catch drift, not just pass trivially: hand-reverting a field name and re-running --check reports DRIFT DETECTED and exits 1. Enums are real Python str, Enum classes (e.g. SignatureAlgorithm, VerificationStatus), not bare strings.

Structured HTTP errors — they actually raise

from parmana import ConflictError, NotFoundError, ValidationError

try:
    client.execution.execute(transaction)
except ConflictError as exc:          # HTTP 409
    print(exc.status_code, exc)
StatusException
400ValidationError
401AuthenticationError
403ExecutionRejectedError
404NotFoundError
409ConflictError
5xxServerError
connection failureNetworkError
All inherit ParmanaHttpErrorApiError. Proven against the real HttpTransport (not a test double) using responses-mocked HTTP, one test per status code plus a connection failure case — 10 tests in python/tests/test_http_transport.py. The client also reuses a requests.Session() (connection pooling) and retries idempotent GETs with backoff on 502/503/504 — POSTs are never retried.

Every endpoint the API exposes has a method

client.execution.execute(transaction)          # POST /execute
client.execution.health()                       # GET /health
client.execution.version()                      # GET /version
client.verification.verify(id)                  # POST /verify        (fresh)
client.verification.get_latest(id)              # GET /verification/:id (cached)
client.receipt.generate(id)                     # POST /receipt
client.receipt.get_latest(id)                   # GET /receipt/latest/:id
client.replay.replay(id)                        # POST /replay
client.transactions.get(id) / .list()           # GET /transactions[/:id]
client.trust_records.get(id)                    # GET /trust-records/:id
client.policy.validate(policy_id, policy_version) # POST /policies/validate
policy.validate takes (policy_id, policy_version), not a policy document — matching what the route actually reads (see REST API).

Examples

Three runnable examples, each with its own README (prerequisites + real captured output): python/examples/quickstart/, python/examples/verify/, python/examples/content_binding/ (the last one demonstrates — honestly — what content-binding protection you do not get from the plain API; see Content Binding & TOCTOU).