Skip to content

API Reference

This page is generated from the docstrings of the public veloce package — every name exported from the top-level namespace.

Veloce — Ultra-fast async Python web framework.

Veloce is a high-performance asynchronous web framework built on raw asyncio, httptools, and orjson. It pairs a small, well-typed API with predictable performance under load.

Basic usage::

from veloce import Veloce, Request

app = Veloce()

@app.get("/")
async def index(request: Request):
    return {"message": "Hello, World!"}

app.run()

URLRule

A single registered URL rule view object.

Iterable over its fields as (rule, methods, endpoint) so callers that just want tuple-unpack semantics work; full attribute access gives rule, methods, endpoint, defaults, host, etc. for introspection.

Veloce

Bases: AsgiMixin, DispatchMixin, ErrorsMixin, LifecycleMixin, MiddlewareMixin, MountingMixin, OpenAPIMixin, ServingMixin, TestingMixin, BackgroundTasksMixin, TemplatingMixin, Router

Ultra-fast async web framework.

Usage::

app = Veloce()

@app.get("/")
async def index(request: Request):
    return {"message": "Hello, World!"}

app.run()

debug property writable

debug: bool

Whether debug mode is enabled; bound to config['DEBUG'].

Interprets a dotenv-style string (DEBUG=false) correctly rather than treating any non-empty string as truthy.

secret_key property writable

secret_key: str | None

Session-signing secret; bound to config['SECRET_KEY'].

SessionMiddleware constructed without an explicit secret_key= resolves it from here on the first request, so app.secret_key = ... and config['SECRET_KEY'] are one and the same setting.

url_map property

url_map: _URLMap

Read-only mapping of registered URL rules.

Iterating it yields URLRule objects (rule, methods, endpoint). Subscript by endpoint name (app.url_map["users.detail"]) returns a list of rules registered under that endpoint. Length is the total registered route count.

This is the introspection-friendly view of Veloce.routes; callers who just want the dict-list keep using app.routes.

routes property

routes: list[dict[str, Any]]

List all registered routes.

json property writable

json: Any

Active JSONProvider instance.

Lazily instantiated from app.json_provider_class so swapping encoders is just: app.json_provider_class = MyJSONProvider. Setting app.json = instance replaces it explicitly.

package_root property

package_root: str

Filesystem path of the directory containing import_name's module.

Veloce exposes this as app.root_path; veloce already uses Veloce.root_path for the ASGI mount prefix, so we surface the package-directory variant under a non-conflicting name. Useful for resolving template / static directories relative to the app's source file.

jinja_env property

jinja_env: Any

The app's shared Jinja2 Environment.

Available once a template_folder has been configured (either via the constructor or by binding Jinja2Templates). Mutate it directly to register filters/globals or tweak settings: app.jinja_env.filters["money"] = fmt. Raises RuntimeError when no templating is configured.

jinja_loader property

jinja_loader: Any

The app's Jinja template loader.

The FileSystemLoader (or whatever loader the bound Jinja2Templates env uses). None when no templating is configured - Veloce returns None for an app with no template folder rather than raising.

instance_path property

instance_path: str

Writable instance folder beside the package.

Veloce resolves <package_root>/instance as a per-deployment writable directory for config, SQLite files, uploads, etc. An explicit instance_path= constructor argument overrides this computed default. The directory is not auto-created - the caller decides whether to mkdir it.

signal_namespace property

signal_namespace: Any

Accessor that returns the veloce.signals module.

Veloce ships its signals as module-level singletons, so this attribute returns the module - app.signal_namespace.request_started is the same Signal instance as veloce.signals.request_started.

aborter property writable

aborter: Any

Callable that raises typed HTTPExceptions by status code.

app.aborter(404) is equivalent to the module-level abort(404) helper. It is a distinct attribute so applications can subclass Aborter to add custom code-to-exception mappings; veloce returns a fresh Aborter instance per access so users can mutate _mapping per-app without affecting others.

got_first_request property

got_first_request: bool

True after the first request has been fully handled.

Read-only compatibility accessor. Useful when conditional setup depends on whether the app has bootstrapped yet, e.g. a before_first_request hook firing exactly once is reflected here as True.

cli property

cli: Any

Click Group for app-defined custom CLI commands.

Accessing app.cli lazily constructs a click.Group once. Custom commands attach via the standard Click decorator:

@app.cli.command("init-db")
def init_db():
    ...

The veloce console script automatically discovers and mounts the group as a custom subcommand when launched with an app reference. click is required at access time but not at import time - the ImportError is deferred and produces a useful message instead of a hard-import crash on environments that don't need the CLI.

view_functions property

view_functions: dict[str, Callable]

A {endpoint_name: handler} view of registered routes.

Endpoint names follow a simple rule - the route's name= kwarg, or the handler's __name__ when no name is set; blueprint routes are prefixed with <bpname>.. Returned dict is a fresh snapshot - mutation doesn't poison framework state.

error_handler_spec property

error_handler_spec: dict[Any, dict[Any, Callable]]

Inspection view of registered error handlers.

Returns a {blueprint_name_or_None: {key: handler}} mapping. App-level handlers live under the None key; each blueprint's handlers live under the blueprint's name, keyed by integer status code or exception class. Blueprint handlers are scoped to their own routes at dispatch time, so they appear under their blueprint name here, not folded into None.

before_request_funcs property

before_request_funcs: dict[Any, list[Callable]]

View of registered before_request hooks.

Returns {blueprint_name_or_None: [hook, ...]}. App-level hooks live under the None key; blueprint hooks under the blueprint's name. The dispatcher walks the None bucket plus the bucket whose name matches the matched route's endpoint prefix.

after_request_funcs property

after_request_funcs: dict[Any, list[Callable]]

Return the per-blueprint after-request hook registry.

teardown_request_funcs property

teardown_request_funcs: dict[Any, list[Callable]]

Return the per-blueprint teardown-request hook registry.

blueprints property

blueprints: dict[str, Any]

Snapshot mapping of bp.name -> Blueprint.

Returns a fresh copy, so caller mutations don't affect the framework. Re-registering the same name overwrites the previous entry.

url_value_preprocessors property

url_value_preprocessors: dict[Any, list[Callable]]

View of registered URL-value preprocessors.

Returns {blueprint_name_or_None: [fn, ...]}. Veloce flattens blueprint preprocessors into the app list at registration time, so the dict carries a single None key.

url_default_functions property

url_default_functions: dict[Any, list[Callable]]

View of registered URL-default callbacks.

dependency_overrides property writable

dependency_overrides: dict[Callable, Callable]

Mutable map of dependency callables to test replacements.

Populate it to swap a real dependency for a fake one in tests::

app.dependency_overrides[get_db] = get_fake_db

The resolver consults this map on every request, so changes take effect immediately. Assigning a fresh dict (or calling .clear()) removes all overrides.

route

route(path: Annotated[str, Doc('URL path template, including `{param}` / `{param:converter}` placeholders.')], methods: Annotated[list[str] | None, Doc('HTTP methods this handler serves; defaults to `GET`.')] = None, dependencies: Annotated[list[Any] | None, Doc('Dependencies run for this route, appended after the router-level ones.')] = None, response_model: Annotated[Any, Doc("Type used to filter and serialize the handler's return value and the OpenAPI response schema.")] = None, tags: Annotated[list[str] | None, Doc('OpenAPI tags for this route, combined with the router-level tags.')] = None, summary: Annotated[str | None, Doc('Short OpenAPI summary for this operation.')] = None, name: Annotated[str | None, Doc("Endpoint name for `url_for` reverse lookup; defaults to the handler's name.")] = None, description: Annotated[str | None, Doc("OpenAPI description; defaults to the handler's docstring.")] = None, deprecated: Annotated[bool, Doc('Mark the operation as deprecated in the OpenAPI document.')] = False, response_description: Annotated[str, Doc('Description of the successful response in the OpenAPI document.')] = MSG_SUCCESSFUL_RESPONSE, status_code: Annotated[int, Doc('Default HTTP status code for a successful response.')] = HTTP_200_OK, response_class: Annotated[Any, Doc('Response class for this route, overriding the router and framework defaults.')] = None, response_model_include: Annotated[set[str] | None, Doc('Fields to include when serializing the response model.')] = None, response_model_exclude: Annotated[set[str] | None, Doc('Fields to exclude when serializing the response model.')] = None, response_model_exclude_unset: Annotated[bool, Doc('Omit fields left unset on the response model from the serialized output.')] = False, response_model_exclude_defaults: Annotated[bool, Doc('Omit fields equal to their default on the response model from the serialized output.')] = False, response_model_by_alias: Annotated[bool, Doc('Serialize the response model using field aliases instead of attribute names.')] = False, response_model_exclude_none: Annotated[bool, Doc('Omit fields whose value is `None` from the serialized response model.')] = False, include_in_schema: Annotated[bool, Doc('Register the route but omit it from the generated OpenAPI document when False.')] = True, responses: Annotated[dict[int, dict[str, Any]] | None, Doc('Additional OpenAPI responses for this route, overlaid on the router-level ones.')] = None, operation_id: Annotated[str | None, Doc('Explicit OpenAPI `operationId`; defaults to the route name.')] = None, openapi_extra: Annotated[dict[str, Any] | None, Doc("Arbitrary dict deep-merged into this route's OpenAPI operation object.")] = None, defaults: Annotated[dict[str, Any] | None, Doc('Fixed values merged into the path params at dispatch without overriding URL-matched ones.')] = None, callbacks: Annotated[dict[str, Any] | None, Doc("OpenAPI Callback objects emitted verbatim into the operation's `callbacks` field.")] = None, strict_slashes: Annotated[bool | None, Doc('When False, match both slashed and unslashed forms; `None` defers to the app policy.')] = None, subdomain: Annotated[str | None, Doc('Constrain the route to a subdomain of `SERVER_NAME`; `*` matches any non-apex subdomain.')] = None, host: Annotated[str | None, Doc('Constrain the route to an exact `Host` header value (case-insensitive).')] = None, expose_as_mcp_tool: Annotated[bool, Doc('Expose the route as an MCP tool in the contrib MCP registry.')] = False, mcp_description: Annotated[str | None, Doc("LLM-facing description for the route's MCP tool, required when exposed as one.")] = None, expose_as_mcp_resource: Annotated[bool, Doc('Expose the read-only route as an MCP resource in the contrib MCP registry.')] = False, mcp_resource_uri: Annotated[str | None, Doc("Resource URI for the route's MCP resource: a static URI, or a URI template (`users://{user_id}`) binding its path parameters.")] = None, mcp_scopes: Annotated[Sequence[str] | None, Doc('Authorization scopes required to call this route over MCP.')] = None, mcp_icons: Annotated[Sequence[Any] | None, Doc('Optional MCP `Icon` objects a client may render next to the tool/resource.')] = None, mcp_task_support: Annotated[bool, Doc("Allow this route's MCP tool to run as a background task (task-augmented `tools/call`, polled via `tasks/get` / `tasks/result`).")] = False, exclude_middleware: Annotated[Sequence[str] | None, Doc('Names of middleware this route opts out of.')] = None, stream: Annotated[bool, Doc('Opt into request-body streaming: the body is not buffered before the handler, so the handler may consume `request.stream()` incrementally.')] = False) -> Callable

Generic route decorator.

exclude_middleware=["CSRFMiddleware"] opts this route out of the named middleware (matched against each middleware's name), so a webhook or health-check route can skip CSRF, auth, or rate limiting without forking the middleware. Routes that declare no exclusions pay no extra per-request cost.

trace

trace(path: str, **kwargs) -> Callable

TRACE route decorator - RFC 9110 Sec. 9.3.8.

query

query(path: str, **kwargs) -> Callable

QUERY route decorator - RFC 10008.

QUERY is safe and idempotent like GET but carries a request body like POST, for read-only operations whose parameters do not fit a URL (search, filtering, paging). The handler reads the body exactly as a POST handler does (request.get_json() / a body model parameter).

websocket

websocket(path: Annotated[str, Doc('URL path template for the WebSocket route, including `{param}` placeholders.')]) -> Callable

Register a WebSocket route via decorator.

websocket_listener

websocket_listener(path: str, *, receive: str = 'json', send: str = 'json', on_connect: RouteHandler | Callable[..., Any] | None = None, on_disconnect: RouteHandler | Callable[..., Any] | None = None) -> Callable

Declarative WebSocket route - wrap a per-message callback.

The decorated callback handles one message at a time; the framework owns the accept handshake, the receive loop, and the clean close on disconnect. The callback is called as cb(data), or cb(ws, data) when its first parameter is named ws/socket (or it takes two positional parameters). Returning a non-None value sends it back in send mode; returning None sends nothing.

receive/send select the codec ("json" default, or "text" / "bytes"). on_connect(ws) runs after accept; on_disconnect(ws) always runs when the loop ends, including on peer disconnect. Sync callbacks and hooks are offloaded to the executor.

Usage::

@app.websocket_listener("/echo")
async def echo(data):
    return data

For full control over the handshake and loop use @app.websocket.

add_websocket_route

add_websocket_route(path: Annotated[str, Doc('URL path template for the WebSocket route, including `{param}` placeholders.')], handler: Annotated[RouteHandler, Doc('Callable invoked with the accepted WebSocket connection when the route matches.')]) -> None

Register a WebSocket route imperatively (ASGI shape).

The non-decorator form of @app.websocket(path).

add_api_websocket_route

add_api_websocket_route(path: str, endpoint: RouteHandler, name: str | None = None) -> None

Register an imperative WebSocket route, mirroring add_api_route.

The non-decorator form of @app.websocket(path). name, when given, registers the route for reverse lookup so app.url_for(name) resolves to its path.

add_api_route

add_api_route(path: str, endpoint: RouteHandler, *, methods: list[str] | None = None, **kwargs: Any) -> None

Register a route imperatively.

The non-decorator form: the handler argument is named endpoint here and forwarded to add_route (where it is handler). All route kwargs - response_model, tags, dependencies, status_code, openapi_extra, ... - pass straight through. Defaults to ["GET"] when methods is omitted.

context_processor

context_processor(func: Callable) -> Callable

Register a template context processor. The function should return a dict that merges into the template context.

template_filter

template_filter(name: str | None = None) -> Callable

Register a function as a Jinja filter.

Usage::

@app.template_filter("upper")
def upper(s): return s.upper()

The filter becomes available in every Jinja2Templates render that runs inside this app's request scope. name defaults to the function's own __name__.

template_global

template_global(name: str | None = None) -> Callable

Register a callable as a Jinja global - accessible from any template by name. Same shape as template_filter.

add_template_global

add_template_global(func: Callable, name: str | None = None) -> None

Imperative equivalent of @template_global.

template_test

template_test(name: str | None = None) -> Callable

Register a Jinja test - used in {% if x is name %} constructs.

add_template_filter

add_template_filter(func: Callable, name: str | None = None) -> None

Imperative equivalent of @template_filter.

add_template_test

add_template_test(func: Callable, name: str | None = None) -> None

Imperative equivalent of @template_test.

get_spawned_task

get_spawned_task(name: str) -> Task[Any] | None

Return the named spawned task, or None if there is no such task.

cancel_spawned_task

cancel_spawned_task(name: str) -> bool

Cancel a named spawned task. Return whether a task was cancelled.

supervise

supervise(coro_factory: Callable[[], Coroutine[Any, Any, Any]], *, name: str, max_restarts: int = 5, restart_window: float = 60.0, backoff: float = 1.0, max_backoff: float = 30.0) -> Task[Any]

Run a long-lived coroutine, restarting it on failure.

coro_factory is a zero-argument callable that returns a fresh coroutine each time it is invoked - the supervisor calls it to start the task and again to restart after a crash, so a single coroutine object (which cannot be re-awaited) is not accepted. The supervised coroutine is expected to run for the application's lifetime; if it returns normally the supervisor restarts it, and if it raises the failure is logged and the coroutine is restarted after a bounded backoff delay. asyncio.CancelledError is never suppressed, so the task stops cleanly when cancelled at shutdown.

A count-within-window circuit breaker bounds runaway restarts: at most max_restarts restarts are allowed within any restart_window seconds. The restart counter resets whenever the coroutine runs for longer than the window without failing (a clean run), so steady-state restarts far apart never trip the breaker; a tight crash loop does. When the breaker trips the supervisor logs the give-up and stops restarting. backoff is the initial delay between restarts and doubles up to max_backoff on consecutive failures, resetting to backoff after a clean run.

The supervisor itself runs as an app.spawn(...) task, so it is tracked with a strong reference and cancelled-and-drained on shutdown like any other spawned task. name is required (the supervisor task is named so it is retrievable / cancellable via get_spawned_task / cancel_spawned_task); a duplicate name raises. Must be called with a running event loop.

Usage::

@app.on_startup
async def _start():
    app.supervise(lambda: poll_queue(), name="queue-poller")

test_client

test_client(**kwargs: Any) -> Any

Return an in-memory TestClient for this app.

app.test_client() is the factory API; the kwargs (e.g. follow_redirects=True, base_url=...) are forwarded to TestClient.__init__. Equivalent to TestClient(app, **kwargs) for callers that prefer the method form.

async_test_client

async_test_client(**kwargs: Any) -> Any

Return an AsyncTestClient for this app.

The async counterpart of test_client() - used as async with app.async_test_client() as client: inside an async test, so requests are awaited on the test's own running loop rather than driven through a private loop. Kwargs are forwarded to AsyncTestClient.__init__.

app_context

app_context() -> _AppContext

Bind current_app and reset g for use outside a request.

Use as with app.app_context(): .... CLI commands, background jobs, and tests need this when they want to read app.config or write into g without going through handle_request. Nestable: the previous binding (if any) is restored on exit.

test_request_context

test_request_context(path: str = '/', method: str = HTTP_METHOD_GET, headers: dict[str, str] | None = None, query_string: str = '', body: bytes = b'') -> _TestRequestContext

Synthesise a fake request for outside-request testing.

Inside with app.test_request_context(): ..., current_app, g, and the request-scoped contextvars resolve as if Veloce had just received that request - without spinning up the full dispatch pipeline. Strict subset of what handle_request does: no middleware, no DI, no handler.

run

run(host: str | None = None, port: int = 8000, workers: int = 1, access_log: bool = True, ssl_context: SSLContext | None = None, bind_all: bool = False, reload: bool = False) -> None

Start the built-in development server.

Veloce's from-scratch HTTP server is intended for local development only. For production, run the app under a hardened ASGI server - uvicorn your_module:app - which veloce is fully compatible with through its ASGI __call__ interface. run() logs a reminder of this on startup.

host resolves to "127.0.0.1" when unset so the dev server is reachable only from the local machine. Pass bind_all=True to opt in to all-interfaces binding ("0.0.0.0"). host and bind_all=True are mutually exclusive - passing both raises ValueError to avoid silent privilege widening. Binding to 0.0.0.0 exposes the dev server to every reachable network - including remote attackers if the machine is on a public network - so it should be used only in trusted environments and never with debug=True.

ssl_context - an ssl.SSLContext - turns on HTTPS for local testing; it is handed straight to loop.create_server(ssl=...). Left None (the default) the serving path is byte-for-byte the same as plain HTTP. Production should still terminate TLS at uvicorn or a reverse proxy.

workers must be 1: the built-in server runs a single process and does not pre-fork. Passing more raises ValueError - run under uvicorn module:app --workers N or the gunicorn VeloceWorker for multiple processes.

reload=True turns on the development auto-reloader: this process supervises a child that serves requests and restarts it whenever a project .py file changes. The watching happens in the supervisor, so the served child carries no overhead. It is a development aid - leave it off for any deployment.

openapi

openapi() -> dict[str, Any]

Return the generated OpenAPI schema dict.

Computes the schema on first call, caches the result in app.openapi_schema. Subsequent calls return the cached dict; users can mutate the result in place (e.g. to inject custom info.x-logo or tags orderings) and the swagger UI / json endpoints will serve the mutated copy.

To bypass the auto-build entirely, assign a custom dict to app.openapi_schema before any request lands.

mount

mount(prefix: str, app: Any) -> None

Mount a sub-application at a path prefix.

A veloce sub-app is dispatched through the parent's request pipeline. Any other ASGI application - an ASGI micro-app, an instrumentation shim - is dispatched at the ASGI layer instead: the matched prefix is stripped from the scope's path and moved onto root_path, so the mounted app sees a normal root-relative request.

Lifecycle: a mounted Veloce sub-app has its startup and shutdown driven by the parent - the parent runs each child's startup after its own during lifespan/run() startup, and tears children down in reverse on shutdown, so a child's on_startup / lifespan resources initialise and release without a separate ASGI lifespan. A mounted non-Veloce ASGI app receives http and websocket scopes only: the parent does not fan the lifespan cycle out to it, so it must not depend on ASGI lifespan events for its setup. A mounted ASGI app owns its entire prefix subtree - a native route registered under the same prefix is unreachable.

Prefixes must not overlap: registering a prefix equal to, nested under, or containing an existing mount raises ValueError, since overlapping mounts would shadow each other in a confusing, order-dependent way.

mount_static

mount_static(prefix: str = '/static', directory: str = 'static', html: bool = False, must_exist: bool = True) -> None

Mount a static file directory.

The directory must exist and be readable at wiring time (a typo otherwise 404s every asset silently); pass must_exist=False to downgrade the check to a warning when the directory is created after the app is constructed.

add_middleware

add_middleware(middleware: Any, **options: Any) -> None

Add middleware to the pipeline.

Call forms:

  • add_middleware(VeloceMiddlewareClass, **options) - a class subclassing Middleware is instantiated with **options and appended to the request/response pipeline.
  • add_middleware(instance) - append an already-built Middleware instance directly.
  • add_middleware(ASGIMiddlewareClass, **options) - a class that is not a Middleware subclass is treated as a standard ASGI middleware: it wraps the whole application and is instantiated as ASGIMiddlewareClass(app, **options) when the ASGI stack is assembled. This is what lets third-party ASGI middleware (observability, tracing, profiling, ...) plug in. Middleware added first is the outermost wrapper.

Pass name= to override the instance's exclusion name (the identifier exclude_middleware=[...] on a route references). The override is applied after construction rather than forwarded into the subclass constructor, so per-instance naming works for every Middleware subclass - including user subclasses whose __init__ does not accept a name keyword.

Pass priority= (an int, default 0) to order this middleware deterministically regardless of registration order. Higher priority runs earlier in the request phase and correspondingly later in the response phase; middleware of equal priority keeps registration order (a stable tiebreak). The ordered chain is resolved once at registration time, so per-request dispatch pays no sorting cost. When no middleware sets a priority the behaviour is unchanged - the chain is the plain registration order it has always been. priority applies to the request/response Middleware pipeline only, not to ASGI-class middleware (which is ordered by its own wrap nesting).

add_http_middleware

add_http_middleware(middleware: Any) -> Any

Register a BaseHTTPMiddleware-style middleware on the (request, call_next) -> response chain. Accepts an instance, a bare callable, or a class (which is instantiated with no args). Returns the registered object so it can be used as a decorator.

middleware

middleware(middleware_class_or_type: type | str, **kwargs) -> Any

Add middleware - supports both a class form and a decorator form.

Class form: app.middleware(CORSMiddleware, allow_origins=["*"]) Decorator form: @app.middleware("http") async def add_header(request, call_next): response = await call_next(request) response.headers["X-Custom"] = "value" return response

before_request

before_request(func: Callable) -> Callable

Register a function to run before each request.

before_first_request

before_first_request(func: Callable) -> Callable

Register a function to run exactly once on the first request.

A legacy hook style - lifespan startup handlers are preferred, but first-request hooks are still a common pattern, so both are supported. Hooks fire serially in registration order; single-fire is guarded with an asyncio.Lock so concurrent first requests don't double-run the callbacks.

after_request

after_request(func: Callable) -> Callable

Register a function to run after each request.

teardown_request

teardown_request(func: Callable) -> Callable

Register a function to run after request teardown. Called with an optional exception argument, even if an exception occurred.

teardown_appcontext

teardown_appcontext(func: Callable) -> Callable

Register a function to run on app-context teardown.

on_event

on_event(event: str) -> Callable

Register startup/shutdown event handlers.

Deprecated: use @app.on_startup / @app.on_shutdown instead. Scheduled for removal in v1.0.0.

on_startup

on_startup(func: Callable) -> Callable

Register a startup event handler.

on_shutdown

on_shutdown(func: Callable) -> Callable

Register a shutdown event handler.

add_event_handler

add_event_handler(event: str, func: Callable) -> None

Imperative event-handler registration - ASGI shape.

Deprecated: call app.on_startup(fn) / app.on_shutdown(fn) directly instead. Scheduled for removal in v1.0.0.

before_serving

before_serving(func: Callable) -> Callable

Register a coroutine to run once at app startup.

after_serving

after_serving(func: Callable) -> Callable

Register a coroutine to run once at app shutdown.

lifespan_context

lifespan_context() -> _LifespanManager

Return an async context manager driving the lifespan cycle.

async with app.lifespan_context(): ... runs the full startup sequence (lifespan CM enter + on_startup handlers) on entry and the shutdown sequence on exit - independent of any request. Useful for tests and for embedding the app where you want startup/shutdown without an ASGI server in the loop.

register_error_handler

register_error_handler(code_or_exception: int | type, func: Callable) -> None

Register an error handler without a decorator.

exception_handler

exception_handler(exc_class_or_status: type | int) -> Callable

Register a custom exception handler by exception type or status code.

add_exception_handler

add_exception_handler(exc_class_or_status: type | int, handler: Callable) -> None

Imperative exception-handler registration - ASGI shape.

The non-decorator form of @app.exception_handler(...). Accepts an exception class (matched by MRO at dispatch time) or an int HTTP status code.

log_exception

log_exception(exc: BaseException) -> None

Log an exception with traceback.

Routes the exception through the app logger at ERROR level. Used internally before falling back to a 500 response; exposed publicly so error-handler code can re-log via the same path.

handle_http_exception async

handle_http_exception(exc: HTTPException, request: Request | None = None) -> Response

Build the response for an HTTPException.

Walks registered status-code + class handlers first (matching abort() semantics), falling back to JSON {"detail": exc.detail} with exc.headers applied. Useful for code paths outside the normal request cycle (e.g. background tasks) that want framework-consistent error shapes.

Pass request= when calling from inside a request scope so the registered error handler receives the real failing request (with the actual path, method, path_params, state, etc.) instead of a synthetic GET /. Callers without a request (the original out-of-band use case) can omit it.

handle_user_exception async

handle_user_exception(exc: BaseException, request: Request | None = None) -> Response

Dispatch an arbitrary exception.

HTTPException -> handle_http_exception. Otherwise walks registered class handlers (MRO); on no match, logs via log_exception and returns 500. Pass request= to propagate the real failing request to the registered handler; omit to get a synthetic GET / for out-of-band callers (background tasks, CLI hooks).

include_router

include_router(router: Any, prefix: str = '', url_prefix: str | None = None) -> None

Mount a sub-router include_router.

Accepts either a Blueprint (delegates to register_blueprint, honouring its hooks / error handlers / url processors) or a plain Router (delegates to Router.include_router). The prefix and url_prefix are interchangeable; both spellings spells it prefix, Veloce spells it url_prefix.

add_instrumentation

add_instrumentation(hook: Callable | None = None, *, exclude_routes: Iterable[str] | None = None) -> Callable

Register an observability instrumentation hook.

hook is called once per finished HTTP request with a RequestMetrics record - the request method, the concrete path, the matched route template (a low-cardinality metric label), the status code, and the wall-clock duration in milliseconds. It may be a plain function or a coroutine function. A hook that raises is logged and skipped, so instrumentation never breaks a response.

Returns hook unchanged, so it also works as a decorator. Both the no-argument and the keyword-argument decorator forms are supported - when hook is omitted a decorator is returned that captures exclude_routes and registers the function it wraps:

@app.add_instrumentation
def export(metrics):
    statsd.timing(metrics.route or "unmatched", metrics.duration_ms)

@app.add_instrumentation(exclude_routes={"/health"})
def export(metrics):
    statsd.timing(metrics.route or "unmatched", metrics.duration_ms)

Pass exclude_routes to suppress this hook for noisy routes - a set of matched route templates (e.g. {"/health", "/metrics"}). When a finished request's route template is in the set the hook is skipped, so health checks and scrape endpoints never pollute traces or metric series. Matching is on the low-cardinality template resolved during routing (never the concrete, attacker-controlled path), so there is no per-request regex and no path-normalisation bypass. The filter is applied in the core delivery loop, so every consumer of this hook - tracing, metrics, access logs, custom - honours the same exclusion. An unmatched request (route template None) is never excluded by a named-route set.

With no hook registered the request path carries no instrumentation cost - not even a clock read.

use_secure_defaults

use_secure_defaults() -> None

Apply a security-hardened configuration baseline.

  • Marks the session cookie Secure, HttpOnly, and (unless already configured) SameSite=Lax.
  • Registers SecurityHeadersMiddleware - nosniff, frame-deny, a referrer policy, and a one-year HSTS max-age - unless one is already present.

Call once after construction, before serving. Production-oriented: the Secure cookie flag means cookies are not sent over plain HTTP, so do not call this for local HTTP development.

security_audit

security_audit() -> list[str]

Return human-readable warnings about the current security posture.

An empty list means nothing was flagged. Drives the veloce check CLI command and is also callable directly from a pre-deploy script or a test.

send_static_file

send_static_file(filename: str) -> Any

Serve a file from app.static_folder.

app.static_folder defaults to "static" (relative to app.package_root). Use app.static_url_path to control the URL prefix when mounting via app.static(...). Returns a FileResponse; traversal-safe via safe_join.

This reads the file synchronously and emits a DeprecationWarning when called on a running loop. From async handlers, prefer send_static_file_async.

send_static_file_async async

send_static_file_async(filename: str) -> Any

Serve a file from app.static_folder - async variant.

Reads the file in an executor via send_from_directory_async, so it never blocks the event loop. Prefer this from async handlers over the sync send_static_file.

test_cli_runner

test_cli_runner(**kwargs: Any) -> Any

Return a Click CliRunner bound for testing app.cli.

Veloce exposes this for unit-testing @app.cli.command(...) handlers without manual Click import. Kwargs flow through to click.testing.CliRunner.

dispatch_request async

dispatch_request(request: Request) -> Any

Alias for _dispatch_request.

full_dispatch_request async

full_dispatch_request(request: Request) -> Any

Alias for _dispatch_request (which already runs the full before/after-request hook chain inline).

preprocess_request async

preprocess_request(request: Request) -> Any

Run all before_request hooks for request.

Walks the registered hooks in order; if any hook returns a non-None value it short-circuits the chain and that value is returned (the contract - a non-None return becomes the response). Both sync and async hooks are supported. App-level hooks fire first, then the matched-blueprint bucket - the same shape _dispatch_request uses.

process_response async

process_response(request: Request, response: Any) -> Any

Run all after_request hooks for (request, response).

Hooks fire in reverse registration order; each hook may return a replacement response (the contract: a None return keeps the existing response). App-level hooks reverse-iterate first, then the matched-blueprint bucket - mirrors _dispatch_request's ordering.

ensure_sync staticmethod

ensure_sync(func: Callable) -> Callable

Wrap func so it is callable from synchronous code.

  • If func is a regular function, returns it unchanged.
  • If func is a coroutine function, returns a sync wrapper that runs the coroutine to completion on a dedicated event loop and returns the result.

Use to bridge async handlers / hooks into sync code (CLI commands, background workers, test scaffolding).

make_response

make_response(value: Any) -> Response

Coerce a handler-return value into a Response.

Accepts (with this coercion table): - Response -> returned as-is - str / bytes -> wrapped as a text/HTML response - dict / list -> wrapped as a JSON response via jsonify - tuple of (body,), (body, status), (body, status, headers), or (body, headers) -> unpacked and re-coerced

endpoint

endpoint(name: str) -> Callable

Decorator attaching a function as the view for name on an already-registered route.

Useful when separating route declaration (via app.add_url_rule(rule, endpoint="x")) from view registration. Replaces the existing route's handler in place.

iter_blueprints

iter_blueprints() -> Any

Iterate over every registered Blueprint.

Returns the blueprints in registration order (Python 3.7+ dict insertion order). Yields the Blueprint objects, not their names.

shell_context_processor

shell_context_processor(func: Callable) -> Callable

Register a function returning a dict to merge into veloce shell.

each processor is called with no args; its dict becomes part of the namespace the interactive shell starts with. Useful for surfacing models / db sessions / common helpers so User.query.first() works without a manual from myapp.models import User every time.

make_shell_context

make_shell_context() -> dict[str, Any]

Build the dict the CLI's shell command drops into.

Always includes app (this Veloce instance) and g. Each registered shell-context-processor's return dict overlays on top, in registration order - later processors win on conflicts.

url_value_preprocessor

url_value_preprocessor(func: Callable) -> Callable

Register a function fn(endpoint, values) that can mutate the matched path params before the handler runs.

Usage::

@app.url_value_preprocessor
def pull_lang(endpoint, values):
    from veloce import g
    g.lang = values.pop("lang", "en")

endpoint is the route name; values is the path_params dict (mutating it in place is the supported way to remove / rewrite values before the handler sees them).

url_for

url_for(name: str, **path_params: Any) -> str

Veloce.url_for runs @app.url_defaults callbacks before delegating to Router.url_for, so injected defaults appear in the rendered URL.

On build failure (unknown endpoint or missing path parameter), each registered app.url_build_error_handlers callback is invoked with (error, endpoint, values) in order; the first non-None return is used. If none recovers, a BuildError is raised.

url_path_for

url_path_for(name: str, **path_params: Any) -> str

Resolve a URL path by endpoint name and parameters.

url_defaults

url_defaults(func: Callable) -> Callable

Register a function fn(endpoint, values) that injects default kwargs into every url_for / url_path_for call.

Usage::

@app.url_defaults
def add_lang(endpoint, values):
    from veloce import g
    values.setdefault("lang", g.get("lang", "en"))

Runs in registration order; mutate values in place.

register_blueprint

register_blueprint(blueprint: Any, url_prefix: str | None = None) -> None

Mount a Blueprint's routes + hooks onto this app.

  • Re-registers each route under (url_prefix or bp.url_prefix) + path so the same blueprint can be mounted twice (e.g. v1/v2 versions).
  • Splices the blueprint's before_request / after_request / teardown_request hooks into the app's own lists. Blueprint hooks fire only for blueprint-routed requests (gated via request.endpoint starting with "<bpname>."); we tag the blueprint's hooks so the dispatcher can filter.
  • Buckets blueprint-level error handlers under the blueprint name (and each nested child under its dotted name), scoped to that blueprint's own routes; an app-level handler still catches everything as a fallback.

Mountable multiple times on different apps with different prefixes - the blueprint itself stays unmodified.

add_url_rule

add_url_rule(rule: Annotated[str, Doc('URL path template, including `{param}` / `{param:converter}` placeholders.')], endpoint: Annotated[str | None, Doc('Endpoint name for `url_for`; required when registering an endpoint-only stub.')] = None, view_func: Annotated[Callable | None, Doc('Handler for the route; `None` registers an endpoint-only stub for later attachment.')] = None, methods: Annotated[list[str] | None, Doc('HTTP methods this rule serves; defaults to `GET`.')] = None, **kwargs: Any) -> None

Add a URL rule programmatically.

view_func=None registers an endpoint-only stub: the route exists for url_for resolution but has no handler yet. Attach one later with @app.endpoint(endpoint). Calling such a route before a handler is attached raises a clear RuntimeError. Requires endpoint to be set in the stub case.

dependency_overrides_provider

dependency_overrides_provider() -> dict[Callable, Callable]

Return the dependency override mapping.

mcp_tool

mcp_tool(description: str, *, name: str | None = None, namespace: str | None = None, scopes: Sequence[str] | None = None, icons: Sequence[Icon] | None = None, task_support: bool = False) -> Callable

Register an MCP-only tool callable by an AI agent (contrib.mcp).

The decorated coroutine (or sync function) becomes an MCP tool whose input JSON Schema is derived from its signature; Depends() params resolve through the same dependency machinery routes use, with an MCPContext standing in for the HTTP Request. description is the required LLM-facing text (separate from the docstring). namespace prefixes the tool name (<namespace>_<name>), mirroring how a blueprint namespaces an exposed route. icons is an optional list of Icon objects a client may render next to the tool. task_support=True lets a client run the tool as a background task (task-augmented tools/call, polled via tasks/get / tasks/result).

Usage::

@app.mcp_tool(description="Add two integers")
async def add(a: int, b: int) -> int:
    return a + b

mcp_prompt

mcp_prompt(description: str, *, name: str | None = None, namespace: str | None = None, scopes: Sequence[str] | None = None, icons: Sequence[Icon] | None = None) -> Callable

Register an MCP prompt template fetchable by an AI agent (contrib.mcp).

The decorated callable's parameters become the prompt's arguments, and its return - a string, or a list of role/content messages - becomes the messages prompts/get returns. Depends() params resolve through the same dependency machinery routes use, with an MCPContext standing in for the HTTP Request. description is the required LLM-facing text; namespace prefixes the prompt name (<namespace>_<name>). icons is an optional list of Icon objects a client may render next to the prompt.

Usage::

@app.mcp_prompt(description="Summarise a topic in three bullets")
async def summarise(topic: str) -> str:
    return f"Summarise {topic} in three bullet points."

mcp_completer

mcp_completer(*, argument: str, prompt: str | None = None, resource: str | None = None) -> Callable

Register an argument-value completer for an MCP prompt or resource (contrib.mcp).

The decorated callable suggests values for one argument of a prompt (named) or a resource (by URI template) as the user types, answering the MCP completion/complete request. It is called with the partial value and a mapping of the sibling argument values already resolved, and returns a sequence of candidate strings (or a CompletionResult for explicit totals). Pass exactly one of prompt or resource. An argument with no registered completer answers with an empty completion.

Usage::

@app.mcp_completer(prompt="greet", argument="name")
async def complete_name(value: str, context: dict[str, str]) -> list[str]:
    return [n for n in KNOWN_NAMES if n.startswith(value)]

mount_mcp

mount_mcp(transport: str = 'stdio', *, path: str = '/mcp', auth: Any = None, principal: Any = None, allowed_origins: Sequence[str] | None = None, exclude_middleware: Sequence[str] | None = None, sessions: bool = False, resumable: bool = False) -> Any

Build the MCP server and serve the registered tools.

Assembles the tool registry from @app.mcp_tool registrations plus every route flagged expose_as_mcp_tool=True, the resource registry from every read-only route flagged expose_as_mcp_resource=True, and the prompt registry from @app.mcp_prompt registrations, then serves them over the chosen transport.

transport="stdio" (the default) serves JSON-RPC 2.0 on stdin/stdout for subprocess use and returns an awaitable serve coroutine that runs until stdin closes, inside the app's lifespan_context() - so every on_startup handler runs before the first tool is served. Schedule it explicitly (asyncio.run(app.mount_mcp())). A local subprocess is trusted, so authentication is from the environment: pass a principal (a veloce.Principal) to establish the identity / scopes the served tools run under.

transport="http" mounts the Streamable HTTP transport as a POST route at path (default /mcp) on this app and returns None; serve the app with any ASGI server (or app.run()) as usual. Pass auth (a veloce.contrib.mcp.MCPAuth) to make the endpoint an OAuth 2.1 resource server - validating the bearer token on every request and serving the RFC 9728 metadata. allowed_origins enables Origin validation (DNS-rebinding defense); exclude_middleware names app middleware the transport routes opt out of (an app-wide auth middleware auth replaces). sessions opts into Mcp-Session-Id lifecycle: the server assigns a session id on initialize, requires it on later requests (400 missing, 404 once terminated), and accepts a DELETE to terminate it. resumable opts into SSE resumability: each streamed event gets an id encoding its stream, and a GET carrying Last-Event-ID replays only that stream's missed events so a client can reconnect after a dropped connection. Call this after the tool / resource / prompt routes are registered.

BackgroundTask

A single background task.

run async

run() -> None

Execute the background task.

BackgroundTasks

Collection of background tasks to run after response.

add_task

add_task(func: Callable, *args: Any, **kwargs: Any) -> None

Append a task to the queue.

run_all async

run_all() -> None

Execute all queued tasks sequentially.

Blueprint

Bases: Router

Deferred-registration route collection.

add_route

add_route(path: Annotated[str, Doc('URL path template, including `{param}` / `{param:converter}` placeholders.')], handler: Annotated[RouteHandler, Doc('Async or sync callable invoked when the route matches.')], methods: Annotated[list[str], Doc('HTTP methods this handler serves (uppercased internally).')], dependencies: Annotated[list[Any] | None, Doc('Dependencies run for this route, appended after the router-level ones.')] = None, response_model: Annotated[Any, Doc("Type used to filter and serialize the handler's return value and the OpenAPI response schema.")] = None, tags: Annotated[list[str] | None, Doc('OpenAPI tags for this route, combined with the router-level tags.')] = None, summary: Annotated[str | None, Doc('Short OpenAPI summary for this operation.')] = None, name: Annotated[str | None, Doc("Endpoint name for `url_for` reverse lookup; defaults to the handler's name.")] = None, description: Annotated[str | None, Doc("OpenAPI description; defaults to the handler's docstring.")] = None, deprecated: Annotated[bool, Doc('Mark the operation as deprecated in the OpenAPI document.')] = False, response_description: Annotated[str, Doc('Description of the successful response in the OpenAPI document.')] = MSG_SUCCESSFUL_RESPONSE, status_code: Annotated[int, Doc('Default HTTP status code for a successful response.')] = HTTP_200_OK, response_class: Annotated[Any, Doc('Response class for this route, overriding the router and framework defaults.')] = None, response_model_include: Annotated[set[str] | None, Doc('Fields to include when serializing the response model.')] = None, response_model_exclude: Annotated[set[str] | None, Doc('Fields to exclude when serializing the response model.')] = None, response_model_exclude_unset: Annotated[bool, Doc('Omit fields left unset on the response model from the serialized output.')] = False, response_model_exclude_defaults: Annotated[bool, Doc('Omit fields equal to their default on the response model from the serialized output.')] = False, response_model_by_alias: Annotated[bool, Doc('Serialize the response model using field aliases instead of attribute names.')] = False, response_model_exclude_none: Annotated[bool, Doc('Omit fields whose value is `None` from the serialized response model.')] = False, include_in_schema: Annotated[bool, Doc('Register the route but omit it from the generated OpenAPI document when False.')] = True, responses: Annotated[dict[int, dict[str, Any]] | None, Doc('Additional OpenAPI responses for this route, overlaid on the router-level ones.')] = None, operation_id: Annotated[str | None, Doc('Explicit OpenAPI `operationId`; defaults to the route name.')] = None, openapi_extra: Annotated[dict[str, Any] | None, Doc("Arbitrary dict deep-merged into this route's OpenAPI operation object.")] = None, defaults: Annotated[dict[str, Any] | None, Doc('Fixed values merged into the path params at dispatch without overriding URL-matched ones.')] = None, callbacks: Annotated[dict[str, Any] | None, Doc("OpenAPI Callback objects emitted verbatim into the operation's `callbacks` field.")] = None, strict_slashes: Annotated[bool | None, Doc('When False, match both slashed and unslashed forms; `None` defers to the app policy.')] = None, subdomain: Annotated[str | None, Doc('Constrain the route to a subdomain of `SERVER_NAME`; `*` matches any non-apex subdomain.')] = None, host: Annotated[str | None, Doc('Constrain the route to an exact `Host` header value (case-insensitive).')] = None, expose_as_mcp_tool: Annotated[bool, Doc('Expose the route as an MCP tool in the contrib MCP registry.')] = False, mcp_description: Annotated[str | None, Doc("LLM-facing description for the route's MCP tool, required when exposed as one.")] = None, expose_as_mcp_resource: Annotated[bool, Doc('Expose the read-only route as an MCP resource in the contrib MCP registry.')] = False, mcp_resource_uri: Annotated[str | None, Doc("Resource URI for the route's MCP resource: a static URI, or a URI template (`users://{user_id}`) binding its path parameters.")] = None, mcp_scopes: Annotated[Sequence[str] | None, Doc('Authorization scopes required to call this route over MCP.')] = None, mcp_icons: Annotated[Sequence[Any] | None, Doc('Optional MCP `Icon` objects a client may render next to the tool/resource.')] = None, mcp_task_support: Annotated[bool, Doc("Allow this route's MCP tool to run as a background task (task-augmented `tools/call`, polled via `tasks/get` / `tasks/result`).")] = False, exclude_middleware: Annotated[Sequence[str] | None, Doc('Names of middleware this route opts out of.')] = None, stream: Annotated[bool, Doc('Opt into request-body streaming: the body is not buffered before the handler, so the handler may consume `request.stream()` incrementally. The synchronous body accessors are unavailable on a streaming route until the body is drained.')] = False) -> None

Register a route in the radix tree.

strict_slashes=False matches both the slashed and unslashed forms without redirecting. None (default) defers to the app's global redirect_slashes policy.

subdomain="api" constrains the route to requests whose Host header matches {subdomain}.{app.config["SERVER_NAME"]}. The match is exact (no globbing); for wildcard subdomain matching use subdomain="*" and inspect request.subdomain inside the handler.

match

match(method: str, path: str) -> RouteMatch | None

Match a request path. Static map, then radix tree, then regex.

O(1) for a literal path (the static map), else O(k) on the tree where k = path depth. The regex fallback runs only when the tree misses and regex routes are registered; the tree always wins over regex when both could match.

get_allowed_methods

get_allowed_methods(path: str) -> list[str]

Get allowed methods for a path (for 405 responses).

Unions the methods reachable through the radix tree AND any regex routes that match the same path, so a path served by a tree handler on one method and a regex handler on another reports both for 405/OPTIONS. Tree methods are listed first (dispatch precedence); duplicates removed.

route

route(path: Annotated[str, Doc('URL path template, including `{param}` / `{param:converter}` placeholders.')], methods: Annotated[list[str] | None, Doc('HTTP methods this handler serves; defaults to `GET`.')] = None, dependencies: Annotated[list[Any] | None, Doc('Dependencies run for this route, appended after the router-level ones.')] = None, response_model: Annotated[Any, Doc("Type used to filter and serialize the handler's return value and the OpenAPI response schema.")] = None, tags: Annotated[list[str] | None, Doc('OpenAPI tags for this route, combined with the router-level tags.')] = None, summary: Annotated[str | None, Doc('Short OpenAPI summary for this operation.')] = None, name: Annotated[str | None, Doc("Endpoint name for `url_for` reverse lookup; defaults to the handler's name.")] = None, description: Annotated[str | None, Doc("OpenAPI description; defaults to the handler's docstring.")] = None, deprecated: Annotated[bool, Doc('Mark the operation as deprecated in the OpenAPI document.')] = False, response_description: Annotated[str, Doc('Description of the successful response in the OpenAPI document.')] = MSG_SUCCESSFUL_RESPONSE, status_code: Annotated[int, Doc('Default HTTP status code for a successful response.')] = HTTP_200_OK, response_class: Annotated[Any, Doc('Response class for this route, overriding the router and framework defaults.')] = None, response_model_include: Annotated[set[str] | None, Doc('Fields to include when serializing the response model.')] = None, response_model_exclude: Annotated[set[str] | None, Doc('Fields to exclude when serializing the response model.')] = None, response_model_exclude_unset: Annotated[bool, Doc('Omit fields left unset on the response model from the serialized output.')] = False, response_model_exclude_defaults: Annotated[bool, Doc('Omit fields equal to their default on the response model from the serialized output.')] = False, response_model_by_alias: Annotated[bool, Doc('Serialize the response model using field aliases instead of attribute names.')] = False, response_model_exclude_none: Annotated[bool, Doc('Omit fields whose value is `None` from the serialized response model.')] = False, include_in_schema: Annotated[bool, Doc('Register the route but omit it from the generated OpenAPI document when False.')] = True, responses: Annotated[dict[int, dict[str, Any]] | None, Doc('Additional OpenAPI responses for this route, overlaid on the router-level ones.')] = None, operation_id: Annotated[str | None, Doc('Explicit OpenAPI `operationId`; defaults to the route name.')] = None, openapi_extra: Annotated[dict[str, Any] | None, Doc("Arbitrary dict deep-merged into this route's OpenAPI operation object.")] = None, defaults: Annotated[dict[str, Any] | None, Doc('Fixed values merged into the path params at dispatch without overriding URL-matched ones.')] = None, callbacks: Annotated[dict[str, Any] | None, Doc("OpenAPI Callback objects emitted verbatim into the operation's `callbacks` field.")] = None, strict_slashes: Annotated[bool | None, Doc('When False, match both slashed and unslashed forms; `None` defers to the app policy.')] = None, subdomain: Annotated[str | None, Doc('Constrain the route to a subdomain of `SERVER_NAME`; `*` matches any non-apex subdomain.')] = None, host: Annotated[str | None, Doc('Constrain the route to an exact `Host` header value (case-insensitive).')] = None, expose_as_mcp_tool: Annotated[bool, Doc('Expose the route as an MCP tool in the contrib MCP registry.')] = False, mcp_description: Annotated[str | None, Doc("LLM-facing description for the route's MCP tool, required when exposed as one.")] = None, expose_as_mcp_resource: Annotated[bool, Doc('Expose the read-only route as an MCP resource in the contrib MCP registry.')] = False, mcp_resource_uri: Annotated[str | None, Doc("Resource URI for the route's MCP resource: a static URI, or a URI template (`users://{user_id}`) binding its path parameters.")] = None, mcp_scopes: Annotated[Sequence[str] | None, Doc('Authorization scopes required to call this route over MCP.')] = None, mcp_icons: Annotated[Sequence[Any] | None, Doc('Optional MCP `Icon` objects a client may render next to the tool/resource.')] = None, mcp_task_support: Annotated[bool, Doc("Allow this route's MCP tool to run as a background task (task-augmented `tools/call`, polled via `tasks/get` / `tasks/result`).")] = False, exclude_middleware: Annotated[Sequence[str] | None, Doc('Names of middleware this route opts out of.')] = None, stream: Annotated[bool, Doc('Opt into request-body streaming: the body is not buffered before the handler, so the handler may consume `request.stream()` incrementally.')] = False) -> Callable

Generic route decorator.

exclude_middleware=["CSRFMiddleware"] opts this route out of the named middleware (matched against each middleware's name), so a webhook or health-check route can skip CSRF, auth, or rate limiting without forking the middleware. Routes that declare no exclusions pay no extra per-request cost.

trace

trace(path: str, **kwargs) -> Callable

TRACE route decorator - RFC 9110 Sec. 9.3.8.

query

query(path: str, **kwargs) -> Callable

QUERY route decorator - RFC 10008.

QUERY is safe and idempotent like GET but carries a request body like POST, for read-only operations whose parameters do not fit a URL (search, filtering, paging). The handler reads the body exactly as a POST handler does (request.get_json() / a body model parameter).

websocket

websocket(path: Annotated[str, Doc('URL path template for the WebSocket route, including `{param}` placeholders.')]) -> Callable

Register a WebSocket route via decorator.

websocket_listener

websocket_listener(path: str, *, receive: str = 'json', send: str = 'json', on_connect: RouteHandler | Callable[..., Any] | None = None, on_disconnect: RouteHandler | Callable[..., Any] | None = None) -> Callable

Declarative WebSocket route - wrap a per-message callback.

The decorated callback handles one message at a time; the framework owns the accept handshake, the receive loop, and the clean close on disconnect. The callback is called as cb(data), or cb(ws, data) when its first parameter is named ws/socket (or it takes two positional parameters). Returning a non-None value sends it back in send mode; returning None sends nothing.

receive/send select the codec ("json" default, or "text" / "bytes"). on_connect(ws) runs after accept; on_disconnect(ws) always runs when the loop ends, including on peer disconnect. Sync callbacks and hooks are offloaded to the executor.

Usage::

@app.websocket_listener("/echo")
async def echo(data):
    return data

For full control over the handshake and loop use @app.websocket.

add_websocket_route

add_websocket_route(path: Annotated[str, Doc('URL path template for the WebSocket route, including `{param}` placeholders.')], handler: Annotated[RouteHandler, Doc('Callable invoked with the accepted WebSocket connection when the route matches.')]) -> None

Register a WebSocket route imperatively (ASGI shape).

The non-decorator form of @app.websocket(path).

add_api_websocket_route

add_api_websocket_route(path: str, endpoint: RouteHandler, name: str | None = None) -> None

Register an imperative WebSocket route, mirroring add_api_route.

The non-decorator form of @app.websocket(path). name, when given, registers the route for reverse lookup so app.url_for(name) resolves to its path.

add_api_route

add_api_route(path: str, endpoint: RouteHandler, *, methods: list[str] | None = None, **kwargs: Any) -> None

Register a route imperatively.

The non-decorator form: the handler argument is named endpoint here and forwarded to add_route (where it is handler). All route kwargs - response_model, tags, dependencies, status_code, openapi_extra, ... - pass straight through. Defaults to ["GET"] when methods is omitted.

url_for

url_for(name: str, **path_params: Any) -> str

Reverse URL lookup by route name (url_for).

Substitutes each {name} placeholder in the registered template with the matching path_params kwarg. Underscore-prefixed kwargs are control parameters (convention):

  • _external=True - return an absolute URL. Uses app.config["SERVER_NAME"] when set, otherwise falls back to localhost. Caller should override _scheme/_host for anything more specific.
  • _scheme="https" - override scheme on the absolute URL.
  • _host="example.com" - override host on the absolute URL.
  • _anchor="section" - append #section.
  • Any other unmatched kwarg becomes a query-string parameter.

include_router

include_router(router: Router, prefix: str = '') -> None

Include another router (a sub-router with its own prefix, tags, and hooks).

before_request

before_request(func: Callable) -> Callable

Register a function to run before each blueprint request.

Fires only for requests that match a route declared on this blueprint. Use app.before_request for app-wide hooks.

after_request

after_request(func: Callable) -> Callable

Register a function to run after each blueprint request.

teardown_request

teardown_request(func: Callable) -> Callable

Run after blueprint-routed request teardown, with optional exc.

errorhandler

errorhandler(exc_class_or_status: type | int) -> Callable

Blueprint-scoped error handler.

Matches app.errorhandler semantics: integer keys go to the status-code table, classes go to the MRO-matched exception table. The handler runs for exceptions raised by blueprint handlers; app-level handlers act as fallback (registration order: blueprint wins on direct match).

url_value_preprocessor

url_value_preprocessor(func: Callable) -> Callable

Register a fn(endpoint, values) URL preprocessor on this blueprint.

Mirrors @app.url_value_preprocessor (R20) - runs after route match for blueprint-routed requests, mutating values in place. Use to pop a path-param into g (e.g. a lang segment) before the handler sees it.

url_defaults

url_defaults(func: Callable) -> Callable

Register a fn(endpoint, values) URL-defaults injector for url_for.

Mirrors @app.url_defaults (R21) - runs inside url_for / url_path_for for endpoints belonging to this blueprint. Use values.setdefault(...) for caller-wins semantics.

register_blueprint

register_blueprint(child: Blueprint, url_prefix: str | None = None) -> None

Mount another blueprint as a sub-blueprint of this one.

Routes from child register under self.url_prefix + (url_prefix or child.url_prefix) + path; endpoint names stored on this blueprint become <child.name>.<handler> and pick up the <self.name>. prefix once this blueprint is itself registered with an app, yielding a final <self.name>.<child.name>.<handler> lookup name so the dispatcher's prefix-gate finds them under either name.

Hooks and error handlers from child are merged into this blueprint's lists (not the app's - the app gets them when this blueprint is registered).

Cache

Result-cache backend interface.

A backend stores opaque bytes under a string key with a per-entry TTL in seconds. The methods are async so a network-backed store does not block the event loop. cached serialises and deserialises the values, so a backend only moves bytes.

get async

get(key: str) -> bytes | None

Return the stored bytes for key, or None if absent or expired.

set async

set(key: str, value: bytes, ttl: int) -> None

Store value under key, to expire after ttl seconds.

delete async

delete(key: str) -> None

Remove key if present.

InMemoryCache

Bases: Cache

A process-local, size-bounded result cache with per-entry TTL.

Fine for a single process and tests. It does not share state across workers, so a multi-worker deployment needs a shared backend such as veloce.contrib.redis.RedisCache. TTLs use a monotonic clock, so a wall-clock change cannot prematurely expire or extend an entry.

Usage::

from veloce import InMemoryCache

cache = InMemoryCache(max_entries=2048)

get async

get(key: str) -> bytes | None

Return the stored bytes for key, or None if absent or expired.

set async

set(key: str, value: bytes, ttl: int) -> None

Store value under key for ttl seconds, evicting when at capacity.

delete async

delete(key: str) -> None

Remove key if present.

Config

Bases: dict[str, Any]

A dict that knows how to load itself from common config sources.

Only keys made of ASCII uppercase letters, digits, or underscores (and not starting with a digit) are stored - see _is_uppercase_key.

default_config staticmethod

default_config() -> dict[str, Any]

The documented default config keys with their values.

Seeded into app.config at construction so reads never raise KeyError. Values are the documented defaults; veloce-specific behaviour reads several of these (MAX_CONTENT_LENGTH, JSON_SORT_KEYS, PROPAGATE_EXCEPTIONS).

from_mapping

from_mapping(mapping: Mapping[str, Any] | None = None, **kwargs: Any) -> bool

Bulk-update from mapping and/or kwargs.

Only UPPERCASE keys are stored; lowercase keys are silently skipped. Always returns True so the call can be used as a chaining sentinel.

from_object

from_object(obj: object | str) -> bool

Import UPPERCASE attributes from a module, class, instance, or dotted-path string.

from_object("myapp.settings.Prod") resolves the dotted path, then walks attributes whose names pass _is_uppercase_key.

from_pyfile

from_pyfile(filename: str, silent: bool = False) -> bool

Execute a Python file and pull UPPERCASE module-level names.

Returns True on success. If silent=True and the file is missing, returns False instead of raising.

from_env_file

from_env_file(filename: str = '.env', silent: bool = False) -> bool

Load KEY=VALUE pairs from a dotenv-style .env file.

Full-line # comments and blank lines are skipped, an optional export prefix is accepted, and a value wrapped in matching single or double quotes is unquoted. An unquoted value may carry a trailing # inline comment, which is stripped; a # inside quotes is kept literal. Values are stored as plain strings - a .env file carries no types. Only UPPERCASE keys are kept (see from_mapping). With silent=True a missing file returns False rather than raising.

from_envvar

from_envvar(varname: str, silent: bool = False) -> bool

Read a filename from os.environ[varname] and from_pyfile it.

from_prefixed_env

from_prefixed_env(prefix: str = 'VELOCE', loads: Callable[[str], Any] = loads) -> bool

Pull env vars starting with <prefix>_, strip the prefix, store with JSON-decoded values (falling back to the raw string when JSON parsing fails). Nested config via __ separator: VELOCE_MAIL__SERVER sets config["MAIL"]["SERVER"].

from_file

from_file(filename: str, load: Callable[[Any], Mapping[str, Any]] = _orjson_load, silent: bool = False, text: bool = False) -> bool

Load any structured file (JSON, TOML via tomllib.load, YAML ...).

Opens the file in text or binary mode (per text=), hands the file object to load, expects a mapping back, then applies it through from_mapping.

get_namespace

get_namespace(namespace: str, *, lowercase: bool = True, trim_namespace: bool = True) -> dict[str, Any]

Return all config keys starting with namespace, trimmed.

A helper for extracting one subsystem's settings. With lowercase=True (default), trimmed keys are lower-cased - extension code conventionally uses lowercase attribute names.

MCPContext

Per-invocation context for an MCP tool call.

Usage::

@app.mcp_tool(description="Look up a user by id")
async def get_user(user_id: int, ctx: MCPContext) -> dict:
    await ctx.log("info", f"looking up {user_id}")
    await ctx.report_progress(1, 2)
    return {"id": user_id}

cancelled property

cancelled: bool

Whether the client has sent notifications/cancelled for this call.

log async

log(level: str, message: Any, logger: str | None = None) -> None

Send a log message to the MCP client (notifications/message).

Dropped when no notification channel is wired, or when level is below the client's logging/setLevel minimum.

report_progress async

report_progress(progress: float, total: float | None = None, message: str | None = None) -> None

Report progress to the MCP client (notifications/progress).

Dropped when no notification channel is wired, or when the client did not send a progressToken with the call (progress is only reported on request).

sample async

sample(messages: list[dict[str, Any]], *, max_tokens: int, model_preferences: dict[str, Any] | None = None, system_prompt: str | None = None, temperature: float | None = None, stop_sequences: list[str] | None = None, tools: list[dict[str, Any]] | None = None, tool_choice: dict[str, Any] | None = None, metadata: dict[str, Any] | None = None) -> dict[str, Any]

Ask the client's LLM to sample a completion (sampling/createMessage).

Returns the client's result (its chosen model, role, and content). Requires a bidirectional transport and a client that advertised the sampling capability; tools / tool_choice additionally require sampling.tools.

elicit async

elicit(message: str, *, requested_schema: dict[str, Any] | None = None, url: str | None = None) -> dict[str, Any]

Ask the client to gather input from its user (elicitation/create).

Form mode passes a requested_schema (the JSON Schema of the fields to collect); URL mode passes a url the client opens instead. Returns the client's response (its action and any collected content). Requires a bidirectional transport and a client that advertised elicitation.

roots async

roots() -> list[dict[str, Any]]

List the client's exposed filesystem roots (roots/list).

Returns the client's roots array. Requires a bidirectional transport and a client that advertised the roots capability.

StaticFiles

Serve static files from a directory — all file I/O runs in executor.

Usage::

from veloce import Veloce
from veloce.contrib.staticfiles import StaticFiles

app = Veloce()
app.mount("/static", StaticFiles(directory="static"))

handle async

handle(request: Request) -> Response | None

Handle a static file request - file I/O offloaded to thread pool.

Jinja2Templates

Jinja2 template engine integration.

Usage::

templates = Jinja2Templates(directory="templates")

@app.get("/page")
async def page(request: Request):
    return templates.TemplateResponse("page.html", {"request": request, "name": "World"})

Any callables registered via @app.context_processor run before each render; their returned dicts are merged into the template context (caller's explicit context wins on collisions).

TemplateResponse

TemplateResponse(name: str | Sequence[str], context: dict[str, Any], status_code: int = HTTP_200_OK, headers: dict[str, str] | None = None, *, media_type: str | None = None, background: Any = None) -> Response

Render a template and return a response, optionally overriding the content type and attaching a background task.

render

render(name: str | Sequence[str], context: dict[str, Any] | None = None) -> str

Render a named template to a string (no Response wrapping).

Mirrors TemplateResponse but stops at the string stage so the render_template(name, **ctx) helper can plug in without building an HTMLResponse around the result.

stream

stream(name: str | Sequence[str], context: dict[str, Any] | None = None) -> Any

Render a named template incrementally, yielding str chunks.

Mirrors render but returns a synchronous iterator of str chunks instead of a fully-rendered string, so large templates can be streamed to the client without buffering the whole body. Wrap it in a StreamingResponse to return it from a handler.

Jinja's generator is lazy - chunks render as the response body is consumed, which on the built-in server happens on a separate task after the handler returns. Each chunk is therefore produced inside a snapshot of the current context (current_app, g, request), so a template that reads them or calls url_for resolves correctly during emission instead of raising "working outside of application context". The returned iterator is still synchronous, preserving the contract.

render_string

render_string(source: str, context: dict[str, Any]) -> str

Render a template from string.

render_async async

render_async(name: str | Sequence[str], context: dict[str, Any] | None = None) -> str

Asynchronously render a named template - Jinja enable_async.

Uses a separate async-enabled Environment (built lazily) so {% include %}d templates with async I/O resolve without blocking the loop. Filters/globals registered on app are synced onto the async env too.

get_template

get_template(name: str | Sequence[str]) -> Any

Get a raw Jinja2 template object, resolving a fallback list to the first existing template.

Depends

Dependency marker — use in function signature defaults.

dependency may be omitted (Depends()); the resolver then infers it from the parameter's type annotation — the shorthand for x: SomeClass = Depends().

Usage::

def get_db() -> Database:
    return Database()

@app.get("/users")
async def list_users(db: Database = Depends(get_db)) -> list[str]:
    return db.all_usernames()

Security

Bases: Depends

Dependency marker with OAuth2 scopes for OpenAPI emission.

SecurityScopes

Aggregated OAuth 2.0 scopes for the current Security() chain.

A handler / sub-dependency that declares a parameter of this type receives the union of all Security(..., scopes=[...]) calls between the route entry and this point in the dependency graph. Typical use: an authorising dependency checks security_scopes.scopes against the scopes the bearer token actually carries and builds a WWW-Authenticate: Bearer scope="<...>" header when denying.

Per RFC 6749 Sec. 3.3 the scope-string serialisation is space-separated.

BuildError

Bases: LookupError

url_for could not build a URL for the given endpoint.

Carries the endpoint name and the values that were being substituted so registered app.url_build_error_handlers callbacks can recover (e.g. fall back to a different endpoint, or fetch from an external routing table) by inspecting the failure and returning a URL string.

ConfigurationError

Bases: RuntimeError

A handler or route was declared in a way that cannot be resolved.

Raised at registration time, never per request, so a genuinely ambiguous parameter declaration becomes a startup error instead of a silent mis-binding discovered only at runtime. Carries the offending parameter name so the message points straight at the conflict.

DuplicateRouteError

Bases: ValueError

Two handlers were registered for the same path and HTTP method.

Raised at registration time when a route would silently overwrite an existing handler. Carries the conflicting path, method, and both handler qualified names so the message points at the exact collision. Configure the policy per router with on_duplicate="error"|"warn"|"override".

FilesKeyError

Bases: KeyError

Descriptive miss on request.files raised in debug mode.

Subclasses KeyError so handlers that already catch the bare lookup miss keep working, while the message explains the most common cause: the field was submitted as a plain form value (missing enctype="multipart/form-data") or the body was JSON rather than a multipart upload. Only raised when app.debug is set; production keeps the plain KeyError semantics.

HTTPException

Bases: Exception

HTTP error with status code and detail.

Either subclass with a fixed code (and optional description), or instantiate HTTPException(status_code, detail, headers) directly.

RequestValidationError

Bases: ValidationError

Framework-level request validation failure (422).

Raised by the dependency resolver when path / query / header / cookie / body / form / file parameters fail validation. Distinct from a user-level ValidationError so handlers can pick one or the other:

@app.exception_handler(RequestValidationError)
async def on_req_invalid(request, exc):
    return JSONResponse(
        {"errors": exc.errors},
        status_code=HTTP_422_UNPROCESSABLE_ENTITY,
    )

Subclasses ValidationError so existing except ValidationError handlers continue to catch it via the MRO walk.

SetupError

Bases: RuntimeError

A registration ran after the application started serving.

Routes, hooks, blueprints, middleware, and similar setup must be wired before the first request is dispatched. Once serving begins the route table and hook lists are frozen, so a late mutation - which would race in-flight requests under concurrent ASGI dispatch - raises this instead of silently corrupting the live application. The lock is relaxed in DEBUG/TESTING so hot-reload and test monkeypatching stay ergonomic.

ValidationError

Bases: UnprocessableEntity

Request validation error (422).

Subclasses UnprocessableEntity so handlers registered against either UnprocessableEntity or HTTPException catch it via the MRO walk Veloce performs in error dispatch.

WebSocketDisconnect

Bases: Exception

WebSocket connection closed.

WebSocketException

Bases: Exception

Raised inside a WebSocket handler to close the connection cleanly.

ASGI shape. The dispatch layer catches it and sends a close frame carrying code (RFC 6455 Sec. 7.4.1) and the optional reason - no traceback is propagated, since this is an application-driven close rather than an internal error.

WebSocketRequestValidationError

Bases: RequestValidationError

A WebSocket dependency failed parameter validation.

Raised when a Depends() resolved during a WebSocket handshake reports a RequestValidationError. The dispatch layer closes the connection with code 1008 (policy violation) rather than 1011 (internal error), since the failure is a client-side contract violation, not a server fault.

Aborter

A callable that turns a status code into an HTTPException.

Used as app.aborter(404) or app.aborter(403, "Forbidden"). Subclasses can override mapping to register custom exception classes for specific status codes; the base class leaves it empty so the default exception_for_status lookup applies.

URL

Parsed URL with component access - lazily constructed.

netloc property

netloc: str

Return the network location (host:port).

from_request classmethod

from_request(headers: Mapping[str, str], path: str, query_string: str, scope_scheme: str | None = None, forwarded_port: int | None = None) -> URL

Construct a URL from request headers and path components.

forwarded_port is the public port a trusted reverse proxy supplied (via ProxyFix reading X-Forwarded-Port / Forwarded host=...:port). A port embedded in the Host header always wins; forwarded_port only fills in the port when the Host header carries none, so a proxy on a non-default port (e.g. 8443) survives into netloc / absolute URLs.

replace

replace(**kwargs: Any) -> URL

Return a new URL with the specified components replaced.

AcceptHeader

Parsed Accept-* header with RFC 9110 Sec. 12.5 q-value semantics.

Construction is via AcceptHeader.parse(raw, mime=False). mime=True enables MIME-style wildcard matching (text/*, */*) used by Accept; defaults to plain string equality used by Accept-Language, Accept-Encoding, Accept-Charset.

values property

values: list[str]

All accepted values in the order the client sent them.

parse classmethod

parse(raw: str, mime: bool = False) -> AcceptHeader

Parse a comma-separated header into (value, q) tuples.

Q-values missing or unparseable default to 1.0 (RFC 9110 Sec. 12.4.2). Entries with q=0 are kept - best_match treats them as explicit rejections of that option. For MIME headers, media-type parameters (e.g. application/json;profile="x") are retained and participate in matching (RFC 9110 Sec. 12.5.1); the q parameter separates the q-value from the media-type parameters.

quality

quality(value: str) -> float

Return the q-value the client assigned to value.

For MIME headers, matches */* and type/* wildcards as well as parameterized media ranges (e.g. application/json;profile=x); the MOST SPECIFIC matching client range wins (RFC 9110 Sec. 12.5.1), with ties broken by the higher q-value. Returns 0 when the value is rejected or not mentioned (callers usually special-case this).

quality_explicit

quality_explicit(value: str) -> float

Return the q-value for value, with explicit tokens overriding *.

RFC 9110 Sec. 12.5.3: an explicit q=0 means "not acceptable" and must override a more permissive wildcard. quality() returns the MAX across an exact match and a * match, so for br;q=0, *;q=1 it reports 1.0 for br - serving a rejected coding. This variant prefers an EXACT token match (so an explicit q=0 excludes the coding) and only falls back to the * wildcard q when value is not explicitly listed. Used by precompressed static selection where honoring an explicit rejection matters; non-MIME (Accept-Encoding) semantics.

accepts_identity

accepts_identity() -> bool

Whether the identity (no-encoding) coding is acceptable per RFC 9110.

RFC 9110 Sec. 12.5.3: identity is acceptable by default unless it is explicitly excluded. It is UNacceptable only when an explicit identity entry carries q=0, OR when identity is not explicitly listed and a * wildcard entry carries q=0 (the wildcard rejects every coding not named, including identity). A missing header, or any header that does not exclude identity, leaves identity acceptable. Token comparison is case-insensitive (Sec. 8.4.1). Used by precompressed static selection to decide between serving the uncompressed asset and returning 406.

best_match

best_match(options: list[str], default: str | None = None) -> str | None

Return the option the client accepts with the highest q-value.

Among candidates the client accepts (q>0), the one whose best matching client range has the highest (q, specificity) wins, so a parameterized exact match beats a bare wildcard (RFC 9110 Sec. 12.5.1). Ties on both go to the order in options (caller's preference). Returns default when no option has q>0. When the header is empty (no preference expressed), returns options[0] - a missing Accept means "accept anything".

Authorization

Parsed Authorization header.

Two common shapes are first-class: - Basic (RFC 7617): .type == "basic", .username + .password set. - Bearer (RFC 6750): .type == "bearer", .token set.

Other schemes (Digest per RFC 7616, Negotiate, custom) populate .params with the comma-separated key="value" parameters parsed from the credentials portion; .type is the scheme name lower-cased.

Construction is via Authorization.from_header(value) which returns None for empty / malformed inputs rather than raising.

from_header classmethod

from_header(header_value: str) -> Authorization | None

Parse an Authorization: header value. Returns None on miss.

FormData

Bases: _GetListMixin, MultiDict

Multi-value form-field collection (text fields + file uploads).

Backed by multidict.MultiDict. Repeated form fields (<input name="a"> submitted twice, or repeated multipart parts with the same name) preserve every value; single-value access form["a"] returns the first. getlist("a") returns the full list.

getlist

getlist(key: str) -> list[str]

Return all values for the given key as a list. Empty list if absent.

get_upload

get_upload(key: str) -> UploadFile | None

Return the first value if it is an UploadFile, else None.

Headers

Bases: _GetListMixin, CIMultiDict

Case-insensitive, multi-value header collection.

Backed by multidict.CIMultiDict. Existing single-value access via headers["X"] returns the first value (multidict semantics); use headers.getlist("X") to get all values. Construction from a plain dict, a list of tuples, or another multidict all work - the underlying constructor handles each shape.

getlist

getlist(key: str) -> list[str]

Return all values for the given key as a list. Empty list if absent.

to_wsgi_list

to_wsgi_list() -> list[tuple[str, str]]

Return headers as a list of (name, value) tuples.

Preserves insertion order and every duplicate. Useful for emitting to a WSGI/ASGI layer or for round-tripping.

copy

copy() -> Headers

Return a shallow copy - a fresh Headers with the same entries.

add

add(key: str, value: str, **params: str) -> None

Append a header, with optional key=value parameters.

headers.add("Content-Disposition", "attachment", filename="x.txt") emits attachment; filename="x.txt". Parameter values containing whitespace or punctuation are double-quoted. Underscores in parameter names map to hyphens.

RangeSpec

Parsed Range: header (RFC 9110 Sec. 14.2).

  • unit is the range unit, e.g. "bytes" (the only commonly-used one).
  • ranges is a list of (start, end) tuples, with None standing in for an open endpoint:
    • 0-499 -> (0, 499)
    • 1000- -> (1000, None) (open at the right)
    • -500 -> (None, 500) (suffix-range - last 500 bytes)

parse classmethod

parse(header_value: str) -> RangeSpec | None

Parse a Range: header, returning None for a missing or unparseable value.

UploadFile

Uploaded file with an async read/write interface.

content property

content: bytes

Return the full file content as bytes.

Warning: this is a synchronous property. For large uploads that have been spooled to disk (i.e. _file_is_in_memory() returns False), the underlying read() call performs blocking I/O. Prefer await read() in async contexts for spooled files.

save

save(destination: str | BinaryIO, buffer_size: int = 16384) -> None

Stream this upload into destination.

  • destination is either a filesystem path (str) or an already-open binary file object. With a path, the file is opened in "wb" mode and closed afterwards; with a file object, the caller stays responsible for closing it.
  • buffer_size controls the chunk size used while streaming - keeps memory bounded for large uploads without loading them fully into RAM.

The upload's read cursor is reset to 0 before reading and restored to its prior position afterwards so the upload remains available for re-inspection.

read async

read(size: int = -1) -> bytes

Read up to size bytes from the upload.

write async

write(data: bytes) -> int

Write data to the upload's spool file.

seek async

seek(offset: int) -> None

Seek to a position in the upload's spool file.

close async

close() -> None

Close the upload's underlying spool file.

Request

Incoming HTTP request with lazy attribute parsing.

All expensive operations (JSON parsing, cookie parsing, URL construction, form/multipart parsing) are deferred until accessed — zero overhead for properties you don't use.

Usage::

@app.get("/users/{user_id}")
async def show(request: Request, user_id: str):
    data = await request.json()
    agent = request.headers.get("user-agent", "")
    return {"id": user_id, "agent": agent, "body": data}

query_params property

query_params: QueryParams

Parse query string lazily - only when accessed.

Repeated keys are preserved: params.getlist("tag") returns every value; params["tag"] returns the first.

args property

Alias for query_params — the parsed URL query string.

view_args property

view_args: dict[str, Any]

Alias for path_params — the matched route's path params.

request.view_args and request.path_params are two names for the dict of URL-captured values; both point at the same dict.

headers property writable

headers: Headers

Return the parsed request headers, materializing from raw ASGI tuples on first access.

user_agent property

user_agent: str

Return the User-Agent header value.

referrer property

referrer: str

Value of the Referer request header.

Spelling preserved from the original RFC misprint (RFC 7231 Sec. 5.5.2 documents Referer, not Referrer). The accessor uses the corrected spelling so callers don't have to remember.

origin property

origin: str | None

The Origin header - RFC 6454. None when absent.

Set by browsers on cross-origin requests (and all CORS preflights). CORS middleware matches the allow-list against it.

date property

date: datetime | None

The request Date header as a tz-aware UTC datetime.

RFC 9110 Sec. 6.6.1 - the originator's timestamp for the message. Returns None when the header is missing or unparseable.

pragma property

pragma: str

Value of the legacy Pragma header - RFC 9111 Sec. 5.4.

Almost always no-cache from HTTP/1.0 clients. Returns the empty string when absent. Prefer cache_control for HTTP/1.1.

max_forwards property

max_forwards: int | None

The Max-Forwards header as an int - RFC 9110 Sec. 7.6.2.

Bounds how many proxies a TRACE/OPTIONS request may traverse. None when absent or non-numeric.

is_xhr property

is_xhr: bool

Detect XMLHttpRequest-style AJAX calls.

The convention is X-Requested-With: XMLHttpRequest, set by jQuery, fetch wrappers, and similar libraries. It's a hint, not a guarantee (the client controls the header), but it's the traditional signal application code uses to switch between full HTML responses and partial / JSON ones.

content_type property

content_type: str

Return the Content-Type header value, or an empty string.

mimetype property

mimetype: str

Content-Type without parameters.

application/json; charset=utf-8 -> application/json. Lower-cased and stripped - per RFC 9110 Sec. 8.3 the media type is case-insensitive.

mimetype_params property

mimetype_params: dict[str, str]

Parameters from Content-Type (e.g. {"charset": "utf-8"}).

Each parameter is key=value; quoted values have their surrounding double-quotes stripped. Keys are lower-cased; values preserve case.

content_length property

content_length: int | None

Return the Content-Length as an integer, or None.

content_encoding property

content_encoding: str

Value of the Content-Encoding header.

Returns the lowercased encoding name ("gzip", "br", etc.) or the empty string when the header is missing.

content_language property

content_language: str

Value of the Content-Language header - RFC 9110 Sec. 8.5.

Returns the raw header value (a comma-separated list of language tags) or the empty string when the header is absent.

charset property

charset: str

Request body charset, decoded from Content-Type.

Defaults to utf-8 when no charset is declared (the modern default; the also moved off ISO-8859-1).

is_json property

is_json: bool

True for application/json or any application/*+json subtype.

Per RFC 6839 Sec. 3.1 the structured-suffix +json (e.g. application/vnd.api+json, application/problem+json) marks the body as JSON-encoded.

is_multipart property

is_multipart: bool

True when the request body is multipart/*.

is_form property

is_form: bool

True when the body is application/x-www-form-urlencoded or multipart/form-data.

accept property

accept: str

Return the raw Accept header value.

accept_mimetypes property

accept_mimetypes: AcceptHeader

Parsed Accept header with MIME wildcard matching.

accept_languages property

accept_languages: AcceptHeader

Parsed Accept-Language header. q-value ordered.

accept_encodings property

accept_encodings: AcceptHeader

Parsed Accept-Encoding header (e.g. gzip, br).

accept_charsets property

accept_charsets: AcceptHeader

Parsed Accept-Charset header.

authorization property

authorization: str | None

Return the parsed Authorization header.

auth property

auth: Authorization | None

Lazy-parse the Authorization: header into a typed object.

Returns None when the header is missing. Basic and Bearer schemes populate .username/.password and .token respectively; other schemes carry their key=value parameters in .params.

access_control_request_method property

access_control_request_method: str | None

CORS preflight Access-Control-Request-Method - RFC 6454.

On an OPTIONS preflight, names the method the real request will use. None outside a preflight.

access_control_request_headers property

access_control_request_headers: list[str]

CORS preflight Access-Control-Request-Headers - header list.

The headers the real request intends to send, lower-cased and whitespace-trimmed. Empty list when the header is absent.

if_modified_since property

if_modified_since: float | None

Parse If-Modified-Since (RFC 9110 Sec. 13.1.3) to a Unix timestamp.

Accepts IMF-fixdate, obsolete RFC 850, and ANSI C asctime() forms. Returns None when the header is missing or unparseable - never raises, so callers can use it in a single branch.

if_unmodified_since property

if_unmodified_since: float | None

Parse If-Unmodified-Since (RFC 9110 Sec. 13.1.4) to a Unix timestamp.

Returns None when the header is missing or unparseable. Write-side companion to If-Modified-Since: precondition that fails with 412 when the resource has been modified since the given date.

if_match property

if_match: tuple[str, ...]

Parse If-Match (RFC 9110 Sec. 13.1.1) into a tuple of ETags.

Returns ("*",) for the wildcard, an empty tuple when the header is absent, otherwise a tuple of quoted ETags (quotes and any W/ weak prefix preserved verbatim).

If-Match is the write-side companion to If-None-Match: precondition that fails the request with 412 Precondition Failed when none of the listed ETags matches the resource's current ETag. Standard guard against the lost-update problem.

if_none_match property

if_none_match: tuple[str, ...]

Parse If-None-Match (RFC 9110 Sec. 13.1.4) into a tuple of ETags.

Returns ("*",) when the header is the literal * (matches any existing representation), an empty tuple when the header is missing, or a tuple of one or more quoted ETags (the quotes are preserved so callers can compare them verbatim against an ETag header on the response).

if_range property

if_range: tuple[str, float | None]

Parse If-Range: (RFC 9110 Sec. 13.1.5).

The header carries either an ETag or an HTTP-date - never both. Returns (etag, None) when the value is an ETag (quoted, possibly weak-prefixed) and ("", timestamp) when it parses as a date. Returns ("", None) when the header is absent or unparseable. Caller picks the relevant slot.

Used by GET with Range: to convert a partial-content request into a full 200 when the cached resource is stale.

range property

range: RangeSpec | None

Parse Range: header per RFC 9110 Sec. 14.2. Returns None when absent or unparseable.

cache_control property

cache_control: Any

Parsed Cache-Control header.

Returns a CacheControl view: req.cache_control.no_cache (bool), req.cache_control.max_age (int or None), etc. Always returns a fresh parse to reflect any header mutation.

cookies property

cookies: Cookies

Parse cookies from the Cookie header - lazy, MultiDict-shaped.

Returns a Cookies (MultiDict). cookies["name"] gives the first value; cookies.getlist("name") gives every value when a name repeats (rare but valid per RFC 6265).

url property

url: Any

Full URL object - lazy construction.

base_url property

base_url: str

Base URL (scheme + host).

full_path property

full_path: str

Path + ? + query string. Always contains a ? even when the query string is empty.

url_root property

url_root: str

Root URL of the request: scheme://host/ (with trailing slash, no path or query string).

host_url property

host_url: str

Alias for url_root - Veloce exposes both names.

is_secure property

is_secure: bool

Return True if the request uses HTTPS.

scheme property

scheme: str

Request scheme - "http" or "https".

Sourced from the ASGI scope["scheme"] when present, then from the X-Forwarded-Proto header (only meaningful behind a trusted proxy), then default http.

host property

host: str

Value of the Host request header.

Mirrors Request.url.netloc for the common case but pulls directly from the header to remain cheap (no full URL parse). Returns the empty string when the header is absent.

root_path property

root_path: str

ASGI scope["root_path"] - the URL prefix the app is mounted under.

Comes from the ASGI server (e.g. uvicorn --root-path /api) or from app.mount("/sub", inner_app). Used so an app behind a prefix can generate correct external URLs without knowing the prefix at code-time.

Returns the empty string when the app is at root.

script_root property

script_root: str

Alias for root_path — also called script_root.

ProxyFix-style middleware may also set _state["proxy_fix_prefix"]; that wins over the ASGI scope because it represents the trusted outer-edge prefix when the ASGI server is behind a reverse proxy that strips the prefix.

subdomain property

subdomain: str

Leftmost host label minus app.config["SERVER_NAME"].

Returns the empty string when the request host equals SERVER_NAME exactly (apex), or when SERVER_NAME isn't configured and the host has no dots. With SERVER_NAME set, the returned value is the prefix that wouldn't match the configured apex; without it, the leftmost label.

environ property

environ: dict

Alias for the ASGI scope dict.

Third-party code paths reach for request.environ (WSGI); ASGI scope is the analogue. Returns the live dict so middleware can introspect (mutation goes through framework APIs, not this).

url_rule property

url_rule: str | None

Return the matched route's template (e.g. /users/{id}).

Returns the raw path template the radix tree used for the match — i.e. path_params placeholders are unsubstituted. None for synthetic requests that never went through dispatch.

is_mcp property

is_mcp: bool

Return whether this request is a replayed MCP tool / resource call.

True when the request was synthesised by the MCP integration to replay a route through Depends / middleware for an agent call, rather than a real HTTP request. Authentication middleware that checks a browser credential (a session cookie, an Authorization header) should return early on these - the MCP transport authenticates the agent separately. The transport request itself (POST /mcp) opts such middleware out via mount_mcp(..., exclude_middleware=[...]).

blueprint property

blueprint: str | None

Return the name of the blueprint that owns the matched route.

Veloce stores the endpoint as <bp>.<name> for blueprint routes. Returns the bit before the dot, or None if the endpoint is unset or is a top-level (no-dot) name.

blueprints property

blueprints: list[str]

Return every blueprint in the matched endpoint's parent chain.

For an endpoint a.b.c.view, returns ["a.b.c", "a.b", "a"] (innermost first). Empty list when the route is top-level or the endpoint is unset.

client_host property

client_host: str | None

Return the client's IP address from the ASGI scope.

client_port property

client_port: int | None

Return the client's port number from the ASGI scope.

client property

client: Address | None

The connecting peer as an Address(host, port).

request.client.host / request.client.port work, and tuple unpacking (host, port = request.client) works too. Returns None when the peer is unknown (e.g. synthetic requests). Honours ProxyFix - client.host reflects the trusted client IP.

remote_addr property

remote_addr: str | None

Alias for client_host — the connecting client's IP.

Honours ProxyFix-style middleware: when the trusted hop has set _state["proxy_fix_client"], that value wins over the raw TCP peer (the ASGI/uvicorn client[0] may be the load balancer).

access_route property

access_route: list[str]

Forwarded-for chain.

Returns the comma-separated X-Forwarded-For values (client -> proxy chain order), with the connecting peer (remote_addr) appended at the end. With no X-Forwarded-For header, returns [remote_addr] when the peer is known, else [].

RFC 7239 Sec. 5.2 defines the IP-order convention: leftmost is the originating client, rightmost is the closest proxy. Production code should consume the leftmost trusted entry, not blindly the leftmost value.

state property

state: State

Per-request scratch namespace - ASGI shape.

Supports attribute access (request.state.user = ...) and dict access (request.state["user"], request.state.get(...)).

session property

session: dict[str, Any]

Access to the session dict.

SessionMiddleware writes the parsed session into _state["session"] during process_request. This property surfaces it under the a convenience accessor. Raises RuntimeError when the middleware hasn't run - keeps "I forgot to add SessionMiddleware" from showing up as a confusing silent empty-dict.

data property

data: bytes

Raw request body bytes - request.data shape.

The sync-property form of body(). Returns the body exactly as received, with no decoding or form parsing. Requires the body to already be buffered; raises RuntimeError otherwise (use await request.body() for the async path).

max_content_length property

max_content_length: int | None

The body-size cap for this request.

Reads app.config["MAX_CONTENT_LENGTH"] from the bound app (the dispatcher enforces it, returning 413 on overflow). None - no limit - when unset or no app is bound.

url_for

url_for(name: str, **path_params: Any) -> str

Reverse-resolve a route URL - ASGI shape.

request.url_for("route_name", id=7) delegates to the bound app's url_for. Raises RuntimeError when the request has no app bound (synthetic requests built outside dispatch).

get_json

get_json(force: bool = False, silent: bool = False, cache: bool = True) -> Any

Parse the request body as JSON.

  • force=True skips the is_json content-type check; useful when the client sends JSON without setting Content-Type (e.g. some XHR libraries). Default is to honour the content type and return None for non-JSON requests.
  • silent=True swallows orjson.JSONDecodeError and returns None. Default raises so caller code can distinguish malformed JSON from missing JSON.
  • cache=False forces a re-parse on every call. Default caches the parsed value (one parse per request); cache invalidation is the caller's job when cache=False.

Returns None for empty bodies regardless of force / silent.

This is the synchronous accessor: it requires the body to already be buffered (the in-memory path), and raises RuntimeError otherwise - reach for await request.json() when the body has not yet been drained.

on_json_loading_failed

on_json_loading_failed(error: Exception) -> Any

Hook invoked when JSON parsing fails on a non-silent body.

Raises BadRequest (400) with a stable, body-independent message so a malformed body cannot leak decoder internals (byte offsets derived from attacker-controlled input) into the production response. The verbose decoder reason is always logged and attached as BadRequest.debug_detail for operators, and is surfaced in the response only when debug mode or the JSON_ERRORS_VERBOSE config flag is set. Override on a Request subclass to customise.

body async

body() -> bytes

Return the full request body as bytes, draining the source once.

Async to match the ASGI convention. Veloce buffers the body before dispatch, so no I/O happens inside the await - the coroutine resolves immediately with the cached bytes.

json async

json() -> Any

Parse the request body as JSON, async to match the ASGI convention.

Veloce buffers the body at construction time, so no I/O actually happens inside the await - the coroutine resolves immediately with the cached parse. The async signature exists so the await request.json() idiom does not blow up at runtime.

The synchronous request.get_json() accessor is available for callers that prefer a sync API.

get_data async

get_data(as_text: bool = False, cache: bool = True) -> bytes | str

Return the raw request body, draining the source once.

Async to match body() / json() / form().

  • as_text=True decodes via the Content-Type charset (default UTF-8). Falls back to latin-1 when the declared charset is unrecognised - a defensive fallback, since latin-1 round-trips arbitrary bytes without raising.
  • cache=True is a no-op today (veloce already buffers the whole body on construction) but keeps the parameter so callers that pass cache=False for streaming compatibility don't break. Streaming-body support arrives separately.

Returns bytes (default) or str (with as_text=True).

form async

form() -> Any

Parse form data including file uploads.

files async

files() -> Any

View of uploaded files only - a FormData subset.

Parses the form (via form()) and returns a FormData containing just the entries whose value is an UploadFile. Non-file form fields are excluded. Empty FormData for non-multipart requests. Result is cached after first parse.

values async

values() -> Any

Merged query string + form body - request.values shape.

Returns a fresh MultiDict with query-string entries first, then form-body entries appended. Both source MultiDicts preserve repeated keys; merging preserves the order across both sources. Form parsing is async (multipart may need executor reads), so this is an awaitable rather than a property.

text async

text() -> str

Return body as text, draining the source once.

is_disconnected async

is_disconnected() -> bool

Whether the client has disconnected.

Veloce fully buffers the request body before dispatch, so by the time a handler runs the body is already received and the connection cannot be "disconnected mid-handler" in the ASGI sense. Always returns False; the method exists so handlers that poll await request.is_disconnected() keep working unchanged.

stream async

stream() -> Any

Async-iterate the request body in chunks - ASGI shape.

Streamed requests (raw HTTP/1.1) yield each chunk as the socket delivers it, so async for chunk in request.stream(): ... processes a large body incrementally without ever buffering it whole. For in-memory requests (TestClient / ASGI), or once a streamed body has already been drained and cached, the buffered bytes are sliced into 64 KiB chunks instead.

FileResponse

Bases: Response

Serve a file from disk - small files inline, large files via executor.

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

from_path async classmethod

from_path(path: str, filename: str | None = None, content_type: str | None = None, headers: dict[str, str] | None = None, content_disposition_type: str = HEADER_VALUE_ATTACHMENT) -> FileResponse

Async factory - reads small files inline, large files in the executor.

Stats the path on the loop (one fast syscall) to size the file. A file at or below _INLINE_READ_MAX is read inline, skipping the thread-pool hop that otherwise dominates serving a small static asset; a larger file is read in the executor so a big read never stalls the loop.

HTMLResponse

Bases: _TextResponse

HTML response.

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

JSONResponse

Bases: Response

JSON response using orjson for speed.

Usage::

from veloce import JSONResponse

async def handler(request):
    return JSONResponse({"ok": True}, status_code=200)

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

from_bytes classmethod

from_bytes(body: bytes, *, status_code: int = HTTP_200_OK, headers: dict[str, str] | None = None) -> JSONResponse

Build a JSONResponse from already-encoded JSON bytes.

Skips __init__'s orjson re-encode - use this when the caller has produced the JSON body itself (e.g. with custom orjson options or via a JSONProvider.dumps). The body is sent verbatim with Content-Type taken from cls.default_media_type (so a subclass like class ProblemJSON(JSONResponse): default_media_type = "application/problem+json" gets its declared type without overriding this method).

The caller is responsible for ensuring body is valid UTF-8 JSON; no parsing or validation is performed. Passing non-JSON bytes will produce a response whose body does not match its declared content type.

body must be bytes or bytearray. A str raises TypeError rather than being silently encoded, so callers do not produce a response with a mismatched charset by accident.

Header precedence: when headers includes a Content-Type entry, the caller-supplied value wins and the class default is not emitted. This matches Response's general rule that user headers override framework defaults and lets callers send application/problem+json or another JSON suffix type without subclassing.

ORJSONResponse

Bases: JSONResponse

Explicit orjson-backed JSON response.

JSONResponse already uses orjson for encoding, so this class is a semantic alias - useful when route declarations want to communicate the encoder choice via response_class=ORJSONResponse.

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

from_bytes classmethod

from_bytes(body: bytes, *, status_code: int = HTTP_200_OK, headers: dict[str, str] | None = None) -> JSONResponse

Build a JSONResponse from already-encoded JSON bytes.

Skips __init__'s orjson re-encode - use this when the caller has produced the JSON body itself (e.g. with custom orjson options or via a JSONProvider.dumps). The body is sent verbatim with Content-Type taken from cls.default_media_type (so a subclass like class ProblemJSON(JSONResponse): default_media_type = "application/problem+json" gets its declared type without overriding this method).

The caller is responsible for ensuring body is valid UTF-8 JSON; no parsing or validation is performed. Passing non-JSON bytes will produce a response whose body does not match its declared content type.

body must be bytes or bytearray. A str raises TypeError rather than being silently encoded, so callers do not produce a response with a mismatched charset by accident.

Header precedence: when headers includes a Content-Type entry, the caller-supplied value wins and the class default is not emitted. This matches Response's general rule that user headers override framework defaults and lets callers send application/problem+json or another JSON suffix type without subclassing.

PlainTextResponse

Bases: _TextResponse

Plain text response.

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

RedirectResponse

Bases: Response

HTTP redirect.

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

Response

Base HTTP response.

Usage::

from veloce import Response

async def handler(request):
    return Response(body=b"hello", content_type="text/plain")

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

StreamingResponse

Bases: Response

Streaming response for large payloads.

content may be an async iterator/iterable or a plain sync iterable (e.g. a generator). A sync iterable is wrapped so the response always exposes an async stream; both forms are accepted.

Usage::

from veloce import StreamingResponse

async def handler(request):
    def chunks():
        yield b"part-1"
        yield b"part-2"

    return StreamingResponse(chunks(), content_type="text/plain")

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

encode

encode() -> bytes

For streaming, encode headers with chunked transfer.

stream_to async

stream_to(transport: Any, drain: Callable[[], Awaitable[None]] | None = None) -> None

Stream chunks to transport.

When drain is supplied (the raw serving protocol passes its write-side flow-control awaitable) it is awaited after each chunk, so a producer outrunning a slow client is throttled instead of growing the transport write buffer without bound. drain is a no-op until the buffer crosses the high-water mark, so the fast path pays one already-set check.

UJSONResponse

Bases: Response

JSON response encoded with ujson.

Lazily imports ujson at construction. Raises ImportError with a clear message when the package is missing rather than at module load, so apps that don't use this class don't need ujson installed.

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

RequestMetrics dataclass

A finished HTTP request, as seen by an instrumentation hook.

route is the matched route's path template (/items/{id}), which is safe to use as a metric label; it is None whenever no route+method pair matched - both a 404 (no such path) and a 405 (the path exists but the method is not allowed). Group by (route, status_code) to keep those apart. path is the concrete request path and is high-cardinality - prefer route for aggregation.

streamed is True when the response body is a streaming iterator (StreamingResponse, EventSourceResponse, a large FileResponse). For those, the hook fires before the body is emitted on the ASGI send path, so duration_ms and status_code reflect only the time to produce the response object - not the time to drain the stream, and not a failure that happens mid-stream. A tracing bridge that needs accurate end-of-request timing should skip records with streamed set.

end_time_ns is the wall-clock (time.time_ns()) instant the request finished, captured the moment dispatch returned - before any instrumentation hook or request_finished receiver runs. A tracing bridge should anchor its span window to this value (and duration_ms) rather than reading the clock when its own hook executes, so a slow earlier hook cannot shift the span past the real request boundary.

error_type is the low-cardinality class name (type(exc).__qualname__) of the exception that produced a 5xx, set only when an unhandled raised exception turned into a server error (the debug traceback page, the generic 500 response, or a propagated exception). It is None for every other outcome - a 2xx/3xx/4xx, or a 5xx deliberately returned by a handler/exception handler without a raised exception. A tracing bridge can record it as the OpenTelemetry error.type attribute without capturing the full traceback or exception instance, keeping the record allocation-light. The class name only is carried, never the message (which may hold attacker-controlled or sensitive text).

DefaultJSONProvider

Bases: JSONProvider

orjson-backed provider — Veloce's default.

Honours two app.config flags so the existing JSON_SORT_KEYS / JSONIFY_PRETTYPRINT_REGULAR toggles keep working without callers needing to subclass.

response

response(value: Any, **kwargs: Any) -> Any

Build a Response carrying value as JSON.

The default implementation delegates to dumps and wraps the result in a JSONResponse.

JSONProvider

Base class for JSON serialisation providers.

Subclass to plug in an alternative serialiser, then point the app at it via app.json (an instance) or app.json_provider_class (a class, instantiated lazily on first access).

Usage::

class MyJSONProvider(JSONProvider):
    def dumps(self, obj, **kwargs):
        return my_lib.dumps(obj).encode()

    def loads(self, data):
        return my_lib.loads(data)

app.json_provider_class = MyJSONProvider

dumps

dumps(obj: Any, **kwargs: Any) -> bytes

Serialise obj to JSON bytes. Subclasses override.

Returns bytes (not str) so callers can write directly to a response body without re-encoding. The kwargs catch-all is provider-specific (e.g. indent=2, sort_keys=True).

loads

loads(data: bytes | str) -> Any

Parse JSON data into Python objects.

response

response(value: Any, **kwargs: Any) -> Any

Build a Response carrying value as JSON.

The default implementation delegates to dumps and wraps the result in a JSONResponse.

Markup

Bases: str

A string flagged as already HTML-safe.

Equivalent to markupsafe.Markup for the subset Veloce's templating rely on. Concatenation with a non-Markup string escapes the other operand first so an injection cannot sneak in via +.

BaseHTTPMiddleware

Class-based dispatch-shape middleware.

Subclass and override dispatch, or construct with dispatch=fn for a one-off middleware. The instance is callable as (request, call_next) -> response, so it composes with the existing @app.middleware("http") chain.

Usage::

class TimingMW(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        start = time.perf_counter()
        response = await call_next(request)
        response.headers["X-Elapsed-ms"] = str(
            int((time.perf_counter() - start) * 1000)
        )
        return response

app.add_http_middleware(TimingMW())

# Or, without subclassing:
async def my_dispatch(request, call_next): ...
app.add_http_middleware(BaseHTTPMiddleware(dispatch=my_dispatch))

dispatch async

dispatch(request: Request, call_next: CallNext) -> Response

Override this in subclasses. Default just calls through.

Implementations must await call_next(request) exactly once to reach the wrapped handler.

ConditionalGetMiddleware

Bases: Middleware

Emit 304 responses for satisfied GET/HEAD preconditions.

With auto_etag (default), a weak ETag is synthesized for a buffered, non-empty, non-streaming 200 response that lacks one (unless Cache-Control: no-store is set). Register this AFTER GZipMiddleware so a synthesized/forwarded ETag reflects the post-compression bytes; StreamingResponse bodies are intentionally not buffered for synthesis.

Usage::

app.add_middleware(GZipMiddleware())
app.add_middleware(ConditionalGetMiddleware())

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Called before route handler. Return a Response to short-circuit.

CORSMiddleware

Bases: Middleware

Cross-Origin Resource Sharing middleware.

Usage::

app.add_middleware(
    CORSMiddleware(
        allow_origins=["https://example.com"],
        allow_methods=["GET", "POST"],
        allow_credentials=True,
    )
)

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Handle CORS preflight requests and validate origins.

process_response async

process_response(request: Request, response: Response) -> Response

Add CORS response headers.

CSPMiddleware

Bases: Middleware

Emit Content-Security-Policy with optional per-request nonce.

policy and report_only_policy each accept a str template containing the literal {nonce} placeholder, or a directive mapping where the 'nonce' source is substituted with a fresh per-request nonce.

Usage::

app.add_middleware(
    CSPMiddleware(
        policy={"default-src": "'self'", "script-src": ["'self'", "'nonce'"]},
        report_only_policy="default-src 'self'",
    )
)

Static (no-nonce) policies can stay on SecurityHeadersMiddleware; use this when a per-request nonce or a report-only policy is needed.

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

CSRFMiddleware

Bases: Middleware

Double-submit-cookie CSRF middleware.

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Validate the CSRF token on state-changing requests.

process_response async

process_response(request: Request, response: Response) -> Response

Set or rotate the CSRF cookie.

GZipMiddleware

Bases: Middleware

GZip compression for responses above a size threshold.

Compression runs in the thread pool executor to avoid blocking the event loop.

Usage::

app.add_middleware(GZipMiddleware(minimum_size=1024, compresslevel=6))

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Called before route handler. Return a Response to short-circuit.

process_response async

process_response(request: Request, response: Response) -> Response

Compress the response body with gzip if the client accepts it.

HTTPSRedirectMiddleware

Bases: Middleware

Redirect HTTP requests to HTTPS.

Resolves the request scheme in this order
  1. ASGI scope "scheme" if set to "https"/"wss" (the server already terminated TLS).
  2. X-Forwarded-Proto header (when a ProxyFix-style middleware ran upstream this is already the trusted value).
  3. Default http.

Uses 308 Permanent Redirect (RFC 9110 Sec. 15.4.9) so non-GET methods preserve their method and body. The earlier 301 form was wrong for POST/PUT callers - those would silently become GET.

Pass exempt_paths=("/health/", ...) to serve some paths over plain HTTP (prefix match - use a trailing slash to scope to a segment). By default /.well-known/acme-challenge/ is exempt (RFC 8555 Sec. 8.3: the HTTP-01 challenge MUST be reachable over plain HTTP for certificate issuance and renewal); pass exempt_acme_challenge=False to drop that default.

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_response async

process_response(request: Request, response: Response) -> Response

Called after route handler. Can modify the response.

process_request async

process_request(request: Request) -> Response | None

Redirect HTTP requests to HTTPS with a 308 status.

LoggingMiddleware

Bases: Middleware

Structured request/response access logging.

Usage::

app.add_middleware(LoggingMiddleware())

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Record the request start time for duration logging.

process_response async

process_response(request: Request, response: Response) -> Response

Log the request method, path, status, and timing.

Middleware

Base middleware class. Subclass and override process_request/process_response.

Each middleware carries a name used by per-route exclusion (exclude_middleware=[...] on a route). The default name is the concrete class name; override the class attribute, or pass name= when two instances of the same class must be addressed independently.

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Called before route handler. Return a Response to short-circuit.

process_response async

process_response(request: Request, response: Response) -> Response

Called after route handler. Can modify the response.

ProxyFix

Bases: Middleware

Reverse-proxy header trust middleware.

Trusts N hops for each X-Forwarded-* header (right-to-left). Setting any field to 0 disables it. Negative values raise at construction.

x_port trusts X-Forwarded-Port: the resolved port fills in the public port for request.url / redirects when the forwarded Host carries none, so a proxy on a non-default port (e.g. 8443) is preserved. An explicit port in the Host / X-Forwarded-Host always wins.

Usage::

# Behind two trusted proxies forwarding client IP and scheme.
app.add_middleware(ProxyFix(x_for=2, x_proto=1, x_host=1))

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_response async

process_response(request: Request, response: Response) -> Response

Called after route handler. Can modify the response.

process_request async

process_request(request: Request) -> Response | None

Rewrite request attributes from trusted proxy headers.

RateLimitMiddleware

Bases: Middleware

Per-client rate limiter with a selectable algorithm and backend.

Two ways to configure it:

  • The default max_requests per window_seconds runs a process-local sliding-log limiter - simple, zero-dependency, intended for a single worker. Counters are NOT shared across workers, so uvicorn --workers N sees roughly N x max_requests per window.
  • Pass a strategy - FixedWindow, SlidingWindow, or TokenBucket - to choose the algorithm, and a backend to choose where state lives: InMemoryRateLimitBackend (default) or veloce.contrib.redis.RedisRateLimitBackend for one limit shared across every worker and host.

Give a route its own limit by decorating its handler with rate_limit - the limit lives on the handler, so there is no route string to mistype::

from veloce import rate_limit

@app.post("/login")
@rate_limit(TokenBucket(rate=5, per=60))
async def login(request): ...

The overrides map is the central alternative for handlers you cannot decorate: it maps a route's full path template to a strategy. The key is the template as matched at runtime - the value of request.url_rule - so a blueprint route includes its url_prefix (/api/login, not /login); an override key that matches no route raises on the first request. An explicit overrides entry wins over a rate_limit tag on the same route.

Either way, an overridden route gets its own per-client counter, independent of the default budget; routes without an override keep the shared default. Like exclude_middleware, the per-route strategy is resolved against the route matched at dispatch entry, so a before_request hook that rewrites the path does not change which limit applies.

Usage::

from veloce import RateLimitMiddleware, TokenBucket

app.add_middleware(
    RateLimitMiddleware(
        strategy=TokenBucket(rate=1000, per=60),
        overrides={"/login": TokenBucket(rate=5, per=60)},
    )
)

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Enforce per-client request rate limits.

process_response async

process_response(request: Request, response: Response) -> Response

Attach X-RateLimit-* headers to successful responses.

RequestIDMiddleware

Bases: Middleware

Assign a unique request ID to each request and echo it in the response.

Usage::

app.add_middleware(RequestIDMiddleware())

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Attach a unique request ID to each request.

process_response async

process_response(request: Request, response: Response) -> Response

Echo the request ID in the response headers.

SecurityHeadersMiddleware

Bases: Middleware

Attach common hardening response headers to every response.

Set by default:

  • X-Content-Type-Options: nosniff - stop MIME sniffing.
  • X-Frame-Options: DENY - block framing (clickjacking).
  • Referrer-Policy: strict-origin-when-cross-origin.

Off unless configured:

  • Strict-Transport-Security - pass hsts_max_age (seconds). Browsers honour HSTS only over HTTPS, so it is inert in plain-HTTP development, but it is still opt-in because it pins clients to HTTPS for the configured lifetime.
  • Content-Security-Policy - pass content_security_policy.
  • Permissions-Policy - pass permissions_policy.

A header a handler already set on the response is left untouched - these are defaults, not overrides.

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Called before route handler. Return a Response to short-circuit.

process_response async

process_response(request: Request, response: Response) -> Response

Attach security hardening headers to every response.

ServerSessionMiddleware

Bases: Middleware

Server-side session - the cookie carries only an opaque session id.

The session payload lives in a SessionStore, not in the cookie, so a session is revocable: empty it in a handler (session.clear()) or delete it straight from the store (await store.delete(session_id)) and it is gone server-side. A tampered or stale cookie simply fails to resolve to a stored payload and is treated as a fresh session.

The default store is a process-local InMemorySessionStore; pass a shared backend (e.g. a Redis-backed SessionStore) for a multi-worker deployment. The store is a plain object the caller owns - keep a reference to it to revoke sessions by id.

Set renew_on_access=True for sliding expiry: a session that was only read during a request has its store TTL refreshed (via SessionStore.touch) and its cookie re-stamped on the way out - an idle-timeout reset. Default off.

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Load the session from the server-side store by cookie id.

process_response async

process_response(request: Request, response: Response) -> Response

Save the modified session back to the server-side store.

SessionMiddleware

Bases: Middleware

Server-side session stored in a signed, timestamped cookie.

Constructor arguments left out fall back to the app's config on the first request: secret_key to SECRET_KEY (also settable as app.secret_key), cookie_name to SESSION_COOKIE_NAME, path to APPLICATION_ROOT, httponly/secure/samesite to the SESSION_COOKIE_* keys, permanent_lifetime to PERMANENT_SESSION_LIFETIME, and max_cookie_size to MAX_COOKIE_SIZE. An explicit argument always wins over config. Without either a secret_key= argument or a configured SECRET_KEY, the first request raises.

Set renew_on_access=True for sliding expiry: a session that was only read during a request has its cookie re-signed with a fresh Max-Age on the way out, so an active user is not logged out at the fixed max_age. Default is off - only a modifying write rewrites the cookie.

Set chunked=True to transparently split a signed value larger than max_cookie_size across numbered cookies (<cookie_name>.0, .1, ...) and reassemble them on the next request. max_chunks bounds the split so an oversized session is dropped with a warning rather than exploded into an unbounded number of cookies. Default is off - the single oversized cookie is dropped with a warning, unchanged from before.

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_request async

process_request(request: Request) -> Response | None

Load the session from the signed cookie into request state.

process_response async

process_response(request: Request, response: Response) -> Response

Save the modified session back into the signed cookie.

TrustedHostMiddleware

Bases: Middleware

Validates Host header against an allow-list.

Supports literal hostnames, the catch-all *, and subdomain wildcards of the form *.example.com (matches api.example.com, a.b.example.com, etc. - never the bare example.com). Matching is case-insensitive; the port portion of Host: is stripped before comparison (RFC 9110 Sec. 7.2).

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_response async

process_response(request: Request, response: Response) -> Response

Called after route handler. Can modify the response.

is_host_allowed

is_host_allowed(host: str) -> bool

Whether host (bare hostname, no port) passes the allow-list.

Public so the WebSocket dispatch path can apply the same check - a WebSocket handshake never reaches an HTTP middleware's process_request.

process_request async

process_request(request: Request) -> Response | None

Reject requests whose Host header is not in the allow-list.

WebSocketOriginMiddleware

Bases: Middleware

Reject cross-site WebSocket handshakes (CSWSH).

A WebSocket handshake is not subject to the Same-Origin Policy and bypasses CORS entirely, so a page on any origin can open a socket to your app unless the handshake Origin is checked. Register this with the origins your own front-end is served from; a handshake whose Origin is present but unlisted is refused with close code 1008.

Browsers always send Origin on a WebSocket handshake (RFC 6455 Sec. 4.1), so allow_missing=True (the default) still blocks every browser-driven CSWSH attempt while leaving non-browser clients (mobile apps, service-to-service) - which legitimately omit Origin - able to connect. Set allow_missing=False to additionally refuse handshakes that carry no Origin at all.

Plain HTTP requests pass straight through - Origin enforcement for HTTP is CORSMiddleware's job.

middleware_name property

middleware_name: str

Resolved exclusion name - the instance/class name or class name.

process_response async

process_response(request: Request, response: Response) -> Response

Called after route handler. Can modify the response.

is_websocket_origin_allowed

is_websocket_origin_allowed(origin: str) -> bool

Whether a handshake carrying origin may proceed.

Public so the WebSocket dispatch path can apply the check - a handshake never reaches an HTTP middleware's process_request.

Principal dataclass

The authenticated identity and granted scopes for the current request.

Usage::

from veloce import Principal, set_principal

set_principal(Principal(subject="user-42", scopes={"mcp:tools"}))

has_scope

has_scope(scope: str) -> bool

Return whether the principal was granted scope.

has_scopes

has_scopes(scopes: Iterable[str]) -> bool

Return whether the principal was granted every scope in scopes.

FixedWindow

Bases: RateLimitStrategy

Allow limit requests per fixed window seconds.

Simple and cheap, but a burst straddling a window boundary can admit up to 2 x limit briefly - use SlidingWindow or TokenBucket when that matters.

InMemoryRateLimitBackend

Bases: RateLimitBackend

Process-local rate-limit state - the default backend.

Not shared across workers, so a multi-worker deployment enforces roughly N x the limit; use veloce.contrib.redis.RedisRateLimitBackend for one shared limit. State is size-bounded to cap memory across many client keys.

RateLimitBackend

Where per-client rate-limit state lives, and the atomic read-modify-write.

evaluate loads the state for key, runs strategy.evaluate, persists the next state with its TTL, and returns the decision - all atomically, so two concurrent requests for the same client cannot both read a stale count.

Subclasses must declare __slots__ (even __slots__ = ()).

RateLimitResult dataclass

The outcome of evaluating one request against a strategy.

RateLimitStrategy

A rate-limit algorithm as a pure state transition.

evaluate takes the client's prior state (None on the first request or after expiry) and the current wall-clock time, and returns the decision, the next state to persist, and a TTL in seconds after which that state can be dropped. It performs no I/O, so a backend can run it under its own atomicity (a dict mutation in-process, a watched transaction on Redis).

Subclasses must declare __slots__ (even __slots__ = ()).

SlidingWindow

Bases: RateLimitStrategy

Allow limit requests per rolling window seconds.

Weights the previous window's count by how far the current window has progressed, so the boundary burst FixedWindow allows is smoothed away while keeping only two counters of state.

TokenBucket

Bases: RateLimitStrategy

Refill rate tokens per per seconds, allowing bursts up to burst.

Each request spends one token; an empty bucket rejects until it refills. The bucket tolerates a short burst (up to burst, default rate) while holding the long-run average to rate/per. A leaky-bucket-style strict limiter is TokenBucket(rate, per, burst=1).

Body

Bases: ParamBase

Request body parameter declaration.

resolve_default

resolve_default() -> Any

Return a fresh default - calling default_factory when one is set.

validate

validate(value: Any, name: str) -> Any

Validate value against constraints.

Cookie

Bases: ParamBase

Cookie parameter declaration.

resolve_default

resolve_default() -> Any

Return a fresh default - calling default_factory when one is set.

validate

validate(value: Any, name: str) -> Any

Validate value against constraints.

File

Bases: ParamBase

File upload parameter declaration.

resolve_default

resolve_default() -> Any

Return a fresh default - calling default_factory when one is set.

validate

validate(value: Any, name: str) -> Any

Validate value against constraints.

Form

Bases: ParamBase

Form field parameter declaration.

resolve_default

resolve_default() -> Any

Return a fresh default - calling default_factory when one is set.

validate

validate(value: Any, name: str) -> Any

Validate value against constraints.

Header

Bases: ParamBase

HTTP header parameter declaration.

resolve_default

resolve_default() -> Any

Return a fresh default - calling default_factory when one is set.

validate

validate(value: Any, name: str) -> Any

Validate value against constraints.

Path

Bases: ParamBase

Path parameter declaration.

resolve_default

resolve_default() -> Any

Return a fresh default - calling default_factory when one is set.

validate

validate(value: Any, name: str) -> Any

Validate value against constraints.

Query

Bases: ParamBase

Query parameter declaration.

resolve_default

resolve_default() -> Any

Return a fresh default - calling default_factory when one is set.

validate

validate(value: Any, name: str) -> Any

Validate value against constraints.

Router

High-performance radix-tree router with a decorator-based route API.

Usage::

from veloce import Router, Veloce

api = Router(prefix="/api")

@api.get("/items/{item_id:int}")
async def get_item(item_id: int):
    return {"item_id": item_id}

app = Veloce()
app.include_router(api)

add_route

add_route(path: Annotated[str, Doc('URL path template, including `{param}` / `{param:converter}` placeholders.')], handler: Annotated[RouteHandler, Doc('Async or sync callable invoked when the route matches.')], methods: Annotated[list[str], Doc('HTTP methods this handler serves (uppercased internally).')], dependencies: Annotated[list[Any] | None, Doc('Dependencies run for this route, appended after the router-level ones.')] = None, response_model: Annotated[Any, Doc("Type used to filter and serialize the handler's return value and the OpenAPI response schema.")] = None, tags: Annotated[list[str] | None, Doc('OpenAPI tags for this route, combined with the router-level tags.')] = None, summary: Annotated[str | None, Doc('Short OpenAPI summary for this operation.')] = None, name: Annotated[str | None, Doc("Endpoint name for `url_for` reverse lookup; defaults to the handler's name.")] = None, description: Annotated[str | None, Doc("OpenAPI description; defaults to the handler's docstring.")] = None, deprecated: Annotated[bool, Doc('Mark the operation as deprecated in the OpenAPI document.')] = False, response_description: Annotated[str, Doc('Description of the successful response in the OpenAPI document.')] = MSG_SUCCESSFUL_RESPONSE, status_code: Annotated[int, Doc('Default HTTP status code for a successful response.')] = HTTP_200_OK, response_class: Annotated[Any, Doc('Response class for this route, overriding the router and framework defaults.')] = None, response_model_include: Annotated[set[str] | None, Doc('Fields to include when serializing the response model.')] = None, response_model_exclude: Annotated[set[str] | None, Doc('Fields to exclude when serializing the response model.')] = None, response_model_exclude_unset: Annotated[bool, Doc('Omit fields left unset on the response model from the serialized output.')] = False, response_model_exclude_defaults: Annotated[bool, Doc('Omit fields equal to their default on the response model from the serialized output.')] = False, response_model_by_alias: Annotated[bool, Doc('Serialize the response model using field aliases instead of attribute names.')] = False, response_model_exclude_none: Annotated[bool, Doc('Omit fields whose value is `None` from the serialized response model.')] = False, include_in_schema: Annotated[bool, Doc('Register the route but omit it from the generated OpenAPI document when False.')] = True, responses: Annotated[dict[int, dict[str, Any]] | None, Doc('Additional OpenAPI responses for this route, overlaid on the router-level ones.')] = None, operation_id: Annotated[str | None, Doc('Explicit OpenAPI `operationId`; defaults to the route name.')] = None, openapi_extra: Annotated[dict[str, Any] | None, Doc("Arbitrary dict deep-merged into this route's OpenAPI operation object.")] = None, defaults: Annotated[dict[str, Any] | None, Doc('Fixed values merged into the path params at dispatch without overriding URL-matched ones.')] = None, callbacks: Annotated[dict[str, Any] | None, Doc("OpenAPI Callback objects emitted verbatim into the operation's `callbacks` field.")] = None, strict_slashes: Annotated[bool | None, Doc('When False, match both slashed and unslashed forms; `None` defers to the app policy.')] = None, subdomain: Annotated[str | None, Doc('Constrain the route to a subdomain of `SERVER_NAME`; `*` matches any non-apex subdomain.')] = None, host: Annotated[str | None, Doc('Constrain the route to an exact `Host` header value (case-insensitive).')] = None, expose_as_mcp_tool: Annotated[bool, Doc('Expose the route as an MCP tool in the contrib MCP registry.')] = False, mcp_description: Annotated[str | None, Doc("LLM-facing description for the route's MCP tool, required when exposed as one.")] = None, expose_as_mcp_resource: Annotated[bool, Doc('Expose the read-only route as an MCP resource in the contrib MCP registry.')] = False, mcp_resource_uri: Annotated[str | None, Doc("Resource URI for the route's MCP resource: a static URI, or a URI template (`users://{user_id}`) binding its path parameters.")] = None, mcp_scopes: Annotated[Sequence[str] | None, Doc('Authorization scopes required to call this route over MCP.')] = None, mcp_icons: Annotated[Sequence[Any] | None, Doc('Optional MCP `Icon` objects a client may render next to the tool/resource.')] = None, mcp_task_support: Annotated[bool, Doc("Allow this route's MCP tool to run as a background task (task-augmented `tools/call`, polled via `tasks/get` / `tasks/result`).")] = False, exclude_middleware: Annotated[Sequence[str] | None, Doc('Names of middleware this route opts out of.')] = None, stream: Annotated[bool, Doc('Opt into request-body streaming: the body is not buffered before the handler, so the handler may consume `request.stream()` incrementally. The synchronous body accessors are unavailable on a streaming route until the body is drained.')] = False) -> None

Register a route in the radix tree.

strict_slashes=False matches both the slashed and unslashed forms without redirecting. None (default) defers to the app's global redirect_slashes policy.

subdomain="api" constrains the route to requests whose Host header matches {subdomain}.{app.config["SERVER_NAME"]}. The match is exact (no globbing); for wildcard subdomain matching use subdomain="*" and inspect request.subdomain inside the handler.

match

match(method: str, path: str) -> RouteMatch | None

Match a request path. Static map, then radix tree, then regex.

O(1) for a literal path (the static map), else O(k) on the tree where k = path depth. The regex fallback runs only when the tree misses and regex routes are registered; the tree always wins over regex when both could match.

get_allowed_methods

get_allowed_methods(path: str) -> list[str]

Get allowed methods for a path (for 405 responses).

Unions the methods reachable through the radix tree AND any regex routes that match the same path, so a path served by a tree handler on one method and a regex handler on another reports both for 405/OPTIONS. Tree methods are listed first (dispatch precedence); duplicates removed.

route

route(path: Annotated[str, Doc('URL path template, including `{param}` / `{param:converter}` placeholders.')], methods: Annotated[list[str] | None, Doc('HTTP methods this handler serves; defaults to `GET`.')] = None, dependencies: Annotated[list[Any] | None, Doc('Dependencies run for this route, appended after the router-level ones.')] = None, response_model: Annotated[Any, Doc("Type used to filter and serialize the handler's return value and the OpenAPI response schema.")] = None, tags: Annotated[list[str] | None, Doc('OpenAPI tags for this route, combined with the router-level tags.')] = None, summary: Annotated[str | None, Doc('Short OpenAPI summary for this operation.')] = None, name: Annotated[str | None, Doc("Endpoint name for `url_for` reverse lookup; defaults to the handler's name.")] = None, description: Annotated[str | None, Doc("OpenAPI description; defaults to the handler's docstring.")] = None, deprecated: Annotated[bool, Doc('Mark the operation as deprecated in the OpenAPI document.')] = False, response_description: Annotated[str, Doc('Description of the successful response in the OpenAPI document.')] = MSG_SUCCESSFUL_RESPONSE, status_code: Annotated[int, Doc('Default HTTP status code for a successful response.')] = HTTP_200_OK, response_class: Annotated[Any, Doc('Response class for this route, overriding the router and framework defaults.')] = None, response_model_include: Annotated[set[str] | None, Doc('Fields to include when serializing the response model.')] = None, response_model_exclude: Annotated[set[str] | None, Doc('Fields to exclude when serializing the response model.')] = None, response_model_exclude_unset: Annotated[bool, Doc('Omit fields left unset on the response model from the serialized output.')] = False, response_model_exclude_defaults: Annotated[bool, Doc('Omit fields equal to their default on the response model from the serialized output.')] = False, response_model_by_alias: Annotated[bool, Doc('Serialize the response model using field aliases instead of attribute names.')] = False, response_model_exclude_none: Annotated[bool, Doc('Omit fields whose value is `None` from the serialized response model.')] = False, include_in_schema: Annotated[bool, Doc('Register the route but omit it from the generated OpenAPI document when False.')] = True, responses: Annotated[dict[int, dict[str, Any]] | None, Doc('Additional OpenAPI responses for this route, overlaid on the router-level ones.')] = None, operation_id: Annotated[str | None, Doc('Explicit OpenAPI `operationId`; defaults to the route name.')] = None, openapi_extra: Annotated[dict[str, Any] | None, Doc("Arbitrary dict deep-merged into this route's OpenAPI operation object.")] = None, defaults: Annotated[dict[str, Any] | None, Doc('Fixed values merged into the path params at dispatch without overriding URL-matched ones.')] = None, callbacks: Annotated[dict[str, Any] | None, Doc("OpenAPI Callback objects emitted verbatim into the operation's `callbacks` field.")] = None, strict_slashes: Annotated[bool | None, Doc('When False, match both slashed and unslashed forms; `None` defers to the app policy.')] = None, subdomain: Annotated[str | None, Doc('Constrain the route to a subdomain of `SERVER_NAME`; `*` matches any non-apex subdomain.')] = None, host: Annotated[str | None, Doc('Constrain the route to an exact `Host` header value (case-insensitive).')] = None, expose_as_mcp_tool: Annotated[bool, Doc('Expose the route as an MCP tool in the contrib MCP registry.')] = False, mcp_description: Annotated[str | None, Doc("LLM-facing description for the route's MCP tool, required when exposed as one.")] = None, expose_as_mcp_resource: Annotated[bool, Doc('Expose the read-only route as an MCP resource in the contrib MCP registry.')] = False, mcp_resource_uri: Annotated[str | None, Doc("Resource URI for the route's MCP resource: a static URI, or a URI template (`users://{user_id}`) binding its path parameters.")] = None, mcp_scopes: Annotated[Sequence[str] | None, Doc('Authorization scopes required to call this route over MCP.')] = None, mcp_icons: Annotated[Sequence[Any] | None, Doc('Optional MCP `Icon` objects a client may render next to the tool/resource.')] = None, mcp_task_support: Annotated[bool, Doc("Allow this route's MCP tool to run as a background task (task-augmented `tools/call`, polled via `tasks/get` / `tasks/result`).")] = False, exclude_middleware: Annotated[Sequence[str] | None, Doc('Names of middleware this route opts out of.')] = None, stream: Annotated[bool, Doc('Opt into request-body streaming: the body is not buffered before the handler, so the handler may consume `request.stream()` incrementally.')] = False) -> Callable

Generic route decorator.

exclude_middleware=["CSRFMiddleware"] opts this route out of the named middleware (matched against each middleware's name), so a webhook or health-check route can skip CSRF, auth, or rate limiting without forking the middleware. Routes that declare no exclusions pay no extra per-request cost.

trace

trace(path: str, **kwargs) -> Callable

TRACE route decorator - RFC 9110 Sec. 9.3.8.

query

query(path: str, **kwargs) -> Callable

QUERY route decorator - RFC 10008.

QUERY is safe and idempotent like GET but carries a request body like POST, for read-only operations whose parameters do not fit a URL (search, filtering, paging). The handler reads the body exactly as a POST handler does (request.get_json() / a body model parameter).

websocket

websocket(path: Annotated[str, Doc('URL path template for the WebSocket route, including `{param}` placeholders.')]) -> Callable

Register a WebSocket route via decorator.

websocket_listener

websocket_listener(path: str, *, receive: str = 'json', send: str = 'json', on_connect: RouteHandler | Callable[..., Any] | None = None, on_disconnect: RouteHandler | Callable[..., Any] | None = None) -> Callable

Declarative WebSocket route - wrap a per-message callback.

The decorated callback handles one message at a time; the framework owns the accept handshake, the receive loop, and the clean close on disconnect. The callback is called as cb(data), or cb(ws, data) when its first parameter is named ws/socket (or it takes two positional parameters). Returning a non-None value sends it back in send mode; returning None sends nothing.

receive/send select the codec ("json" default, or "text" / "bytes"). on_connect(ws) runs after accept; on_disconnect(ws) always runs when the loop ends, including on peer disconnect. Sync callbacks and hooks are offloaded to the executor.

Usage::

@app.websocket_listener("/echo")
async def echo(data):
    return data

For full control over the handshake and loop use @app.websocket.

add_websocket_route

add_websocket_route(path: Annotated[str, Doc('URL path template for the WebSocket route, including `{param}` placeholders.')], handler: Annotated[RouteHandler, Doc('Callable invoked with the accepted WebSocket connection when the route matches.')]) -> None

Register a WebSocket route imperatively (ASGI shape).

The non-decorator form of @app.websocket(path).

add_api_websocket_route

add_api_websocket_route(path: str, endpoint: RouteHandler, name: str | None = None) -> None

Register an imperative WebSocket route, mirroring add_api_route.

The non-decorator form of @app.websocket(path). name, when given, registers the route for reverse lookup so app.url_for(name) resolves to its path.

add_api_route

add_api_route(path: str, endpoint: RouteHandler, *, methods: list[str] | None = None, **kwargs: Any) -> None

Register a route imperatively.

The non-decorator form: the handler argument is named endpoint here and forwarded to add_route (where it is handler). All route kwargs - response_model, tags, dependencies, status_code, openapi_extra, ... - pass straight through. Defaults to ["GET"] when methods is omitted.

url_for

url_for(name: str, **path_params: Any) -> str

Reverse URL lookup by route name (url_for).

Substitutes each {name} placeholder in the registered template with the matching path_params kwarg. Underscore-prefixed kwargs are control parameters (convention):

  • _external=True - return an absolute URL. Uses app.config["SERVER_NAME"] when set, otherwise falls back to localhost. Caller should override _scheme/_host for anything more specific.
  • _scheme="https" - override scheme on the absolute URL.
  • _host="example.com" - override host on the absolute URL.
  • _anchor="section" - append #section.
  • Any other unmatched kwarg becomes a query-string parameter.

include_router

include_router(router: Router, prefix: str = '') -> None

Include another router (a sub-router with its own prefix, tags, and hooks).

Secret

Hold a str/bytes secret while resisting accidental disclosure.

Usage::

token = Secret(os.environ["API_TOKEN"])
send(token.reveal())

reveal

reveal() -> str | bytes

Return the wrapped plaintext. The only way to obtain it.

APIKeyCookie

Bases: _APIKeyBase

API Key authentication via cookie.

challenge

challenge() -> dict[str, str]

The WWW-Authenticate challenge sent on a 401.

Returns {WWW-Authenticate: APIKey realm="..."} when a realm is configured, else the bare APIKey token, which still satisfies RFC 9110 Sec. 11.6.1. Subclasses may override to emit a custom challenge.

APIKeyHeader

Bases: _APIKeyBase

API Key authentication via HTTP header.

challenge

challenge() -> dict[str, str]

The WWW-Authenticate challenge sent on a 401.

Returns {WWW-Authenticate: APIKey realm="..."} when a realm is configured, else the bare APIKey token, which still satisfies RFC 9110 Sec. 11.6.1. Subclasses may override to emit a custom challenge.

APIKeyQuery

Bases: _APIKeyBase

API Key authentication via query parameter.

challenge

challenge() -> dict[str, str]

The WWW-Authenticate challenge sent on a 401.

Returns {WWW-Authenticate: APIKey realm="..."} when a realm is configured, else the bare APIKey token, which still satisfies RFC 9110 Sec. 11.6.1. Subclasses may override to emit a custom challenge.

BadResetToken

Bases: Exception

Raised on programmer misuse; invalid tokens return False instead.

Claims

Bases: Mapping[str, Any]

Read-only mapping over a decoded JWT payload.

Usage::

claims = decode_jwt(token, secret, algorithms=["HS256"])
user_id = claims["sub"]

ExpiredSignatureError

Bases: JWTError

The token's exp claim is in the past (beyond leeway).

HTTPBasic

Bases: SecurityScheme

HTTP Basic authentication - extracts username:password from Authorization header.

HTTPBasicCredentials

HTTP Basic auth credentials.

HTTPBearer

Bases: _BearerScheme

HTTP Bearer token authentication.

HTTPDigest

Bases: SecurityScheme

HTTP Digest authentication - RFC 7616.

Parses the Authorization: Digest ... header into the named fields and returns them as HTTPDigestCredentials. This class does NOT validate the response hash - the application owns the secret (HA1) and must compute the expected digest itself; Digest's whole point is that the secret never crosses the wire. Veloce's job is to parse the challenge response and to emit a 401 + WWW-Authenticate: Digest ... header when auth is missing or malformed.

The scheme's responsibility is the parse + challenge dance; verifying the response is application logic.

HTTPDigestCredentials

Parsed Digest auth challenge response - RFC 7616 Sec. 3.4.

ImmatureSignatureError

Bases: JWTError

The token's nbf claim is in the future (beyond leeway).

InvalidAudienceError

Bases: JWTError

The aud claim does not match the expected audience.

InvalidIssuerError

Bases: JWTError

The iss claim does not match the expected issuer.

InvalidSignatureError

Bases: JWTError

The HMAC signature did not verify against the secret.

InvalidTokenError

Bases: JWTError

Malformed structure: not three segments, bad base64, or bad JSON.

JWTError

Bases: Exception

Base class for all JWT decode/encode failures.

MissingClaimError

Bases: JWTError

A claim named in require is absent from the payload.

OAuth2AuthorizationCodeBearer

Bases: _OAuth2BearerScheme

OAuth2 Authorization-Code (with PKCE) Bearer flow.

Extracts a Bearer token from the Authorization: header exactly like OAuth2PasswordBearer; the difference is the OpenAPI security scheme it advertises (authorizationUrl + tokenUrl + scopes), which is what an interactive OAuth2 client (Swagger UI's "Authorize" button, an SPA's auth library) uses to start the redirect dance.

The construction shape is chosen so an OpenAPI snippet generated from a standard OpenAPI document can be replayed against veloce without rewrites:

oauth2 = OAuth2AuthorizationCodeBearer(
    authorizationUrl="https://auth.example.com/authorize",
    tokenUrl="https://auth.example.com/token",
    refreshUrl=None,
    scopes={"read:items": "Read items", "write:items": "Write items"},
    auto_error=True,
)

OAuth2PasswordBearer

Bases: _OAuth2BearerScheme

OAuth2 Password Bearer flow - extracts token from Authorization header.

OAuth2PasswordRequestForm

OAuth2 password request form data.

from_request async classmethod

from_request(request: Request) -> OAuth2PasswordRequestForm

Parse an OAuth2 password grant from the request form data.

OAuth2PasswordRequestFormStrict

Bases: OAuth2PasswordRequestForm

OAuth2PasswordRequestForm with a mandatory grant_type.

The non-strict form leaves grant_type optional; the strict form requires it and constrains the value to the literal password (RFC 6749 Sec. 4.3.2). Missing or mismatched values fail validation with 422.

from_request async classmethod

from_request(request: Request) -> OAuth2PasswordRequestFormStrict

Parse and validate that grant_type is present and equals 'password'.

OpenIdConnect

Bases: _OAuth2BearerScheme

OpenID Connect Bearer authentication.

Same Bearer extraction logic as the OAuth2 schemes; the OpenAPI scheme advertises a single openIdConnectUrl pointing at the provider's .well-known/openid-configuration document. Clients auto-discover everything else from there.

SecurityScheme

Base contract for callable authentication schemes.

Owns the auto_error field and documents the resolver's expected shape: a __call__(self, request) that returns the extracted credential, or None when authentication is absent and auto_error is False. Not an abc.ABC: the hook raises NotImplementedError so subclasses that forget to override fail loudly without pulling in the ABC machinery.

UnsupportedAlgorithmError

Bases: JWTError

The header alg is not allow-listed, unknown, or none.

InMemorySessionStore

Bases: SessionStore

A process-local SessionStore - a dict with per-entry expiry.

Fine for a single-process app and for tests. It does not share state across workers, so a multi-worker deployment needs a shared backend (e.g. Redis) implementing the SessionStore interface.

read async

read(session_id: str) -> dict[str, Any] | None

Return a copy of the stored payload, or None when absent or expired.

write async

write(session_id: str, data: dict[str, Any], max_age: int) -> None

Store a copy of data under session_id, expiring after max_age seconds.

delete async

delete(session_id: str) -> None

Drop session_id from the store. No-op if not present.

replace async

replace(session_id: str, data: dict[str, Any], max_age: int) -> bool

Write data only when session_id still exists and is unexpired.

touch async

touch(session_id: str, max_age: int) -> bool

Refresh the expiry of an existing, unexpired entry without copying its payload.

sweep_expired

sweep_expired() -> int

Drop every expired entry and return how many were removed.

Callers that want deterministic eviction (e.g. a background task on a known cadence) can call this directly rather than relying on the probabilistic sweep that fires from write / replace.

Session

Bases: dict[str, Any]

The request session - a dict that knows when it has changed.

permanent property writable

permanent: bool

Whether the session cookie should use the longer lifetime.

backed by the reserved _permanent key, so the flag persists in the cookie across requests and toggling it counts as a session mutation.

regenerate_id

regenerate_id() -> None

Request a fresh server-side session id on the next response.

Call this at a privilege boundary - login, role change - so a pre-existing (possibly attacker-planted) session id cannot be replayed against the now-elevated session: the session-fixation defence. It marks the session modified so the rotation is written back. Harmless with cookie-only sessions, which carry no server-side id to rotate.

clear

clear() -> None

Remove every key and mark the session modified.

pop

pop(key: Any, *default: Any) -> Any

Remove key and return its value, marking the session modified on removal.

popitem

popitem() -> Any

Remove and return the last (key, value) pair, marking the session modified.

setdefault

setdefault(key: Any, default: Any = None) -> Any

Insert default under key when absent, marking the session modified on insert.

update

update(*args: Any, **kwargs: Any) -> None

Merge keys into the session, marking it modified unless the input is empty.

SessionStore

Server-side session backend interface.

A concrete store persists session payloads keyed by an opaque session id; ServerSessionMiddleware drives it. The methods are async so a network-backed store (Redis, a database) can implement them without blocking the event loop - the bundled InMemorySessionStore satisfies the contract without any real awaiting.

read async

read(session_id: str) -> dict[str, Any] | None

Return the stored payload for session_id, or None when it is absent, expired, or has been revoked.

write async

write(session_id: str, data: dict[str, Any], max_age: int) -> None

Persist data under session_id, to expire after max_age seconds.

delete async

delete(session_id: str) -> None

Revoke session_id - a later read of it must return None.

replace async

replace(session_id: str, data: dict[str, Any], max_age: int) -> bool

Write data for session_id only if it still exists.

Returns True on success, False when the id is absent - it was revoked or expired. This is the race-safe write the middleware uses for an already-stored session, so a request still in flight cannot resurrect a session a concurrent delete removed.

The default is a non-atomic read-then-write; a store with an atomic conditional write (Redis SET ... XX, a DB UPDATE) should override this to close the check-then-write window.

touch async

touch(session_id: str, max_age: int) -> bool

Extend the expiry of an existing entry without rewriting its payload.

Returns True when the id existed and its TTL was refreshed, False when it was absent (revoked or expired). This is the sliding-expiry write ServerSessionMiddleware uses on a read-only access, so an idle session stays alive without round-tripping its full payload.

The default reads then rewrites the payload; a store with a native TTL-refresh primitive (Redis EXPIRE, a DB UPDATE ... expires_at) should override this to avoid moving the payload.

BadData

Bases: BadSignature

The token's payload could not be decoded (malformed base64 / JSON).

BadSignature

Bases: Exception

The token's signature did not verify against the configured secret.

BadTimeSignature

Bases: BadSignature

The signature verified but the token is older than max_age.

Signer

HMAC-SHA256 signer for arbitrary JSON-serialisable values.

Usage

s = Signer(secret="server-secret", salt="reset-token") token = s.dumps({"user_id": 42}) ... data = s.loads(token, max_age=3600) # raises if older than 1h

add_fallback_secret

add_fallback_secret(secret: str | bytes, salt: str | bytes = 'veloce.signing') -> None

Add an additional secret accepted for verification (not signing).

Used during secret rotation: configure the new secret as primary, keep the old one as a fallback for the rotation window. Tokens signed with the fallback still verify; new tokens use the primary.

dumps

dumps(data: Any) -> str

Serialise data to a signed, timestamped, URL-safe token.

loads

loads(token: str, max_age: int | None = None) -> Any

Verify token and return the original data.

Raises BadSignature on tamper / unknown secret, BadTimeSignature when max_age is set and the token's timestamp is older than that.

EventSourceResponse

Bases: Response

SSE streaming response - sends events over a long-lived connection.

Usage::

@app.get("/events")
async def events(request: Request):
    async def generate():
        for i in range(10):
            yield ServerSentEvent(data=f"Event {i}")
            await asyncio.sleep(1)
    return EventSourceResponse(generate())

Pass ping=<seconds> to emit a keep-alive comment frame whenever no event is produced within that interval - useful for holding idle connections open through proxies that close silent sockets.

body property writable

body: bytes

Return the response body bytes.

media_type property writable

media_type: str

Return the full Content-Type including parameters.

is_json property

is_json: bool

True when Content-Type is JSON.

Matches application/json and any application/*+json structured suffix (RFC 6839 Sec. 3.1).

mimetype property writable

mimetype: str

The bare media type - Content-Type without parameters.

text/html; charset=utf-8 -> text/html. Lower-cased and stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).

status property writable

status: str

Full HTTP status line, e.g. "200 OK".

Assignable: accepts an int (200), a bare numeric string ("200"), or a full status line ("200 OK" / "404 Not Found"). The leading integer is parsed into status_code.

content_length property

content_length: int

Length of the response body in bytes.

Always derived from len(body). Streaming responses (which don't materialise the body) return 0 here; see is_streamed.

is_streamed property

is_streamed: bool

True when the response body is a streaming iterator.

charset property writable

charset: str

Response charset from Content-Type.

Falls back to "utf-8" when no charset parameter is present. Assignable: setting it rewrites the charset= parameter on the existing Content-Type (the bare media type is preserved).

mimetype_params property

mimetype_params: dict[str, str]

Parameters of the Content-Type header.

Everything after the bare media type, as a dict of lower-cased parameter names to their (unquoted) values. For text/html; charset=utf-8 this is {"charset": "utf-8"}. Returns an empty dict when no parameters are present.

last_modified property writable

last_modified: Any

Parsed Last-Modified header -> UTC datetime or None.

Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date forms. Returns None on missing/unparseable.

expires property writable

expires: Any

Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).

cookies property

cookies: dict[str, str]

Parsed cookie jar from this response's Set-Cookie header(s).

Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie: respected) and returns {name: value}. Multiple cookies with the same name resolve to the last set - matches the wire behaviour where the client also keeps the most-recent value. Caller introspection only; mutation goes through set_cookie().

headerlist property

headerlist: list[tuple[str, str]]

Headers flattened to a (name, value) tuple list.

Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so downstream wire-emit / inspection code gets the per-cookie view ASGI requires.

data property writable

data: bytes

Body bytes alias for Response.body.

Read returns the current body; writing through the setter replaces the body, invalidates any cached HTTP/1.1 encoded bytes (_encoded), and updates Content-Length on the headers if it was previously set.

vary property writable

vary: Any

The Vary header as a HeaderSet.

Returns a fresh HeaderSet parsed from the current header. Assign a HeaderSet, iterable of strings, or a comma-separated string to replace it. Mutating the returned object does not write back - call add_vary(...) or reassign for that.

allow property writable

allow: Any

The Allow header as a HeaderSet.

Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1). Assign a HeaderSet, iterable, or comma-separated string.

www_authenticate property writable

www_authenticate: str | None

The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.

Sent on 401 Unauthorized to tell the client which auth scheme(s) to use. None when unset.

content_encoding property writable

content_encoding: str | None

The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.

content_language property writable

content_language: str | None

The Content-Language header - RFC 9110 Sec. 8.5. None when unset.

accept_ranges property writable

accept_ranges: str | None

The Accept-Ranges header - RFC 9110 Sec. 14.3.

Typically bytes (range requests supported) or none (explicitly unsupported). None when the header is unset.

content_range property

content_range: str | None

The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.

date property writable

date: Any

The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.

Returns None when unset or unparseable. Assign a datetime or POSIX timestamp to set it; assign None to remove it.

location property writable

location: str | None

The Location header - RFC 9110 Sec. 10.2.2. None when unset.

content_location property writable

content_location: str | None

The Content-Location header - RFC 9110 Sec. 8.7. None when unset.

retry_after property writable

retry_after: Any

The Retry-After header - RFC 9110 Sec. 10.2.3.

Returns an int (delay in seconds) when the header is numeric, a tz-aware datetime when it's an HTTP-date, or None when unset. Assign an int / timedelta / datetime to set it; assign None to remove it.

age property writable

age: int | None

The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.

cache_control property

cache_control: Any

Parsed Cache-Control header (read-only view).

For setting directives, prefer set_cache_control(...) which writes the header directly. This property is convenient for introspection: resp.cache_control.max_age, resp.cache_control.no_store, etc.

get_json

get_json() -> Any

Parse the response body as JSON.

Returns None for an empty body. Useful in tests to inspect a JSON response without re-decoding body by hand. Raises if the body is non-empty and not valid JSON.

encode

encode() -> bytes

Encode to raw HTTP/1.1 bytes - called once, cached.

set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Build a Set-Cookie header per RFC 6265.

The cookie name must be a valid RFC 6265 token (no spaces, separators, or control characters) and must not collide with a cookie-attribute keyword (Path, Max-Age, ...); a violation raises ValueError.

samesite defaults to "Lax" - a CSRF-resistant default that matches modern browser behaviour. Pass samesite="None" (with secure=True) for a cookie that must travel on cross-site requests, or samesite=None/"" to omit the attribute.

expires= accepts a datetime, a Unix timestamp int|float, or an already-formatted IMF-fixdate str. When both max_age and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients prefer Max-Age when supported, falling back to Expires on legacy IE).

partitioned=True adds the CHIPS Partitioned attribute (Cookies Having Independent Partitioned State) - a partitioned cookie is keyed to the top-level site, so embedded third-party contexts each get an isolated jar. Partitioned requires Secure, so it is only emitted when secure=True.

prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3 name prefix (__Host- / __Secure-) and enforce its invariants: "secure" requires secure=True; "host" also requires path="/" and no domain. A violation raises ValueError.

The cookie name and value are rejected if they contain CR, LF, or NUL - untrusted data must not be able to inject additional cookies or response headers. dump_cookie performs that CRLF check on all five fields (name, value, domain, path, samesite), so set_cookie does not repeat it.

calculate_content_length

calculate_content_length() -> int

Set Content-Length from len(body) and return the value.

Useful when a caller mutates body directly and wants the header to follow. The ASGI emit path computes Content-Length from body on the fly anyway; this helper is for callers that want it locked into self.headers ahead of time.

set_data

set_data(value: bytes | str) -> None

Replace the response body.

Accepts bytes or str (UTF-8 encoded). Invalidates the cached HTTP/1.1 encode so the new body wire-out on the next emit. Refreshes Content-Length when previously set on the headers.

set_cache_control

set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str

Build and set the Cache-Control header - RFC 9111 Sec. 5.2.

Combines the standard directives in the order RFC 9111 Sec. 5.2 documents. Values that are False / None are omitted, so a plain resp.set_cache_control(max_age=3600, public=True) produces Cache-Control: public, max-age=3600. Returns the value set.

add_vary

add_vary(*header_names: str) -> str

Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.

Merges with any existing Vary value (de-duplicates, case-insensitive). Returns the resulting header value. Useful when middleware wants to communicate "this response depends on the named request headers" without clobbering existing entries.

set_basic_auth_challenge

set_basic_auth_challenge(realm: str = 'Authentication Required') -> str

Write a Basic WWW-Authenticate challenge - RFC 7617.

Convenience for the common 401 case: WWW-Authenticate: Basic realm="<realm>", charset="UTF-8". Returns the header value written.

set_content_range

set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str

Write a Content-Range header - RFC 9110 Sec. 14.4.

  • set_content_range(0, 499, 1234) -> bytes 0-499/1234.
  • start/stop both None -> an unsatisfied-range response: bytes */1234 (length required in that form).
  • length None -> unknown total: bytes 0-499/*.

Returns the header value written.

set_etag

set_etag(etag: str, weak: bool = False) -> None

Set the ETag header from an explicit value.

Quotes the value if the caller passed it bare. Prepends W/ when weak=True. Use add_etag() for body-derived MD5 ETags; set_etag is for callers that already have an authoritative tag (DB revision, commit hash, version counter).

get_etag

get_etag() -> tuple[str | None, bool]

Return (etag, is_weak) parsed from the ETag header.

(None, False) when unset. Returned tag keeps its quotes so it compares directly with If-None-Match values.

freeze

freeze() -> None

Pre-compute the cached HTTP/1.1 encode.

For buffered responses, populates _encoded so subsequent access pays no encode cost. For streaming responses, no-op. Used by response caching layers that want immutable bytes.

iter_encoded

iter_encoded() -> Any

Yield the response body.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous iterator yielding bytes. Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain with async for.

Callers must branch on response.is_streamed (or use inspect.isasyncgen / hasattr(it, "__aiter__")) to pick the right loop, e.g.:

it = response.iter_encoded()
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

The return shape is mode-dependent: a buffered response yields a synchronous iterator of bytes, a streaming response yields the underlying AsyncIterator[bytes]. Branch on response.is_streamed to drain with the right loop.

iter_chunked

iter_chunked(size: int) -> Any

Yield the response body in fixed-size chunks.

Return type is mode-dependent and the two modes are NOT interchangeable:

  • Buffered response (is_streamed is False) -> returns a synchronous generator yielding bytes slices of length size (the final slice may be shorter). Drain with for.
  • Streaming response (is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]); size is ignored because chunk boundaries are controlled by the source generator, not the caller. Drain with async for.

Pick the loop based on response.is_streamed:

it = response.iter_chunked(4096)
if response.is_streamed:
    async for chunk in it:
        ...
else:
    for chunk in it:
        ...

size must be positive. The return shape is mode-dependent: branch on response.is_streamed to drain with the right loop.

add_etag

add_etag(weak: bool = False) -> str

Compute and attach an ETag derived from the body.

Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3. weak=True prepends W/ so the validator is treated as a weak match (matching content but possibly different byte-for-byte). Sets ETag even if one was already set; pass the explicit ETag in __init__(headers=...) to skip this. Returns the value set.

make_conditional

make_conditional(request: Any) -> Response

Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.

Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence), then If-Modified-Since. On a match, mutates self to status 304 with no body. Returns self so callers can use it inline: return resp.make_conditional(request).

Handles If-None-Match: * (matches any current representation of the resource) and the weak/strong ETag comparison rules.

check_preconditions

check_preconditions(request: Any) -> Response

Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).

Raises PreconditionFailed (412) when the request carries an If-Match header that the response's current ETag does not satisfy under the strong comparison (Sec. 8.8.3.1) - the lost-update guard. If-Match: * is satisfied whenever a current representation exists, approximated here by the presence of an ETag header. With no If-Match header the response is returned unchanged. Returns self so it can be chained: return resp.check_preconditions(request).

Invoke this inside a handler (where HTTPException is converted to a response); it raises rather than mutating the status.

set_content_disposition

set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str

Write a Content-Disposition header - RFC 6266.

disposition is "attachment" (force download) or "inline" (render in-browser). When filename is given, an ASCII quotable name uses filename="..." (spaces and punctuation preserved, only \ and " escaped); a non-ASCII or non-quotable name uses only the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot. Returns the header value written.

delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None

Delete a cookie by overwriting it with an empty value + Max-Age=0.

The browser only treats the new cookie as a replacement for the existing one if Path, Domain, and the Secure / SameSite / Partitioned attributes match - otherwise it stores both. So a session cookie originally set with Secure; SameSite=None (or with Partitioned) will not be deleted by a plain delete_cookie(key) call. Pass the same flags here. prefix deletes the cookie under its true __Host-/__Secure- wire name and enforces the same invariants on the deletion's attributes.

stream_to async

stream_to(transport: Any, drain: Callable[[], Awaitable[None]] | None = None) -> None

Stream SSE events to transport.

When drain is supplied (the raw serving protocol passes its write-side flow-control awaitable) it is awaited after each event, so a fast event producer feeding a slow client is throttled at the transport buffer instead of growing it without bound. It is a no-op until the buffer crosses the high-water mark.

ServerSentEvent

A single SSE event.

json classmethod

json(payload: Any, *, event: str | None = None, id: str | None = None, retry: int | None = None) -> ServerSentEvent

Build an event whose data field is payload serialized to JSON.

Serialization runs once here, off the per-event stream loop, and the result is stored in the plain data field - so encode() stays the same branch-free path it is for a raw data= string. Use the regular constructor when the payload is already a formatted string.

encode

encode() -> bytes

Encode the event as an SSE-formatted byte string.

AsyncTestClient

Async in-memory test client - drives the app through its ASGI surface.

The async counterpart of TestClient: used as an async context manager inside an async test, so each request is awaited on the test's own running event loop instead of through a private loop. The request methods (get / post / ...) are coroutines.

async with AsyncTestClient(app) as client:
    resp = await client.get("/")

Cookie persistence, redirect following, and the JSON / form / files body shapes match TestClient exactly. WebSocket testing stays on the sync TestClient.websocket_connect.

cookies property

cookies: _TestClientCookies

Live view of the client's cookie jar (see TestClient.cookies).

set_cookie(key: str, value: str) -> None

Add or update a cookie sent on every subsequent request.

delete_cookie(key: str) -> None

Remove a cookie from the jar. No-op if not present.

get async

get(path: str, headers: dict[str, str] | None = None, params: dict[str, str] | Sequence[tuple[str, str]] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a GET request and return the response.

post async

post(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a POST. stream feeds the body as multiple http.request chunks (a sync Iterable or AsyncIterable of bytes/str); when given it takes precedence over and excludes json/data/content/files.

put async

put(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a PUT. See post for the stream chunked-body parameter.

patch async

patch(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a PATCH. See post for the stream chunked-body parameter.

delete async

delete(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a DELETE request and return the response.

head async

head(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a HEAD request and return the response.

options async

options(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send an OPTIONS request and return the response.

request async

request(method: str, path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, params: dict[str, str] | Sequence[tuple[str, str]] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Generic verb-agnostic request dispatcher (see TestClient.request).

TestClient

Sync test client - drives the app through its ASGI surface.

Usage::

client = TestClient(app)
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "ok"}

cookies property

cookies: _TestClientCookies

Live view of the client's cookie jar.

Supports dict-like access (client.cookies["session"]), assignment (client.cookies["k"] = "v"), deletion, iteration, and bulk clear(). Cookies the server sends on responses are automatically merged in via _update_cookies. The state persists across calls until the client is closed.

set_cookie(key: str, value: str) -> None

Add or update a cookie sent on every subsequent request.

delete_cookie(key: str) -> None

Remove a cookie from the jar. No-op if not present.

session_transaction

session_transaction() -> Any

Mutate the session outside a request.

Yields a Session dict pre-loaded from the current session cookie (if any). On block exit the session is re-signed with the app's SessionMiddleware secret and stored in the cookie jar, so the next request carries it::

with client.session_transaction() as sess:
    sess["user_id"] = 7

Raises RuntimeError if the app has no SessionMiddleware.

get

get(path: str, headers: dict[str, str] | None = None, params: dict[str, str] | Sequence[tuple[str, str]] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a GET request and return the response.

post

post(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a POST. stream feeds the body as multiple http.request chunks (a sync Iterable or AsyncIterable of bytes/str); when given it takes precedence over and excludes json/data/content/files.

put

put(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a PUT. See post for the stream chunked-body parameter.

patch

patch(path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Send a PATCH. See post for the stream chunked-body parameter.

delete

delete(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a DELETE request and return the response.

head

head(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send a HEAD request and return the response.

options

options(path: str, headers: dict[str, str] | None = None, follow_redirects: bool | None = None) -> TestResponse

Send an OPTIONS request and return the response.

request

request(method: str, path: str, json: Any = None, data: dict[str, str] | None = None, headers: dict[str, str] | None = None, content: bytes | None = None, files: dict[str, Any] | None = None, params: dict[str, str] | Sequence[tuple[str, str]] | None = None, follow_redirects: bool | None = None, stream: Any | None = None) -> TestResponse

Generic request dispatcher - httpx/test-client shape.

client.request("PATCH", "/x", json=...) is the verb-agnostic form of client.get / client.post / .... Bodies (json / data / content / files / stream) and params are handled exactly as the per-verb methods do; stream (see post) excludes the buffered body forms.

websocket_connect

websocket_connect(path: str, subprotocols: list[str] | None = None, headers: dict[str, str] | None = None) -> _WebSocketSession

Open an in-memory WebSocket against the app - context manager.

Drives the ASGI websocket protocol: synthesise the scope, send websocket.connect, route to the handler, then forward send_text / receive_text / close calls to the running handler through a pair of asyncio queues.

close

close() -> None

Run shutdown lifecycle and close the loop if we own it.

MethodView

Bases: View

Class-based view dispatching one async method per HTTP verb.

Subclasses define get / post / ... as async def. methods is inferred from the defined verbs unless set explicitly.

as_view classmethod

as_view(name: str, *class_args: Any, **class_kwargs: Any) -> Callable

Build a view function bound to this class.

Honours init_every_request (fresh instance per request vs a single shared one) and applies decorators. The returned callable carries view_class, methods, and __name__ = name for router introspection and url_for naming.

dispatch_request async

dispatch_request(*args: Any, **kwargs: Any) -> Any

Pick the matching method by request verb and forward arguments.

The first positional argument is expected to be the Request; the rest are path parameters. If the class doesn't implement the verb, raises MethodNotAllowed with Allow: set.

View

Base class-based view - one dispatch_request per class.

Subclasses override dispatch_request. Class attributes:

  • methods - the HTTP verbs this view answers (advisory; used by the router / OpenAPI introspection).
  • decorators - decorators applied to the generated view function, innermost-first (the last entry wraps outermost).
  • init_every_request - when True (default) a fresh instance is built for each request; when False one instance is reused.

as_view classmethod

as_view(name: str, *class_args: Any, **class_kwargs: Any) -> Callable

Build a view function bound to this class.

Honours init_every_request (fresh instance per request vs a single shared one) and applies decorators. The returned callable carries view_class, methods, and __name__ = name for router introspection and url_for naming.

dispatch_request async

dispatch_request(*args: Any, **kwargs: Any) -> Any

Handle the request - subclasses must override.

EventLoopWatchdog

Detects event-loop stalls and reports the blocked stack.

A heartbeat callback re-arms itself on the loop every interval seconds; a separate daemon thread measures how long it has been since the last heartbeat while the loop is running. When that gap exceeds stall_threshold something is blocking the loop, and the watchdog logs a warning with the loop thread's current stack plus a prescriptive hint (blocking-I/O versus CPU-bound).

Each distinct stall is reported once - the heartbeat counter is frozen for the stall's whole duration, and the watch thread reports a given counter value at most once.

start

start() -> None

Arm the watchdog. Must be called from inside the loop thread.

stop

stop() -> None

Disarm the watchdog and join its thread. Safe to call twice.

WebSocket

WebSocket connection handler.

Usage::

from veloce import Veloce, WebSocket

app = Veloce()

@app.websocket("/ws")
async def chat(ws: WebSocket):
    async with ws:
        await ws.accept()
        async for message in ws.iter_text():
            await ws.send_text(message)

Using async with ws: closes the connection on a clean exit with a normal-closure 1000. If the block exits via an exception, __aexit__ leaves the close to the dispatcher's error handling, which sends the mapped close code (e.g. 1008 for a policy violation, 1011 for an unhandled error) before the exception propagates.

Pass idle_timeout=<seconds> (default None -> disabled) to bound how long a blocking receive (receive/receive_text/ receive_bytes/receive_json and the iter_* loops) waits for the next message. When no message arrives within idle_timeout seconds the connection performs a clean RFC 6455 close with 1001 Going Away and the receive raises WebSocketDisconnect, so the handler loop unwinds exactly as it would on a peer-initiated close. A per-call timeout still applies; whichever deadline is smaller wins. Set it at construction via from_asgi(idle_timeout=...) or from inside the handler with set_idle_timeout. The window bounds each complete message (in production, ASGI delivers complete messages and owns ping/pong; the raw-transport path measures it the same way).

Pass heartbeat=<seconds> (raw-transport mode only, default None -> disabled) to proactively probe a silent peer. After accept() a timer sends an application PING carrying a token every heartbeat seconds; the peer must answer with a PONG (or send any other frame) before the next tick, otherwise the connection is dropped with a 1006 close code recorded on ws.close_code. Any inbound byte defers the next probe, so busy connections send no needless pings. In ASGI mode the server owns ping/pong, so the value is accepted for API symmetry but never starts a timer.

query_params property

query_params: Any

Parsed query string of the WebSocket handshake URL.

Read it as ws.query_params["token"]. Backed by QueryParams (multi-value, getlist-aware). Empty when the scope carries no query_string.

url property

url: str

The WebSocket handshake URL path - ASGI-style shape.

Returns path plus ?query when a query string is present.

client property

client: Any

The connecting peer as an Address(host, port).

Reads scope["client"] (the ASGI (host, port) pair). Returns None when the scope carries no client info.

state property

state: Any

Per-connection scratch namespace.

Lazily-created State (a dict subclass) supporting both ws.state.user = ... attribute access and ws.state["user"].

cookies property

cookies: dict[str, str]

Cookies sent with the WebSocket handshake.

Parses the handshake Cookie header into {name: value}. Empty when no cookie header was present.

application_state property

application_state: WebSocketState

Server-side state of the WebSocket.

  • CONNECTING before the app sends accept().
  • CONNECTED after accept() and until close() is sent.
  • DISCONNECTED after close() (locally) or after the peer half-closes (observed via WebSocketDisconnect).

client_state property

client_state: WebSocketState

Client-side state of the WebSocket.

Veloce does not distinguish the two halves at the protocol level beyond the close flag, so this mirrors application_state once the peer disconnects and otherwise stays CONNECTED once the handshake completes.

origin property

origin: str | None

The client-supplied Origin header, or None if absent.

WebSocket handshakes carry Origin per RFC 6455 Sec. 10.2 / Sec. 4.1. Browsers always send it; non-browser clients may omit it. The header is the application's primary defence against Cross-Site WebSocket Hijacking - CSWSH bypasses CORS because the handshake is plain HTTP/1.1 and Same-Origin Policy does not apply to it. Pair this accessor with check_origin(allowed) before accept().

requested_subprotocols property

requested_subprotocols: list[str]

Subprotocols the client offered in Sec-WebSocket-Protocol.

Returns them in client preference order (RFC 6455 Sec. 1.9). Empty list when the header is absent. Whitespace around each token is stripped; the comparison the negotiator performs is case-sensitive per the spec.

from_asgi classmethod

from_asgi(scope: dict, receive: Any, send: Any, idle_timeout: float | None = None, heartbeat: float | None = None) -> WebSocket

Construct an ASGI-driven WebSocket (no asyncio.Transport).

Used by Veloce.__call__ for scope["type"] == "websocket". Headers come from scope["headers"] (list of (bytes, bytes)), decoded latin-1 per ASGI. accept/send_*/receive_*/close all dispatch through send/receive instead of the raw frame writer used by the asyncio.Transport mode.

idle_timeout (default None -> disabled) bounds how long a blocking receive waits for the next frame before performing a clean 1001 Going Away close; see the class docstring. heartbeat is accepted for signature symmetry with the raw-transport constructor but is inert here - the ASGI server owns ping/pong.

from_transport classmethod

from_transport(transport: Transport, headers: dict[str, str], scope: dict, *, path_params: dict[str, Any] | None = None, idle_timeout: float | None = None, recv_queue_maxsize: int | None = None) -> WebSocket

Construct a raw-transport WebSocket whose 101 was already sent.

Used by the native HttpProtocol upgrade path. The protocol writes the RFC 6455 Sec. 4.2.2 101 response synchronously (to switch the byte stream) before building this object, so _handshake_sent is set: a later accept() validates state but does not emit a second handshake. The connection is otherwise a normal raw-mode WebSocket - transport is set, _asgi_send stays None (so _is_asgi is False), and inbound bytes flow through feed_data/_parse_frame exactly as for a directly constructed instance.

headers are the lowercased handshake headers (latin-1 decoded by the protocol). scope mirrors the ASGI websocket scope shape so the same path/query_params/client/cookies accessors work unchanged.

check_origin

check_origin(allowed: str | Iterable[str]) -> bool

Return True when the handshake's Origin is in allowed.

Pass a single origin string or an iterable of allowed origins (e.g. ["https://app.example.com", "https://admin.example.com"]). Normalisation matches WebSocketOriginMiddleware: each side is lowercased and has any trailing slash stripped, so allow-lists written for one API are interchangeable with the other.

  • Wildcard. "*" in allowed accepts any origin and is the opt-in "I have my own check elsewhere" escape hatch - the symmetric behaviour to WebSocketOriginMiddleware's allowed_origins=["*"].
  • Missing Origin (no header at all, or a literal Origin: null from a sandboxed iframe / file:// page) is a non-match and returns False. Non-browser clients legitimately omit the header - if you want to allow them, branch on ws.origin is None explicitly. The WebSocketOriginMiddleware middleware path also offers an allow_missing=True switch; this in-handler helper is deliberately strict-by-default.
Usage

@app.websocket("/ws") async def chat(ws: WebSocket): if not ws.check_origin("https://app.example.com"): await ws.close(code=WS_1008_POLICY_VIOLATION) # policy violation return await ws.accept() ...

For the middleware-style check (registered once, runs before the handler) reach for veloce.SecurityHeadersMiddleware's sibling WebSocketOriginMiddleware.

negotiate_subprotocol

negotiate_subprotocol(supported: list[str]) -> str | None

Pick the first client-offered subprotocol that the server supports.

Per RFC 6455 Sec. 4.1, the server picks ONE protocol from the client's list. Most servers prefer to honour the client's preference order (first match wins), which is what we do.

accept async

accept(subprotocol: str | None = None, headers: dict[str, str] | None = None) -> None

Complete the WebSocket handshake.

Raises:

Type Description
RuntimeError

if the connection is already accepted or already closed, or if a subprotocol/headers argument is passed on the native (Veloce.run) upgrade path, where the 101 response has already been sent and cannot be renegotiated.

send_text async

send_text(data: str) -> None

Send a text frame.

send_json async

send_json(data: Any, mode: str = 'text') -> None

Send JSON data.

mode="text" (default) wraps the JSON in a text frame (opcode 0x1). mode="binary" sends the raw JSON bytes as a binary frame (0x2).

send_bytes async

send_bytes(data: bytes) -> None

Send a binary frame.

receive async

receive() -> dict

Receive a raw ASGI WebSocket message.

Returns the message dict as the ASGI server delivered it ({"type": "websocket.receive", "text"/"bytes": ...}). A websocket.disconnect message raises WebSocketDisconnect. ASGI-mode only - raw asyncio-transport connections don't carry ASGI message envelopes.

The same handshake state machine the typed receive_* helpers enforce: the raw escape hatch must not be a way around receive-before-accept or receive-after-close (which would consume the websocket.connect envelope and corrupt the next accept()).

send async

send(message: dict) -> None

Send a raw ASGI WebSocket message.

message is forwarded straight to the ASGI send callable, e.g. {"type": "websocket.send", "text": "..."}.

set_send_drain

set_send_drain(drain: Any) -> None

Install the native write-side backpressure hook (raw transport only).

drain is an awaitable-returning callable (HttpProtocol.drain) that blocks while the transport's outgoing buffer is over its high-water mark. The async send_* wrappers await it before writing each frame, so a slow-reading client suspends the producing handler instead of letting the transport buffer grow without bound. The native upgrade path (HttpProtocol) calls this once; ASGI mode leaves it unset.

set_idle_timeout

set_idle_timeout(idle_timeout: float | None) -> None

Set the idle-receive timeout in seconds (None disables it).

Applies to every subsequent blocking receive on this connection. Call it inside the handler (typically right after accept()) to enable or adjust the window; passing idle_timeout= to WebSocket.from_asgi sets the same value at construction.

receive_text async

receive_text(timeout: float | None = None) -> str

Receive a text message. Raises asyncio.TimeoutError if timeout exceeded.

When idle_timeout is configured, a wait longer than the idle window closes the connection with 1001 Going Away and raises WebSocketDisconnect instead of asyncio.TimeoutError.

receive_json async

receive_json(timeout: float | None = None) -> Any

Receive and parse JSON.

receive_bytes async

receive_bytes(timeout: float | None = None) -> bytes

Receive binary data. Raises asyncio.TimeoutError if timeout exceeded.

When idle_timeout is configured, a wait longer than the idle window closes the connection with 1001 Going Away and raises WebSocketDisconnect instead of asyncio.TimeoutError.

iter_text async

iter_text() -> Any

Async-iterate over incoming text frames until the peer closes.

Usage

async for msg in ws.iter_text(): ...

Terminates cleanly on WebSocketDisconnect. Other exceptions propagate.

iter_bytes async

iter_bytes() -> Any

Async-iterate over incoming binary frames until the peer closes.

iter_json async

iter_json() -> Any

Async-iterate over incoming JSON-decoded frames until peer closes.

close async

close(code: int = WS_1000_NORMAL_CLOSURE, reason: str = '') -> None

Send a close frame and complete the RFC 6455 close handshake.

Per RFC 6455 Sec. 5.5.1 the close-frame payload is a 2-byte big-endian status code optionally followed by a UTF-8 reason of at most 123 bytes (so the whole payload fits in the 125-byte control-frame budget). Reasons longer than 123 bytes are truncated to a clean UTF-8 boundary.

On the raw-transport path the close is a full handshake (Sec. 5.5.1, Sec. 7.1.1): the close frame is sent, then a server-initiated close waits for the peer's reply close frame (bounded by CLOSE_HANDSHAKE_TIMEOUT) before dropping the TCP connection. A peer-initiated close already carries the peer's frame, so the reply is sent and the transport closed without waiting.

feed_data

feed_data(data: bytes) -> None

Feed raw bytes from the transport (called by the protocol).

The transport delivers byte runs that need not align with frame boundaries: a single frame may be split across two reads, and one read may carry several frames. Bytes are appended to a persistent receive buffer and complete frames are parsed off the front in a loop - partial frames are kept for the next call.

Handles fragmented messages (RFC 6455 Sec. 5.4): a data frame with FIN=0 opens a message that subsequent continuation frames (opcode 0x0) extend, and the FIN=1 continuation completes it. Control frames (close / ping / pong) are never fragmented and may be interleaved within a fragmented message without disturbing the reassembly buffer.

start_heartbeat

start_heartbeat() -> None

Arm the heartbeat timer for a raw-transport connection.

Idempotent and a no-op in ASGI mode or when heartbeat was not configured. accept() calls this automatically once the raw handshake completes, so handlers rarely call it directly; it is public so a handler that builds a WebSocket by hand can start the probe after wiring its own transport.

cached

cached(cache: Cache, *, ttl: int, key: Callable[..., str] | None = None) -> Callable[[Callable[..., Any]], Callable[..., Any]]

Memoise an async function's JSON-serialisable return in cache.

The key defaults to the function's qualified name plus a digest of its arguments (arguments that are not JSON-serialisable, such as an injected Request, are ignored), so a handler is cached by its scalar inputs. Pass key= a callable taking the same arguments for full control.

The result must be JSON-serialisable (a Pydantic model is dumped in JSON mode); a non-serialisable result raises TypeError. A cache hit returns the JSON-decoded value, so cache results you will re-serialise (handler returns, API payloads) rather than rich objects you need back by type. Only async functions are supported.

render_template

render_template(template_name: str | Sequence[str], **context: Any) -> str

Render a named template against the current app.

Pulls the Jinja2Templates instance off current_app._templates (set when the user constructs a Jinja2Templates(templates_dir) and assigns it). Raises RuntimeError outside a request / app context. Returns the rendered string; callers wrap in a Response themselves if they need one.

render_template_string

render_template_string(source: str, **context: Any) -> str

Render an inline string template against the current app.

Builds a transient Jinja2 environment when no Jinja2Templates is bound on the app, so the helper works for one-off templates that don't need a templates directory. Honours app-level filters / globals / tests and context processors when the env is reachable via app._templates.

stream_template

stream_template(template_name: str | Sequence[str], **context: Any) -> Any

Stream a named template against the current app, chunk by chunk.

Mirrors render_template but returns an iterator of str chunks (Jinja's template.generate(...)) instead of a single string, so a large response body is produced lazily. Pulls the Jinja2Templates instance off current_app._templates; raises RuntimeError outside a request / app context. Wrap the result in a StreamingResponse to return it from a handler::

from veloce import StreamingResponse, stream_template

@app.get("/big")
async def big(request):
    return StreamingResponse(stream_template("big.html", rows=rows))

jsonable_encoder

jsonable_encoder(obj: Any, include: set[str] | None = None, exclude: set[str] | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, custom_encoder: dict[type, Callable[[Any], Any]] | None = None, *, _seen: set[int] | None = None) -> Any

Convert complex objects to JSON-serializable types.

Handles Pydantic models, dataclasses, datetime, Decimal, UUID, Enum, Path, sets, frozensets, and nested structures.

include / exclude apply to dict keys at every depth - passing exclude={"password"} strips a password key wherever it appears in the structure, not only at the top level. exclude_none likewise drops None-valued keys from plain dicts at every depth, not only from a top-level model's own fields.

Raises ValueError on a self-referential object graph (a container that transitively contains itself) instead of recursing until the stack overflows. Detection is by id(); the per-call _seen set is internal and should not be passed by callers.

custom_encoder is an optional {type: fn} mapping consulted before every built-in rule at every depth: the exact type(obj) wins, else the entries are scanned in insertion order returning the first isinstance match. Because it runs first it can override container and model handling as well as leaf scalars. Types registered process-wide via register_encoder are consulted later (after the exact-type fast paths) and cover subclasses through an MRO walk.

Usage::

data = jsonable_encoder(my_pydantic_model, exclude={"password"})

register_encoder

register_encoder(type_: type, encoder: Callable[[Any], Any]) -> None

Register a process-level JSON encoder for type_ and its subclasses.

encoder receives one instance and must return a JSON-able value (str/int/float/bool/None or a list/dict of such). It is consulted by jsonable_encoder after the exact-type fast paths, resolved via an MRO walk so subclasses of type_ are covered too. Registering a type that already has a built-in handler overrides that handler for the type and its subclasses.

Usage::

register_encoder(MyId, lambda v: v.hex)

unregister_encoder

unregister_encoder(type_: type) -> None

Remove a previously registered encoder for type_.

No-op if type_ was never registered.

http_exception_handler async

http_exception_handler(request: Any, exc: HTTPException) -> Response

Render an HTTPException as a JSON {"detail": ...} response.

Honours exc.status_code, exc.detail (falling back to the subclass description), and exc.headers.

request_validation_exception_handler async

request_validation_exception_handler(request: Any, exc: RequestValidationError) -> Response

Render a RequestValidationError as a 422 with the error list.

Uses the structured shape {"detail": [ ...per-field errors... ]}.

abort

abort(status_code: int, detail: str = '', headers: dict[str, str] | None = None) -> NoReturn

Raise an HTTPException - a concise shorthand.

Raises the typed subclass for known status codes (e.g. NotFound for 404, Forbidden for 403) so error handlers registered against a specific subclass match. Unknown codes fall back to the bare HTTPException.

Usage::

abort(404)              # -> raises NotFound
abort(403, "Forbidden") # -> raises Forbidden

after_this_request

after_this_request(func: Any) -> Any

Register a one-shot after-request callback.

Fires after the global @app.after_request hooks have run for the current request only - future requests are unaffected. Useful for work that depends on data computed inside the handler (e.g. setting a cookie whose value the handler decided).

Returns the callback unchanged so it can be used as a decorator. Raises RuntimeError when called outside an active request.

async_send_file async

async_send_file(path_or_file: Any, mimetype: str | None = None, as_attachment: bool = False, download_name: str | None = None, last_modified: Any = None, etag: bool | str = True, max_age: int | None = None) -> Response

Serve a file - async variant of send_file.

Identical to send_file but reads the file in an executor via FileResponse.from_path, so it never blocks the event loop. Prefer this from async handlers; the sync send_file emits a DeprecationWarning when called on a running loop.

flash

flash(message: str, category: str = 'message') -> None

Flash a message for the next request - requires SessionMiddleware.

Usage::

flash("Item created successfully")
flash("Invalid input", "error")

get_flashed_messages

get_flashed_messages(with_categories: bool = False, category_filter: Sequence[str] | None = None) -> list

Get flashed messages - call in templates.

Usage::

messages = get_flashed_messages()
messages = get_flashed_messages(with_categories=True)

has_app_context

has_app_context() -> bool

True iff current_app resolves to a real app.

Use this to gate code that reads current_app/app.config so it can also run outside a request (e.g. helper modules imported at module-import time, before any app is bound to the contextvar).

has_request_context

has_request_context() -> bool

True iff a request is bound to this task/context.

Veloce passes the live request through arguments during dispatch, so this only flips True inside app.test_request_context() blocks or when application code explicitly sets the contextvar.

jsonify

jsonify(*args: Any, **kwargs: Any) -> JSONResponse

Create a JSON response - a concise shorthand.

Honours two app-config flags when called inside a request: - JSON_SORT_KEYS (default True) - sort dict keys alphabetically. - JSONIFY_PRETTYPRINT_REGULAR (default False) - indent the output with 2 spaces for readability. Often enabled under DEBUG.

Usage::

return jsonify(name="alice", age=30)
return jsonify({"name": "alice"})
return jsonify([1, 2, 3])

make_response

make_response(body: Any = b'', status_code: int = HTTP_200_OK, headers: dict[str, str] | None = None, content_type: str | None = None) -> Response

Create a Response - a convenience wrapper.

Usage::

resp = make_response("Hello", 200)
resp = make_response({"data": True}, 201)

redirect

redirect(location: str, code: int = HTTP_302_FOUND, headers: dict[str, str] | None = None) -> Response

Build a redirect response helper.

Default code=302 matches the long-standing convention. RFC 9110 Sec. 15.4 catalogue: 301 (permanent, method may change), 302 (found, method may change), 303 (see other, method becomes GET), 307 (temporary, method preserved), 308 (permanent, method preserved). Pick the one that matches your semantics - the helper is a thin wrapper, not a policy. Accepts extra headers (e.g. Vary).

send_file

send_file(path_or_file: Any, mimetype: str | None = None, as_attachment: bool = False, download_name: str | None = None, last_modified: Any = None, etag: bool | str = True, max_age: int | None = None) -> Response

Serve a file top-level helper.

Accepts a filesystem path (str / PathLike) and returns a FileResponse with conditional-GET headers already set (Last-Modified, ETag - both were added by Q40/Q42). Optional knobs:

  • mimetype= overrides the auto-guessed content type.
  • as_attachment=True sets Content-Disposition: attachment; filename=<download_name or basename>.
  • download_name= overrides the filename in Content-Disposition.
  • last_modified= overrides the file's mtime (datetime, unix ts, or pre-formatted IMF-fixdate string).
  • etag=False suppresses the auto-generated ETag; etag="<value>" uses the caller-provided one verbatim (already-quoted).
  • max_age= adds Cache-Control: public, max-age=<n>.

send_from_directory

send_from_directory(directory: str, filename: str, mimetype: str | None = None, as_attachment: bool = False, download_name: str | None = None) -> FileResponse

Send a file from a directory (sync version).

Traversal-safe via safe_join. Returns 403 on any escape attempt.

For async, use send_from_directory_async() instead.

send_from_directory_async async

send_from_directory_async(directory: str, filename: str, mimetype: str | None = None, as_attachment: bool = False, download_name: str | None = None) -> FileResponse

Send a file from a directory - async version, reads file in executor.

Traversal-safe via safe_join.

stream_with_context

stream_with_context(generator: Any) -> Any

Keep the request context alive while a streaming generator runs.

A streaming response body is consumed by the ASGI emit layer after the handler has returned, by which point the request context has been torn down - so a generator that touches request, g, or current_app would fail. Wrap it::

return StreamingResponse(stream_with_context(generate()))

The current request / app / g snapshot is captured now and re-established for the lifetime of the wrapped iteration. Accepts either an async or a synchronous generator/iterable.

config_orjson_options

config_orjson_options(cfg: Any) -> int

Build the orjson option bitmask from an app config mapping.

Reads the JSON_SORT_KEYS and JSONIFY_PRETTYPRINT_REGULAR flags. Shared by DefaultJSONProvider and helpers.jsonify so the two paths cannot drift. Returns 0 when cfg is None.

escape

escape(value: Any) -> Markup

HTML-escape value and wrap in Markup.

Objects that implement __html__() are trusted: their return is wrapped as-is. Otherwise the value is str()-coerced and the five HTML-significant characters are replaced with numeric character references (per WHATWG HTML Sec. 13).

csp_nonce

csp_nonce(request: Request) -> str | None

Return the per-request CSP nonce, materializing it on first access.

Templating helpers and handlers embed this on <script>/<style> tags as nonce="...". Returns None when CSPMiddleware did not arm a nonce for this request.

rotate_csrf_token

rotate_csrf_token(request: Request) -> None

Force the active CSRFMiddleware to mint a fresh token on response.

Call this at the end of an authentication handler (login, logout, permission elevation) so the CSRF cookie issued to the pre-authentication session is replaced by a fresh one bound to the new authentication state. Without rotation an attacker who plants a known CSRF cookie on an anonymous victim can submit forged requests after the victim logs in (session-fixation pathway).

Usage::

@app.post("/login")
async def login(request: Request):
    user = authenticate(...)
    request.session["user_id"] = user.id
    rotate_csrf_token(request)
    return RedirectResponse("/")

No-op when CSRFMiddleware is not installed.

instrument_access_log

instrument_access_log(app: Veloce, *, logger: Logger | None = None, json: bool = False, include_streamed: bool = True) -> Callable[[RequestMetrics], None]

Register the unified access-log hook (text or JSON), route-keyed.

log_requests_as_json

log_requests_as_json(app: Veloce, *, logger: Logger | None = None, level: int = INFO, include_path: bool = False) -> Callable[[RequestMetrics], None]

Register a hook emitting one JSON access-log record per request.

hash_password

hash_password(password: str | bytes, method: str = 'scrypt', salt_length: int = _SALT_BYTES) -> str

Derive a salted verifier for password.

Returns a self-describing string of the form method$params$salt$hash where each segment is URL-safe base64 (no padding). Pass this string verbatim to verify_password later.

method: - "scrypt" (default): RFC 7914, memory-hard. - "pbkdf2:sha256": NIST SP 800-132, CPU-only.

salt_length is the number of random bytes used for the salt; 16 is the OWASP minimum.

hash_password_async async

hash_password_async(password: str | bytes, method: str = 'scrypt', salt_length: int = _SALT_BYTES) -> str

Async-safe wrapper for hash_password - runs the KDF on a thread.

hash_password calls hashlib.scrypt / pbkdf2_hmac synchronously; those are deliberately slow (~100 ms) and would block the event loop if called directly from an async handler. This wrapper offloads the work to the default executor so the loop stays free for other requests. Use this from async def handlers; keep the sync hash_password for sync handlers / scripts / CLI tools.

is_strong_password

is_strong_password(password: str, *, min_length: int = 8) -> bool

Cheap policy check - not exhaustive.

Returns True only when the password meets a minimum baseline: at least min_length characters and contains at least one digit AND one alphabetic character. Callers that want NIST SP 800-63B-style policy (block known-leaked passwords, drop max-length caps, etc.) should layer on top.

needs_rehash

needs_rehash(stored: str) -> bool

Whether stored should be re-derived with the current defaults.

Returns True when the stored verifier was produced with a weaker configuration than hash_password would produce today - either a non-default method, or cost parameters below the current module defaults. An app can call this after a successful verify_password and transparently re-hash the password (the plaintext is in hand at that moment) so credentials drift up to the current work factor on each login without a forced reset.

A malformed or unparseable stored returns False - it is not a rehash candidate (it would not verify in the first place), so the caller's normal verify-failure path handles it.

verify_and_needs_update

verify_and_needs_update(stored: str, candidate: str | bytes) -> tuple[bool, bool]

Verify candidate and report whether stored should be upgraded.

Returns (ok, needs_update): - ok is the same boolean verify_password returns. - needs_update is True only when ok is True AND the stored verifier is weaker than the current defaults (see needs_rehash). It is always False on a failed verify - there is nothing to upgrade for a credential that did not match.

Usage::

ok, upgrade = verify_and_needs_update(user.pw_hash, form_password)
if not ok:
    raise Unauthorized()
if upgrade:
    user.pw_hash = hash_password(form_password)
    db.save(user)

verify_and_needs_update_async async

verify_and_needs_update_async(stored: str, candidate: str | bytes) -> tuple[bool, bool]

Async-safe wrapper for verify_and_needs_update.

Offloads the KDF verify to a thread for the same reason as verify_password_async; needs_rehash is a cheap string parse and runs inline on the worker thread alongside the verify.

verify_password

verify_password(stored: str, candidate: str | bytes) -> bool

Compare candidate against a stored verifier string.

Returns False (never raises) for any malformed stored, unknown method, or mismatch. Uses hmac.compare_digest for the final byte comparison so timing attacks can't leak partial matches.

verify_password_async async

verify_password_async(stored: str, candidate: str | bytes) -> bool

Async-safe wrapper for verify_password - runs the KDF on a thread.

Same rationale as hash_password_async: the scrypt / PBKDF2 verify is ~100 ms of CPU; calling it synchronously from an async handler blocks the event loop. Offload it.

current_principal

current_principal() -> Principal | None

Return the authenticated Principal for the current request, or None.

set_principal

set_principal(principal: Principal | None) -> None

Set the authenticated Principal for the current request.

Call this from whatever authenticates a request - an HTTP auth middleware or dependency, or the MCP transport's token verifier - so downstream code reads one identity through current_principal, regardless of which door the request arrived on.

rate_limit

rate_limit(strategy: RateLimitStrategy) -> Callable[[T_handler], T_handler]

Attach a per-route rate-limit strategy to a handler.

A decorated handler is limited by strategy with its own per-client counter, overriding the RateLimitMiddleware default; undecorated handlers keep the default. Because the limit lives on the handler, there is no route string to mistype. Place it below the route decorator so the route registers the tagged handler.

Usage::

from veloce import TokenBucket, rate_limit

@app.post("/login")
@rate_limit(TokenBucket(rate=5, per=60))
async def login(request): ...

register_converter

register_converter(name: str, converter_cls: type[_Converter]) -> None

Register a custom path converter.

After register_converter("slug", SlugConverter), routes may use {post:slug} and the radix tree validates/coerces the segment via SlugConverter().match(...). converter_cls must subclass Converter (= _Converter). A built-in name cannot be shadowed - that raises ValueError.

constant_time_compare

constant_time_compare(a: str | bytes, b: str | bytes) -> bool

Compare two secrets without leaking their contents through timing.

Wraps hmac.compare_digest; str inputs are UTF-8 encoded first. Use this when the operands may be str (or mixed str/bytes). Callers that already hold two equal-typed bytes values - the signing, JWT, reset-token, password, and Secret verify paths - call hmac.compare_digest directly: routing them through here would add an isinstance ladder and a redundant encode/copy on a security-hot verify path for no behavioural gain (the False-on-type-mismatch branch is unreachable when both operands are statically bytes).

safe_join

safe_join(directory: str, *paths: str) -> str | None

Join paths onto directory, returning None on any escape.

Returns the absolute joined path if it equals directory or is a descendant. Returns None if: - any component in paths is an absolute path, - any component contains a NUL byte, - on Windows, any segment names a reserved device (COM1, NUL, ...), - the resolved path is outside directory.

The check is performed via os.path.abspath, which collapses .. segments before comparison. Symlinks are not resolved - callers that distrust symlinks must use os.path.realpath themselves.

secure_filename

secure_filename(name: str) -> str

Return a safe basename for name.

  • Strips directory separators (/, \) and any non-ASCII characters.
  • Replaces unsafe characters with underscores; collapses repeats.
  • Strips leading/trailing dots/spaces/underscores (blocks . and ..).
  • Prefixes Windows reserved names (CON, PRN, ...) with _.
  • Returns "" when nothing survives sanitisation.

Empty or whitespace-only input returns "". The caller is responsible for treating that as a rejection - secure_filename will not raise.

check_reset_token

check_reset_token(token: str, state: bytes, *, secret: str | bytes, max_age: int, fallback_secrets: Sequence[str | bytes] = (), salt: str | bytes = RESET_TOKEN_SALT) -> bool

Return True iff the token is authentic, unexpired, and still bound to state.

decode_jwt

decode_jwt(token: str, secret: str | bytes, *, algorithms: Sequence[str], audience: str | Sequence[str] | None = None, issuer: str | None = None, require: Sequence[str] = (), leeway: float = 0, now: float | None = None) -> Claims

Verify a compact JWS token and return its claims as a read-only mapping.

encode_jwt

encode_jwt(claims: Mapping[str, Any], secret: str | bytes, *, alg: str = 'HS256') -> str

Sign claims into a compact JWS token using the given HMAC algorithm.

make_reset_token

make_reset_token(state: bytes, *, secret: str | bytes, salt: str | bytes = RESET_TOKEN_SALT) -> str

Bind a caller-supplied state fingerprint into a signed reset token.

Routing internals

Introspection types exposed from veloce.routing for advanced use (custom dispatch, route inspection). They are not part of the top-level namespace.

RouteInfo

Stored route metadata.

RouteMatch

Result of matching a path against the tree.

HTTP data structures

Additional parsed-header and value containers exposed from veloce.http.

QueryParams

Bases: _GetListMixin, MultiDict

Multi-value, case-sensitive query parameter collection.

Backed by multidict.MultiDict. Repeated query keys (?x=1&x=2) preserve every value; getlist("x") returns ["1", "2"] while params["x"] returns "1" (the first).

getlist

getlist(key: str) -> list[str]

Return all values for the given key as a list. Empty list if absent.

from_query_string classmethod

from_query_string(query_string: str) -> QueryParams

Parse a=1&b=2&a=3 into a multi-value mapping.

Keeps blank values (a=) and decodes percent-escapes. The ordering of repeated keys reflects the order in the URL.

CacheControl

Parsed view of a Cache-Control header.

to_header

to_header() -> str

Serialise back to a Cache-Control header value.

Bool-True directives emit just the directive name; numeric and string directives emit name=value. Preserves source-observed order; user-set directives append in set order.

HeaderSet

Ordered, case-insensitive set of header tokens.

Used for headers like Vary and Allow that carry a comma-joined token list:

  • __contains__ is case-insensitive.
  • add / discard / remove mutate in place.
  • to_header() round-trips to a comma-separated header value.
  • Iteration yields items in insertion order.

add

add(header: str) -> None

Add header to the set. No-op if already present (case-insensitive).

discard

discard(header: str) -> None

Remove header if present. Never raises (set semantics).

remove

remove(header: str) -> None

Remove header; raise KeyError if absent.

to_header

to_header() -> str

Serialise as a comma-separated header value (insertion order).

parse_multipart_form

parse_multipart_form(body: bytes, content_type: str, *, max_parts: int = DEFAULT_MAX_MULTIPART_PARTS, max_files: int | None = None, max_fields: int | None = None, max_part_size: int = DEFAULT_MAX_MULTIPART_PART_SIZE, max_file_size: int | None = None, max_field_size: int | None = None, max_field_memory: int | None = None, charset_fallback: str | None = None) -> FormData

Parse multipart/form-data into FormData with UploadFile support.

max_parts caps the total number of parts. max_files and max_fields, when set, additionally cap file parts and text-field parts independently, so a form may allow many small fields while permitting only a few uploads (or vice versa).

max_part_size caps each part's body size. max_file_size and max_field_size, when set, override it for file parts and text fields respectively, expressing the common "small fields, large files" policy. max_field_memory, when set, caps the cumulative resident bytes of all text fields (value bytes plus field-name bytes), a ceiling that max_field_size alone cannot express.

Exceeding any limit raises RequestEntityTooLarge (413), so a maliciously structured form cannot exhaust memory or CPU even when its total size is within MAX_CONTENT_LENGTH.

A missing boundary parameter, or one violating the RFC 2046 boundary grammar, raises BadRequest (400) rather than silently yielding an empty form.

charset_fallback controls how non-UTF-8 field bytes are handled when a part declares no charset of its own. The default (None) rejects them with BadRequest (400). Pass "replace" to substitute U+FFFD (the pre-0.1.4 behaviour) or "latin-1" to decode as ISO-8859-1 for legacy clients. A part that declares its own Content-Type charset (RFC 7578 §5.1.2) is decoded with that charset instead, provided it is one of ascii, us-ascii, utf-8, or iso-8859-1. A declared charset is decoded strictly: bytes that are invalid in it raise BadRequest (400) rather than being corrupted with U+FFFD, since the part asserted its own encoding.

header_key

header_key(headers: Mapping[str, str], name: str) -> str | None

Return the actual stored key matching name case-insensitively, or None.

name should be passed in its canonical casing; the common case (the header is stored under that exact key) returns without scanning. Use the returned key to rewrite a value in place under whatever casing the caller originally stored.

header_get

header_get(headers: Mapping[str, str], name: str) -> str | None

Return the value stored under name case-insensitively, or None.

header_present

header_present(headers: Mapping[str, str], name: str) -> bool

Return True when a header named name exists under any casing.

OpenAPI helpers

Lower-level OpenAPI helpers used by app.openapi() / the docs routes, exposed from veloce.contrib.openapi for callers that build the schema directly.

get_openapi_schema

get_openapi_schema(app: Any) -> dict

Generate OpenAPI 3.1 schema from the app's registered routes.

setup_openapi_routes

setup_openapi_routes(app: Any, openapi_url: str = '/openapi.json', docs_url: str | None = '/docs', redoc_url: str | None = '/redoc') -> None

Register OpenAPI schema and documentation routes.

docs_url / redoc_url of None disable the Swagger UI / ReDoc UI respectively - the JSON schema route is still registered, so tooling can consume the schema without a public interactive explorer.

MCP — advanced API

The Model Context Protocol server, tool/resource/prompt registries, and transports exposed from veloce.contrib.mcp. Most applications drive MCP through app.mount_mcp(...), @app.mcp_tool, and @app.mcp_prompt; these names are for callers that assemble or serve the registry themselves.

MCPServer

Bases: TasksMixin, InvocationMixin

Serve a Veloce app's MCP tools over JSON-RPC 2.0.

Build once with the app; the registry is assembled eagerly so a registration-time safety violation (missing description, duplicate name) surfaces before any client connects.

set_notifier staticmethod

set_notifier(notifier: Callable[[dict[str, Any]], Awaitable[None]]) -> None

Wire the current context's outbound one-way notification sink.

Sets the per-request _notifier_var; the stdio transport calls this once in its serve task, while the Streamable HTTP transport sets the var per request so concurrent calls never cross notifications.

set_requester staticmethod

set_requester(requester: Callable[[str, dict[str, Any]], Awaitable[dict[str, Any]]]) -> None

Wire the current context's server->client request issuer.

Sets the per-context _requester_var; a bidirectional transport (the stdio loop) calls this once in its serve task so a tool's MCPContext.sample / elicit / roots reaches the client. A one-way transport never calls it, leaving those methods to raise.

current_session staticmethod

current_session() -> MCPSession | None

Return the session of the connection currently dispatching, or None.

Set per dispatch by handle_message when a stateful transport supplies a session; None on the stateless HTTP path or off-dispatch.

register_connection

register_connection(session: MCPSession, sink: Callable[[dict[str, Any]], Awaitable[None]]) -> object | None

Record an open stateful connection so it can receive resource updates.

Returns an opaque token a transport passes back to unregister_connection to drop exactly this stream, so concurrent streams on one session are tracked independently. A no-op returning None when subscriptions are disabled, so a transport may call this unconditionally.

unregister_connection

unregister_connection(token: object | None) -> None

Drop the connection named by its token (a no-op when token is None).

evict_session

evict_session(session: MCPSession) -> None

Reclaim everything an evicted session owns: its connection and tasks.

Called when a session's transport drops it (idle TTL on HTTP). Beyond unregistering the subscription connection, this cancels and drops the session's tasks - including a never-settling one TTL eviction would leave in place - so an abandoned session cannot pin a task for the process lifetime.

notify_resource_updated async

notify_resource_updated(uri: str) -> None

Tell subscribed clients a resource changed (notifications/resources/updated).

Call this from the app when a resource's data changes; the server fans the notification out to every connection subscribed to uri. A no-op when subscriptions are disabled or no connection subscribed to uri.

notify_resources_list_changed async

notify_resources_list_changed() -> None

Tell clients the resource list changed (notifications/resources/list_changed).

Call this from the app when the set of available resources changes; the server fans the notification out to every open connection. A no-op when subscriptions are disabled.

handle_message async

handle_message(message: dict[str, Any], session: MCPSession | None = None) -> dict[str, Any] | None

Dispatch one decoded JSON-RPC request; return the response object.

Returns None for a notification (a request with no id), which carries no response per JSON-RPC 2.0 Sec. 4.1.

A stateful transport (the serial stdio loop) passes its session so the server records the client's advertised capabilities from initialize and enforces the lifecycle ordering: before initialize completes the only requests answered are initialize and ping. The stateless HTTP transport passes none, leaving its fast path unaffected.

MCPTool dataclass

Bases: MCPDescriptor

One registered MCP tool.

MCPResource dataclass

Bases: MCPDescriptor

One registered MCP resource (a read-only route addressed by URI).

MCPPrompt dataclass

Bases: MCPDescriptor

One registered MCP prompt template.

MCPTask dataclass

Bases: MCPDescriptor

One in-flight or settled task created from a task-augmented tool call.

name (inherited) is the task id the client polls by. The task records its lifecycle status, the tool it runs, its created / last-updated timestamps and time-to-live, and - once it settles - the tools/call result the client retrieves with tasks/result.

describe

describe() -> dict[str, Any]

Shape this task into the MCP Task object the task methods return.

is_terminal

is_terminal() -> bool

Return whether the task has settled (no further transition happens).

settle

settle(status: str, result: dict[str, Any], message: str | None = None) -> None

Move the task to a terminal status carrying its final result.

A no-op once the task is already terminal: this guards against a racing tasks/cancel and the natural completion of _run_task both settling the same task, so the first terminal status (e.g. cancelled) is never overwritten by the second.

ToolRegistry dataclass

Bases: Registry[MCPTool]

Name -> MCPTool, plus the shared JSON Schema component registry.

schemas holds Pydantic-model components shared across tool input schemas (mirroring OpenAPI components.schemas); a tool input schema references them by $ref.

register

register(item: T) -> None

Add item, rejecting a key already present with a primitive-specific error.

get

get(name: str) -> T | None

Return the primitive registered under name, or None.

add

add(tool: MCPTool) -> None

Register tool, rejecting a name already taken.

ResourceRegistry dataclass

Bases: Registry[MCPResource]

URI -> MCPResource, plus the shared JSON Schema component registry.

register

register(item: T) -> None

Add item, rejecting a key already present with a primitive-specific error.

get

get(name: str) -> T | None

Return the primitive registered under name, or None.

add

add(resource: MCPResource) -> None

Register resource, rejecting a URI already taken.

statics

statics() -> list[MCPResource]

Return the concrete-URI resources (for resources/list).

templates

templates() -> list[MCPResource]

Return the URI-template resources (for resources/templates/list).

match

match(uri: str) -> tuple[MCPResource, dict[str, str]] | None

Resolve a concrete URI to its resource and extracted path parameters.

A static resource matches by exact URI (no parameters); a template resource matches by its compiled pattern, yielding the path-parameter values to invoke the route with. Static resources are tried first so a concrete URI never falls through to a template that would also match it.

PromptRegistry dataclass

Bases: Registry[MCPPrompt]

Name -> MCPPrompt, plus the shared JSON Schema component registry.

register

register(item: T) -> None

Add item, rejecting a key already present with a primitive-specific error.

get

get(name: str) -> T | None

Return the primitive registered under name, or None.

add

add(prompt: MCPPrompt) -> None

Register prompt, rejecting a name already taken.

TaskRegistry dataclass

Bases: Registry[MCPTask]

Task id -> MCPTask, the in-memory store of created tasks.

register

register(item: T) -> None

Add item, rejecting a key already present with a primitive-specific error.

get

get(name: str) -> T | None

Return the primitive registered under name, or None.

evict_expired

evict_expired() -> None

Drop tasks whose time-to-live has elapsed so a stale task does not leak.

Only a settled task is evicted on expiry; a still-working task is left in place even past its ttl so its eventual result is never discarded out from under a client that is still polling.

owned_by

owned_by(owner_key: int | None) -> list[MCPTask]

Return the tasks created by the given connection.

Used to reclaim a session's tasks when it is evicted so a never-settling task does not pin memory for the process lifetime after its owner is gone.

drop

drop(task: MCPTask) -> None

Remove a task from the store, settled or not (used on owner eviction).

TasksCapability

Bases: _ServerCapability

The tasks/get|result|list|cancel methods and the tasks advertisement.

Advertised only when at least one tool opts into task support, so a server whose tools all run synchronously stays inert and a client never probes an empty capability.

SubscriptionsCapability

Bases: _ServerCapability

The resources/subscribe / resources/unsubscribe methods, opt-in.

Folded into the resource area but kept a separate capability so the base ResourcesCapability stays unchanged when subscriptions are off. It contributes no initialize entry of its own — the resource advertisement (the subscribe/listChanged sub-capability flags) lives on ResourcesCapability, which reads the same opt-in flag — so advertise returns None.

build_registry

build_registry(app: Any) -> ToolRegistry

Assemble the tool registry from explicit tools plus exposed routes.

build_resource_registry

build_resource_registry(app: Any) -> ResourceRegistry

Assemble the resource registry from routes flagged expose_as_mcp_resource.

Mirrors the tool registry walk: every route is visited (including those hidden from the OpenAPI schema), WebSocket routes are skipped, and a multi-verb route is deduplicated by RouteInfo identity so it is exposed once with its full verb set.

build_prompt_registry

build_prompt_registry(app: Any) -> PromptRegistry

Assemble the prompt registry from @app.mcp_prompt registrations.

register_http_transport

register_http_transport(app: Any, server: MCPServer, path: str = '/mcp', auth: MCPAuth | None = None, allowed_origins: frozenset[str] | None = None, exclude_middleware: Sequence[str] | None = None, sessions: bool = False, resumable: bool = False) -> None

Mount the Streamable HTTP transport for server at path on app.

When auth is given the endpoint becomes an OAuth 2.1 resource server: each request is authenticated before dispatch, and the RFC 9728 protected-resource metadata is served so a client can discover the authorization server. allowed_origins enables Origin validation (DNS-rebinding defense). exclude_middleware names app middleware the transport routes opt out of - typically an app-wide auth middleware the transport's own auth replaces.

sessions opts into Mcp-Session-Id lifecycle: the server assigns a session id on the initialize result, requires it on every later request (HTTP 400 if missing, 404 once terminated), and accepts a DELETE to terminate it. The default keeps the stateless behavior with no per-request session bookkeeping.

resumable opts into SSE resumability: each streamed event carrying a payload gets an id encoding its originating stream, the events are kept in a bounded SSEEventStore, and a GET carrying Last-Event-ID replays only that stream's missed events. The default keeps no event ids or history and answers a GET 405.

StdioTransport

Drive an MCPServer over a line-delimited JSON byte stream.

Satisfies BidirectionalTransport: send writes one outbound JSON-RPC message line (wired as the server's notification sink) and request issues a server->client request, awaiting the client's correlated reply read by the serve loop.

serve async

serve() -> None

Read, dispatch, and reply line-by-line until the input closes.

A blank line is skipped; an unparseable line yields a JSON-RPC parse error; a notification (no response) writes nothing; a reply to a pending server->client request resolves it instead of dispatching. The loop ends when read_line returns None (EOF).

Framing is newline-delimited per the MCP stdio transport spec ("messages are delimited by newlines, and MUST NOT contain embedded newlines"). This is deliberate and correct: the MCP stdio transport does NOT use LSP-style Content-Length: header framing - that belongs to the Language Server Protocol, not MCP. One JSON line in, one JSON line out. Do not "fix" this into header framing.

send async

send(message: dict[str, Any]) -> None

Write one server-initiated JSON-RPC message line to the client.

request async

request(method: str, params: dict[str, Any]) -> dict[str, Any]

Issue a server->client request and await the client's correlated reply.

Sends the JSON-RPC request, then reads inbound lines until the matching reply arrives: any other inbound message is dispatched and answered inline, so the client may interleave its own calls while the request is in flight. Returns the reply's result; an error reply raises MCPRequestError.

Refused from a detached task runner: the stdio reader is serial, so this method reads the reply itself, which works only while the serve loop is parked in the calling handler. A task-augmented call has already returned its CreateTaskResult, so the serve loop is reading stdin too; two readers on one blocking stream would split inbound lines arbitrarily.

serve_stdio async

serve_stdio(server: MCPServer) -> None

Serve server over the real process stdin / stdout.

Blocking stdin reads are offloaded to the default thread executor so the event loop stays responsive; stdout writes are flushed per line so a client reading the pipe sees each response immediately.

MCPError

Bases: Exception

Base for any MCP failure that maps to a JSON-RPC error object.

to_error

to_error(msg_id: Any) -> dict[str, Any]

Render this error as a JSON-RPC 2.0 error response.

InvalidRequestError

Bases: MCPError

A malformed JSON-RPC request object.

to_error

to_error(msg_id: Any) -> dict[str, Any]

Render this error as a JSON-RPC 2.0 error response.

MethodNotFoundError

Bases: MCPError

A request named a method this server does not implement.

to_error

to_error(msg_id: Any) -> dict[str, Any]

Render this error as a JSON-RPC 2.0 error response.

InvalidParamsError

Bases: MCPError

A call's params are missing, mistyped, or fail validation.

to_error

to_error(msg_id: Any) -> dict[str, Any]

Render this error as a JSON-RPC 2.0 error response.

InternalError

Bases: MCPError

An unexpected server-side failure handling the request.

to_error

to_error(msg_id: Any) -> dict[str, Any]

Render this error as a JSON-RPC 2.0 error response.

ResourceNotFoundError

Bases: MCPError

A resources/read named a URI the server cannot resolve.

to_error

to_error(msg_id: Any) -> dict[str, Any]

Render this error as a JSON-RPC 2.0 error response.

AuthorizationError

Bases: MCPError

The principal lacks a required scope — reported as a forbidden error.

Carries the required scopes so the HTTP transport can surface them in an RFC 6750 WWW-Authenticate insufficient-scope challenge.

to_error

to_error(msg_id: Any) -> dict[str, Any]

Render this error as a JSON-RPC 2.0 error response.