Runtimes

Python Runtime

The Python runtime is an independent implementation of the AppTheory contract — not a port of the Go runtime. It executes the 275 generic runner fixtures in the 287-vector corpus, including the SP09 MCP tier, SP12 OAuth tier, and SP13 objectstore tier; the Python suite also loads the shared MCP route-algebra expectations table and the M2 auth-facade acceptance tables and the two M3a provider/store conformance tables (JWKS validator, OIDC discovery bootstrap, route challenge, provider conformance, and store conformance). The remaining three CDK-facing facade tables (facade route-inventory, route-model, and transport-posture) are outside Python’s scope.

Install

Distribution is GitHub Releases only. PyPI is not used; AppTheory does not publish to it. Pin the release wheel and verify its checksum before installing it:

VERSION=1.14.0
TAG="v${VERSION}"
REPO="theory-cloud/AppTheory"

gh release download "${TAG}" --repo "${REPO}" \
  --pattern "apptheory-${VERSION}-py3-none-any.whl" \
  --pattern "SHA256SUMS.txt" \
  --clobber
grep " apptheory-${VERSION}-py3-none-any.whl$" SHA256SUMS.txt | sha256sum -c -
python -m pip install "./apptheory-${VERSION}-py3-none-any.whl"

Python 3.12+ is required. The floor is pinned by py/pyproject.toml, Ruff, Pyright, the pinned TableTheory GitHub Release wheel metadata, and CI. Do not document a different Python floor unless scripts/verify-runtime-floor-claims.sh passes with a CI matrix that includes both the floor and Python 3.14.

Module layout

Module Purpose
apptheory Core Python runtime exports: create_app, Context, request/response helpers, event builders, testkit helpers, MCP/OAuth helpers, jobs-ledger primitives, logging profiles, object-store helpers, and sanitization helpers. Rate-limiter classes are not root exports.
apptheory.mcp Streamable HTTP MCP server, registries, session/stream/task stores, SSE parsing, and deterministic test harness helpers.
apptheory.oauth Protected-resource metadata, bearer-token validation, DCR, PKCE, and Remote MCP OAuth helper surfaces.
apptheory.limited DynamoDB-backed rate limiter exports (DynamoRateLimiter, FixedWindowStrategy, SlidingWindowStrategy, MultiWindowStrategy).

See api-snapshots/py.txt for the exact exported surface — that file is the release gate.

Minimal app

from apptheory import create_app, text

app = create_app()

@app.get("/ping")
def ping(ctx):
    return text(200, "pong")

def handler(event, ctx):
    return app.handle_lambda(event, ctx)

handle_lambda is the single Lambda entrypoint for every AWS event shape. See Event Shape Dispatch for the dispatch table.

Tier selection

from apptheory import create_app

app = create_app(tier="p0")

Python tiers are string literals: "p0", "p1", or "p2".

See HTTP Runtime for what each tier includes.

Deterministic tests

from datetime import datetime, timezone
from apptheory import build_apigw_v2_request, create_test_env, text

def test_ping():
    env = create_test_env(now=datetime(2026, 1, 1, tzinfo=timezone.utc))
    env.ids.push("req-1")

    app = env.app()
    app.get("/ping", lambda ctx: text(200, "pong"))

    event = build_apigw_v2_request("GET", "/ping")
    resp = env.invoke_apigw_v2(app, event)

    assert resp["statusCode"] == 200

Route registration

app.handle("GET", "/users/{id}", handler)
app.get("/users/{id}", handler)

Normal fluent registration fails closed on invalid patterns, duplicates, and None handlers. handle_strict remains as a deprecated compatibility wrapper for callers that still need the old helper shape; it now raises AppTheoryError rather than ValueError for registration failures.

Object-store dependency posture

The Python package intentionally keeps boto3 optional and lazy for create_s3_object_store. Importing apptheory or using the fake object store does not require boto3; constructing the S3-backed store fails closed with a stable ObjectStoreError if boto3 or the required S3 methods are unavailable. This differs from TypeScript’s hard S3 SDK dependency by design and does not widen the object-store contract.

The S3-backed store also exposes the bounded upload grant (ObjectStoreUploadGranter#presign_put). Because botocore’s bundled S3 service model defaults to signature version s3, AppTheory constructs the boto3 client with Config(signature_version="s3v4"): a SigV2 presigned URL does not sign content-length at all and hoists content-type and x-amz-checksum-sha256 into the query string. The store additionally re-verifies every presigned URL and fails closed with ObjectStoreError(objectstore.invalid_store_config) if the constraints are not signed headers.

HTTP error format

from apptheory import create_app, HTTP_ERROR_FORMAT_FLAT_LEGACY

app = create_app(http_error_format=HTTP_ERROR_FORMAT_FLAT_LEGACY)

Applies to HTTP error serialization only.

What’s verified

The Python runner passes all 275 generic fixtures in the 287-vector corpus on every commit. It includes the SP09 MCP tier, SP12 OAuth tier, and SP13 objectstore tier; the Python suite also loads the shared MCP route-algebra expectations table and the M2 auth-facade acceptance tables and the two M3a provider/store conformance tables (JWKS validator, OIDC discovery bootstrap, route challenge, provider conformance, and store conformance), while the facade route-inventory, route-model, and transport-posture tables are consumed by Go and CDK-TS. Tests live under py/tests/ and are exercised by ./scripts/verify-python-tests.sh and make rubric.

Next reads