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
¶
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
¶
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
¶
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.
json
property
writable
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
Return the per-blueprint after-request hook registry.
teardown_request_funcs
property
¶
Return the per-blueprint teardown-request hook registry.
blueprints
property
¶
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
¶
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
¶
View of registered URL-default callbacks.
dependency_overrides
property
writable
¶
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.
query
¶
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
¶
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
¶
Register a template context processor. The function should return a dict that merges into the template context.
template_filter
¶
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
¶
Register a callable as a Jinja global - accessible from any
template by name. Same shape as template_filter.
add_template_global
¶
Imperative equivalent of @template_global.
template_test
¶
Register a Jinja test - used in {% if x is name %} constructs.
add_template_filter
¶
Imperative equivalent of @template_filter.
add_template_test
¶
Imperative equivalent of @template_test.
get_spawned_task
¶
Return the named spawned task, or None if there is no such task.
cancel_spawned_task
¶
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
¶
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
¶
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
¶
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
¶
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 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 to the pipeline.
Call forms:
add_middleware(VeloceMiddlewareClass, **options)- a class subclassingMiddlewareis instantiated with**optionsand appended to the request/response pipeline.add_middleware(instance)- append an already-builtMiddlewareinstance directly.add_middleware(ASGIMiddlewareClass, **options)- a class that is not aMiddlewaresubclass is treated as a standard ASGI middleware: it wraps the whole application and is instantiated asASGIMiddlewareClass(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
¶
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
¶
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
¶
Register a function to run before each request.
before_first_request
¶
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
¶
Register a function to run after each request.
teardown_request
¶
Register a function to run after request teardown. Called with an optional exception argument, even if an exception occurred.
teardown_appcontext
¶
Register a function to run on app-context teardown.
on_event
¶
Register startup/shutdown event handlers.
Deprecated: use @app.on_startup / @app.on_shutdown instead.
Scheduled for removal in v1.0.0.
add_event_handler
¶
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
¶
Register a coroutine to run once at app startup.
after_serving
¶
Register a coroutine to run once at app shutdown.
lifespan_context
¶
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 an error handler without a decorator.
exception_handler
¶
Register a custom exception handler by exception type or status code.
add_exception_handler
¶
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 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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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.
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
¶
Wrap func so it is callable from synchronous code.
- If
funcis a regular function, returns it unchanged. - If
funcis 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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
Resolve a URL path by endpoint name and parameters.
url_defaults
¶
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
¶
Mount a Blueprint's routes + hooks onto this app.
- Re-registers each route under
(url_prefix or bp.url_prefix) + pathso the same blueprint can be mounted twice (e.g. v1/v2 versions). - Splices the blueprint's
before_request/after_request/teardown_requesthooks into the app's own lists. Blueprint hooks fire only for blueprint-routed requests (gated viarequest.endpointstarting 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
¶
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
¶
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.
BackgroundTasks
¶
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 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.
query
¶
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
¶
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
¶
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. Usesapp.config["SERVER_NAME"]when set, otherwise falls back tolocalhost. Caller should override_scheme/_hostfor 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
¶
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
¶
Register a function to run after each blueprint request.
teardown_request
¶
Run after blueprint-routed request teardown, with optional exc.
errorhandler
¶
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
¶
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
¶
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.
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)
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
Read a filename from os.environ[varname] and from_pyfile it.
from_prefixed_env
¶
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
¶
Whether the client has sent notifications/cancelled for this call.
log
async
¶
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 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
¶
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"))
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 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
¶
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 a template from string.
render_async
async
¶
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 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()
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.
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.
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.
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
Return all values for the given key as a list. Empty list if absent.
to_wsgi_list
¶
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.
add
¶
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).
unitis the range unit, e.g."bytes"(the only commonly-used one).rangesis a list of(start, end)tuples, withNonestanding in for an open endpoint:0-499->(0, 499)1000-->(1000, None)(open at the right)-500->(None, 500)(suffix-range - last 500 bytes)
UploadFile
¶
Uploaded file with an async read/write interface.
content
property
¶
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
¶
Stream this upload into destination.
destinationis 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_sizecontrols 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.
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.
view_args
property
¶
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.
referrer
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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.
mimetype
property
¶
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
¶
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
¶
Return the Content-Length as an integer, or None.
content_encoding
property
¶
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
¶
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
¶
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
¶
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_form
property
¶
True when the body is application/x-www-form-urlencoded
or multipart/form-data.
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).
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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).
full_path
property
¶
Path + ? + query string. Always contains a ? even when the
query string is empty.
url_root
property
¶
Root URL of the request: scheme://host/ (with trailing slash,
no path or query string).
scheme
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
property
¶
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
¶
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
¶
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
¶
Per-request scratch namespace - ASGI shape.
Supports attribute access (request.state.user = ...) and
dict access (request.state["user"], request.state.get(...)).
session
property
¶
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
¶
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
¶
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
¶
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
¶
Parse the request body as JSON.
force=Trueskips theis_jsoncontent-type check; useful when the client sends JSON without settingContent-Type(e.g. some XHR libraries). Default is to honour the content type and returnNonefor non-JSON requests.silent=Trueswallowsorjson.JSONDecodeErrorand returnsNone. Default raises so caller code can distinguish malformed JSON from missing JSON.cache=Falseforces a re-parse on every call. Default caches the parsed value (one parse per request); cache invalidation is the caller's job whencache=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
¶
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
¶
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
¶
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
¶
Return the raw request body, draining the source once.
Async to match body() / json() / form().
as_text=Truedecodes via theContent-Typecharset (default UTF-8). Falls back tolatin-1when the declared charset is unrecognised - a defensive fallback, since latin-1 round-trips arbitrary bytes without raising.cache=Trueis a no-op today (veloce already buffers the whole body on construction) but keeps the parameter so callers that passcache=Falsefor streaming compatibility don't break. Streaming-body support arrives separately.
Returns bytes (default) or str (with as_text=True).
files
async
¶
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
¶
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.
is_disconnected
async
¶
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
¶
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.
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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.
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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)
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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.
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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.
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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.
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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")
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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")
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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 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.
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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
¶
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
¶
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).
response
¶
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))
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())
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,
)
)
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
¶
Resolved exclusion name - the instance/class name or class name.
CSRFMiddleware
¶
Bases: Middleware
Double-submit-cookie CSRF middleware.
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))
HTTPSRedirectMiddleware
¶
Bases: Middleware
Redirect HTTP requests to HTTPS.
Resolves the request scheme in this order
- ASGI scope
"scheme"if set to"https"/"wss"(the server already terminated TLS). X-Forwarded-Protoheader (when aProxyFix-style middleware ran upstream this is already the trusted value).- 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.
LoggingMiddleware
¶
Bases: Middleware
Structured request/response access logging.
Usage::
app.add_middleware(LoggingMiddleware())
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.
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))
RateLimitMiddleware
¶
Bases: Middleware
Per-client rate limiter with a selectable algorithm and backend.
Two ways to configure it:
- The default
max_requestsperwindow_secondsruns a process-local sliding-log limiter - simple, zero-dependency, intended for a single worker. Counters are NOT shared across workers, souvicorn --workers Nsees roughlyN x max_requestsper window. - Pass a
strategy-FixedWindow,SlidingWindow, orTokenBucket- to choose the algorithm, and abackendto choose where state lives:InMemoryRateLimitBackend(default) orveloce.contrib.redis.RedisRateLimitBackendfor 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)},
)
)
RequestIDMiddleware
¶
Bases: Middleware
Assign a unique request ID to each request and echo it in the response.
Usage::
app.add_middleware(RequestIDMiddleware())
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- passhsts_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- passcontent_security_policy.Permissions-Policy- passpermissions_policy.
A header a handler already set on the response is left untouched - these are defaults, not overrides.
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.
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.
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
¶
Resolved exclusion name - the instance/class name or class name.
process_response
async
¶
Called after route handler. Can modify the response.
is_host_allowed
¶
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.
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
¶
Resolved exclusion name - the instance/class name or class name.
process_response
async
¶
Called after route handler. Can modify the response.
is_websocket_origin_allowed
¶
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"}))
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
¶
Cookie
¶
File
¶
Form
¶
Header
¶
Path
¶
Query
¶
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 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.
query
¶
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
¶
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
¶
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. Usesapp.config["SERVER_NAME"]when set, otherwise falls back tolocalhost. Caller should override_scheme/_hostfor 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.
Secret
¶
Hold a str/bytes secret while resisting accidental disclosure.
Usage::
token = Secret(os.environ["API_TOKEN"])
send(token.reveal())
APIKeyCookie
¶
Bases: _APIKeyBase
API Key authentication via cookie.
challenge
¶
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
¶
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
¶
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"]
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.
InvalidTokenError
¶
Bases: JWTError
Malformed structure: not three segments, bad base64, or bad JSON.
JWTError
¶
Bases: Exception
Base class for all JWT decode/encode failures.
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.
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
¶
Return a copy of the stored payload, or None when absent or expired.
write
async
¶
Store a copy of data under session_id, expiring after max_age seconds.
delete
async
¶
Drop session_id from the store. No-op if not present.
replace
async
¶
Write data only when session_id still exists and is unexpired.
touch
async
¶
Refresh the expiry of an existing, unexpired entry without copying its payload.
sweep_expired
¶
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
¶
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
¶
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.
pop
¶
Remove key and return its value, marking the session modified on removal.
popitem
¶
Remove and return the last (key, value) pair, marking the session modified.
setdefault
¶
Insert default under key when absent, marking the session modified on insert.
update
¶
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
¶
Return the stored payload for session_id, or None when it
is absent, expired, or has been revoked.
write
async
¶
Persist data under session_id, to expire after max_age seconds.
delete
async
¶
Revoke session_id - a later read of it must return None.
replace
async
¶
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
¶
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 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.
loads
¶
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.
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
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
¶
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
¶
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.
charset
property
writable
¶
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
¶
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
¶
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
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
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
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
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
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
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
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
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/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
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
¶
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
¶
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
¶
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 yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync 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
¶
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 yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync 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
¶
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
¶
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 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.
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
¶
Live view of the client's cookie jar (see TestClient.cookies).
set_cookie
¶
Add or update a cookie sent on every subsequent request.
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
¶
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
¶
Add or update a cookie sent on every subsequent request.
session_transaction
¶
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.
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
¶
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
¶
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
¶
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
¶
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.
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
¶
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
¶
The WebSocket handshake URL path - ASGI-style shape.
Returns path plus ?query when a query string is present.
client
property
¶
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
¶
Per-connection scratch namespace.
Lazily-created State (a dict subclass) supporting both
ws.state.user = ... attribute access and ws.state["user"].
cookies
property
¶
Cookies sent with the WebSocket handshake.
Parses the handshake Cookie header into {name: value}.
Empty when no cookie header was present.
application_state
property
¶
Server-side state of the WebSocket.
CONNECTINGbefore the app sendsaccept().CONNECTEDafteraccept()and untilclose()is sent.DISCONNECTEDafterclose()(locally) or after the peer half-closes (observed viaWebSocketDisconnect).
client_state
property
¶
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
¶
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
¶
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
¶
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.
"*"inallowedaccepts any origin and is the opt-in "I have my own check elsewhere" escape hatch - the symmetric behaviour toWebSocketOriginMiddleware'sallowed_origins=["*"]. - Missing
Origin(no header at all, or a literalOrigin: nullfrom a sandboxed iframe /file://page) is a non-match and returnsFalse. Non-browser clients legitimately omit the header - if you want to allow them, branch onws.origin is Noneexplicitly. TheWebSocketOriginMiddlewaremiddleware path also offers anallow_missing=Trueswitch; 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
¶
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
¶
Complete the WebSocket handshake.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
if the connection is already accepted or already
closed, or if a |
send_json
async
¶
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).
receive
async
¶
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 a raw ASGI WebSocket message.
message is forwarded straight to the ASGI send callable,
e.g. {"type": "websocket.send", "text": "..."}.
set_send_drain
¶
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 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 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_bytes
async
¶
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
¶
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
¶
Async-iterate over incoming binary frames until the peer closes.
iter_json
async
¶
Async-iterate over incoming JSON-decoded frames until peer closes.
close
async
¶
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 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
¶
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 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 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 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 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
¶
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
¶
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
¶
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 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
¶
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
¶
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=TruesetsContent-Disposition: attachment; filename=<download_name or basename>.download_name=overrides the filename inContent-Disposition.last_modified=overrides the file's mtime (datetime, unix ts, or pre-formatted IMF-fixdate string).etag=Falsesuppresses the auto-generated ETag;etag="<value>"uses the caller-provided one verbatim (already-quoted).max_age=addsCache-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
¶
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
¶
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
¶
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
¶
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
¶
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 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
¶
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
¶
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
¶
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 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
¶
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
¶
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
¶
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
¶
Sign claims into a compact JWS token using the given HMAC algorithm.
make_reset_token
¶
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
¶
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
¶
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/removemutate in place.to_header()round-trips to a comma-separated header value.- Iteration yields items in 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
¶
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
¶
Return the value stored under name case-insensitively, or None.
header_present
¶
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
¶
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
¶
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
¶
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
¶
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
¶
Drop the connection named by its token (a no-op when token is None).
evict_session
¶
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
¶
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
¶
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
¶
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
¶
Shape this task into the MCP Task object the task methods return.
is_terminal
¶
Return whether the task has settled (no further transition happens).
settle
¶
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.
ResourceRegistry
dataclass
¶
Bases: Registry[MCPResource]
URI -> MCPResource, plus the shared JSON Schema component registry.
register
¶
Add item, rejecting a key already present with a primitive-specific error.
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.
TaskRegistry
dataclass
¶
Bases: Registry[MCPTask]
Task id -> MCPTask, the in-memory store of created tasks.
register
¶
Add item, rejecting a key already present with a primitive-specific error.
evict_expired
¶
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.
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
¶
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
¶
Write one server-initiated JSON-RPC message line to the client.
request
async
¶
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
¶
Render this error as a JSON-RPC 2.0 error response.