Skip to content

Application

The application object and its configuration.

Veloce

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

Ultra-fast async web framework.

Usage::

app = Veloce()

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

app.run()

debug property writable

debug: bool

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

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

secret_key property writable

secret_key: str | None

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

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

url_map property

url_map: _URLMap

Read-only mapping of registered URL rules.

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

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

routes property

routes: list[dict[str, Any]]

List all registered routes.

json property writable

json: Any

Active JSONProvider instance.

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

The provider serialises jsonify(...) and anything else that asks it for bytes. A handler returning a bare dict or list takes the direct orjson path instead and does not consult it, so a custom dialect - key sorting, a house encoder - does not reach those responses. Return jsonify(...) from a handler whose dialect must apply.

package_root property

package_root: str

Filesystem path of the directory containing import_name's module.

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

jinja_env property

jinja_env: Any

The app's shared Jinja2 Environment.

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

jinja_loader property

jinja_loader: Any

The app's Jinja template loader.

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

instance_path property

instance_path: str

Writable instance folder beside the package.

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

signal_namespace property

signal_namespace: Any

Accessor that returns the veloce.signals module.

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

aborter property writable

aborter: Any

Callable that raises typed HTTPExceptions by status code.

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

got_first_request property

got_first_request: bool

True after the first request has been fully handled.

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

cli property

cli: Any

Click Group for app-defined custom CLI commands.

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

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

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

view_functions property

view_functions: dict[str, Callable]

A {endpoint_name: handler} view of registered routes.

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

error_handler_spec property

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

Inspection view of registered error handlers.

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

before_request_funcs property

before_request_funcs: dict[Any, list[Callable]]

View of registered before_request hooks.

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

after_request_funcs property

after_request_funcs: dict[Any, list[Callable]]

Return the per-blueprint after-request hook registry.

teardown_request_funcs property

teardown_request_funcs: dict[Any, list[Callable]]

Return the per-blueprint teardown-request hook registry.

blueprints property

blueprints: dict[str, Any]

Snapshot mapping of bp.name -> Blueprint.

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

url_value_preprocessors property

url_value_preprocessors: dict[Any, list[Callable]]

View of registered URL-value preprocessors.

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

url_default_functions property

url_default_functions: dict[Any, list[Callable]]

View of registered URL-default callbacks.

dependency_overrides property writable

dependency_overrides: dict[Callable, Callable]

Mutable map of dependency callables to test replacements.

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

app.dependency_overrides[get_db] = get_fake_db

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

route

route(path: Annotated[str, Doc('URL path template, including `{param}` / `{param:converter}` placeholders.')], methods: Annotated[list[str] | None, Doc('HTTP methods this handler serves; defaults to `GET`.')] = None, dependencies: Annotated[list[Any] | None, Doc('Dependencies run for this route, appended after the router-level ones.')] = None, response_model: Annotated[Any, Doc("Type used to filter and serialize the handler's return value and the OpenAPI response schema. Defaults to the handler's return annotation when it names a model; pass `None` to declare no response contract.")] = _INFER_RESPONSE_MODEL, 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_resource_mime_type: Annotated[str | None, Doc("Media type advertised for the route's MCP resource.")] = None, mcp_meta: Annotated[dict[str, Any] | None, Doc("`_meta` published on this route's MCP tool or resource.")] = None, mcp_resource_size: Annotated[int | None, Doc("Size in bytes advertised for the route's MCP resource.")] = None, mcp_resource_annotations: Annotated[dict[str, Any] | None, Doc("Annotations (audience, priority) advertised for the route's MCP resource.")] = None, mcp_scopes: Annotated[Sequence[str] | None, Doc('Authorization scopes required to call this route over MCP.')] = None, mcp_icons: Annotated[Sequence[Any] | None, Doc('Optional MCP `Icon` objects a client may render next to the tool/resource.')] = None, mcp_task_support: Annotated[bool, Doc("Allow this route's MCP tool to run as a background task (task-augmented `tools/call`, polled via `tasks/get` / `tasks/result`).")] = False, exclude_middleware: Annotated[Sequence[str] | None, Doc('Names of middleware this route opts out of.')] = None, stream: Annotated[bool, Doc('Opt into request-body streaming: the body is not buffered before the handler, so the handler may consume `request.stream()` incrementally.')] = False) -> Callable

Generic route decorator.

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

trace

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

TRACE route decorator - RFC 9110 Sec. 9.3.8.

query

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

QUERY route decorator - RFC 10008.

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

websocket

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

Register a WebSocket route via decorator.

websocket_listener

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

Declarative WebSocket route - wrap a per-message callback.

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

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

Usage::

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

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

add_websocket_route

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

Register a WebSocket route imperatively (ASGI shape).

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

add_api_websocket_route

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

Register an imperative WebSocket route, mirroring add_api_route.

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

add_api_route

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

Register a route imperatively.

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

context_processor

context_processor(func: Callable) -> Callable

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

template_filter

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

Register a function as a Jinja filter.

Usage::

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

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

template_global

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

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

add_template_global

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

Imperative equivalent of @template_global.

template_test

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

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

add_template_filter

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

Imperative equivalent of @template_filter.

add_template_test

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

Imperative equivalent of @template_test.

get_spawned_task

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

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

cancel_spawned_task

cancel_spawned_task(name: str) -> bool

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

supervise

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

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

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

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

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

Usage::

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

test_client

test_client(**kwargs: Any) -> Any

Return an in-memory TestClient for this app.

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

async_test_client

async_test_client(**kwargs: Any) -> Any

Return an AsyncTestClient for this app.

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

app_context

app_context() -> _AppContext

Bind current_app and reset g for use outside a request.

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

test_request_context

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

Synthesise a fake request for outside-request testing.

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

run

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

Start the built-in development server.

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

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

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

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

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

install

install(plugin: Plugin) -> Plugin

Install plugin and return it.

Call plugin.install(self). When plugin has a truthy name, record it as self.extensions[name] and raise ValueError if that name is already taken. Raise TypeError when plugin has no callable install. A named plugin is recorded only after its install returns, so a failed install leaves no partial registry entry.

openapi

openapi() -> dict[str, Any]

Return the generated OpenAPI schema dict.

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

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

mount

mount(prefix: str, app: Any, *, expose_mcp: bool = False) -> None

Mount a sub-application at a path prefix.

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

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

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

expose_mcp=True additionally publishes the sub-app's MCP tools, resources and prompts through the parent's MCP server, with tool and prompt names prefixed by the mount. It is opt-in because mounting an app for its HTTP routes should not silently hand an agent everything it can do.

mount_static

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

Mount a static file directory.

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

add_middleware

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

Add middleware to the pipeline.

Call forms:

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

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

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

add_http_middleware

add_http_middleware(middleware: Any) -> Any

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

middleware

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

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

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

before_request

before_request(func: Callable) -> Callable

Register a function to run before each request.

before_first_request

before_first_request(func: Callable) -> Callable

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

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

after_request

after_request(func: Callable) -> Callable

Register a function to run after each request.

teardown_request

teardown_request(func: Callable) -> Callable

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

teardown_appcontext

teardown_appcontext(func: Callable) -> Callable

Register a function to run on app-context teardown.

on_event

on_event(event: str) -> Callable

Register startup/shutdown event handlers.

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

add_lifespan

add_lifespan(factory: Callable[..., Any]) -> Callable[..., Any]

Register an additional lifespan context manager.

lifespan= is a single slot owned by the application, which leaves a plugin or blueprint no way to own a resource with paired setup and teardown - it has to split the pair across on_startup / on_shutdown and lose the try/finally (and the yielded handle) between them.

factory is called with the app and must return an async context manager. Every registered lifespan is entered on the same exit stack as lifespan=, so teardown runs in reverse registration order, a failure part-way through startup unwinds only what was entered, and teardown errors are aggregated rather than masking one another.

The yielded value is not consumed - a plugin holds its own handle, the same way it holds any other state it owns.

Usage::

class BrokerPlugin:
    name = "broker"

    def install(self, app):
        app.add_lifespan(self.lifespan)

    @contextlib.asynccontextmanager
    async def lifespan(self, app):
        broker = await connect()
        try:
            yield {"broker": broker}
        finally:
            await broker.close()

on_startup

on_startup(func: Callable) -> Callable

Register a startup event handler.

on_shutdown

on_shutdown(func: Callable) -> Callable

Register a shutdown event handler.

add_event_handler

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

Imperative event-handler registration - ASGI shape.

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

before_serving

before_serving(func: Callable) -> Callable

Register a coroutine to run once at app startup.

after_serving

after_serving(func: Callable) -> Callable

Register a coroutine to run once at app shutdown.

lifespan_context

lifespan_context() -> _LifespanManager

Return an async context manager driving the lifespan cycle.

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

register_error_handler

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

Register an error handler without a decorator.

exception_handler

exception_handler(exc_class_or_status: type | int) -> Callable

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

add_exception_handler

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

Imperative exception-handler registration - ASGI shape.

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

log_exception

log_exception(exc: BaseException) -> None

Log an exception with traceback.

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

handle_http_exception async

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

Build the response for an HTTPException.

Walks registered status-code + class handlers first (matching abort() semantics), falling back to JSON {"detail": exc.detail, "status_code": exc.status_code} with exc.headers applied - byte-identical to what the request cycle emits for the same exception, so a handler reached over MCP or from a background task reports the error exactly as it does over HTTP.

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

handle_user_exception async

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

Dispatch an arbitrary exception.

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

include_router

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

Mount a sub-router include_router.

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

add_instrumentation

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

Register an observability instrumentation hook.

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

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

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

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

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

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

use_secure_defaults

use_secure_defaults() -> None

Apply a security-hardened configuration baseline.

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

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

security_audit

security_audit() -> list[str]

Return human-readable warnings about the current security posture.

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

response_contract_audit

response_contract_audit() -> list[str]

Report routes whose declared response contract is absent or contradictory.

A response contract is only checkable once every route is registered, so it is reported here rather than discovered by a request: a handler whose return value cannot satisfy its declared model would otherwise surface as a server error in production, one request at a time.

Two findings are produced. A route whose explicit response_model= names a different model than its return annotation is a contradiction - the annotation tells a reader and a type checker one thing while the wire carries another. A route with no response contract at all is listed so an app cannot silently ship most of its surface undocumented; many such routes are legitimate (HTML pages, redirects, streams), so this is informational rather than a failure.

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

send_static_file

send_static_file(filename: str) -> Any

Serve a file from app.static_folder.

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

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

send_static_file_async async

send_static_file_async(filename: str) -> Any

Serve a file from app.static_folder - async variant.

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

test_cli_runner

test_cli_runner(**kwargs: Any) -> Any

Return a Click CliRunner bound for testing app.cli.

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

dispatch_request async

dispatch_request(request: Request) -> Any

Alias for _dispatch_request.

full_dispatch_request async

full_dispatch_request(request: Request) -> Any

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

preprocess_request async

preprocess_request(request: Request) -> Any

Run all before_request hooks for request.

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

process_response async

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

Run all after_request hooks for (request, response).

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

ensure_sync staticmethod

ensure_sync(func: Callable) -> Callable

Wrap func so it is callable from synchronous code.

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

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

make_response

make_response(value: Any) -> Response

Coerce a handler-return value into a Response.

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

endpoint

endpoint(name: str) -> Callable

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

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

iter_blueprints

iter_blueprints() -> Any

Iterate over every registered Blueprint.

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

shell_context_processor

shell_context_processor(func: Callable) -> Callable

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

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

make_shell_context

make_shell_context() -> dict[str, Any]

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

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

url_value_preprocessor

url_value_preprocessor(func: Callable) -> Callable

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

Usage::

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

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

url_for

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

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

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

url_path_for

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

Resolve a URL path by endpoint name and parameters.

url_defaults

url_defaults(func: Callable) -> Callable

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

Usage::

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

Runs in registration order; mutate values in place.

register_blueprint

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

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

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

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

add_url_rule

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

Add a URL rule programmatically.

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

dependency_overrides_provider

dependency_overrides_provider() -> dict[Callable, Callable]

Return the dependency override mapping.

mcp_tool

mcp_tool(description: str, *, name: str | None = None, namespace: str | None = None, scopes: Sequence[str] | None = None, tags: Sequence[str] | None = None, icons: Sequence[Icon] | None = None, task_support: bool = False, annotations: dict[str, Any] | None = None, meta: dict[str, Any] | None = None, version: str | None = None) -> 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). version labels this registration: two tools sharing a name and declaring different versions are both registered, the higher one is listed, and a call naming no version reaches it.

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, meta: dict[str, Any] | 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."

add_mcp_tool

add_mcp_tool(tool: Any) -> None

Register an already-built MCPTool (contrib.mcp).

The decorator builds a tool from a handler; this takes one that already exists - most often from derive_tool, which narrows a registered tool into the façade an agent should see::

app.add_mcp_tool(derive_tool(internal, name="search", arguments={...}))

before_mcp_call

before_mcp_call(func: Callable) -> Callable

Register a hook that runs before every MCP call (contrib.mcp).

Called with the primitive's name and the arguments it was given. Return None to let the call proceed, or any other value to answer with that instead of invoking the handler - the same short-circuit shape before_request has. Raising an MCPError reports the failure to the client, which is how an authorization check refuses a call.

Unlike before_request, this reaches a tool registered with @app.mcp_tool, which has no route and so no request lifecycle::

@app.before_mcp_call
async def audit(name, arguments):
    log.info("mcp call", extra={"tool": name})

after_mcp_call

after_mcp_call(func: Callable) -> Callable

Register a hook that runs after every MCP call (contrib.mcp).

Called with the primitive's name and the handler's return value, and returns the value to send on - so a hook may rewrite a result, or return it unchanged. Hooks run in registration order, each seeing what the last returned. It does not run when the call raised.

mcp_completer

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

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

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

Usage::

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

mount_mcp

mount_mcp(transport: str = 'stdio', *, path: str = '/mcp', auth: Any = None, principal: Any = None, allowed_origins: Sequence[str] | None = None, exclude_middleware: Sequence[str] | None = None, sessions: bool = False, resumable: bool = False, tool_filter: Any = None, cache_ttl_ms: int | None = None, page_size: int | None = None, tool_search: bool = False, session_backend: Any = None, message_path: str = '/messages') -> 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. tool_filter narrows what tools/list reports per caller beyond the declared scopes: a callable (tool, principal) -> bool (sync or async) that hides tools an agent has no business seeing, so its context is not spent on tools it cannot invoke. Declared scopes are applied first, whether or not a filter is set - every list omits what this caller would be refused - so a filter can only hide further, never reveal; hiding a primitive does not change what happens if it is called anyway. cache_ttl_ms sets the freshness hint sent with cacheable results (tools/list, prompts/list, resources/list, resources/read and server/discover) on the modern protocol revision; 0 marks them immediately stale. A list that can differ between callers is additionally marked private so a shared proxy cannot serve one caller's answer to another. transport="sse" mounts the deprecated split-endpoint wire of MCP revision 2024-11-05, for a client that speaks only that: a GET at path (defaulting to /sse) opens a stream that names message_path as the URL to POST to, each POST is acknowledged 202 and its JSON-RPC response arrives on the stream. Prefer transport="http" for anything new - one endpoint, and a dropped connection can be resumed. session_backend shares HTTP sessions between workers - any object with async read / write / delete methods over a SessionRecord. Without one a session lives in the worker that minted it, so a request reaching a different worker is answered 404 and the client starts a new session. page_size opts the list methods into cursor pagination: each answers with at most that many entries plus a nextCursor while more remain, so a large catalogue reaches the agent a page at a time instead of filling its context in one response. Left unset, every list is answered in full - a client may ignore nextCursor, so paginating uninvited would hide the rest of the catalogue from one that does. tool_search publishes three tools in place of the catalogue - search_tools, describe_tools and run_tools - so a server with a large catalogue spends the agent's context on the tools it turns out to need rather than on every tool it has. run_tools executes declared calls, not code: each step names a registered tool and its arguments, and a step's argument may reference an earlier step's result. Call this after the tool / resource / prompt routes are registered.

Config

Bases: dict[str, Any]

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

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

default_config staticmethod

default_config() -> dict[str, Any]

The documented default config keys with their values.

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

from_mapping

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

Bulk-update from mapping and/or kwargs.

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

from_object

from_object(obj: object | str) -> bool

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

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

from_pyfile

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

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

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

from_env_file

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

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

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

Keys are stored exactly as the file spells them, and os.environ is not touched. This does not compose with from_prefixed_env, which strips its prefix: a file setting MYAPP_TIMEOUT becomes the config key MYAPP_TIMEOUT here and TIMEOUT there. veloce run seeds os.environ from the same file before importing the app, so an app using both reads two different keys depending on how it was started - pick one of the two and use it on every path.

from_envvar

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

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

from_prefixed_env

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

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

Reads os.environ only. from_env_file reads a file and keeps each key verbatim, so the two name the same setting differently - see its note.

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.

Plugin

Bases: Protocol

A Veloce plugin: any object exposing install(self, app).

Usage::

class TimingPlugin:
    name = "timing"

    def install(self, app):
        app.add_instrumentation(self._record)

app.install(TimingPlugin())

HealthPlugin

Serve liveness and readiness probes, and gate readiness on shutdown.

Usage::

from veloce import Veloce
from veloce.health import HealthPlugin

app = Veloce()
health = app.install(HealthPlugin())

@health.readiness_check("cache")
async def cache_ready() -> bool:
    return await redis.ping()

/livez reports whether the process and its event loop are running; it deliberately ignores dependency checks, because restarting a container cannot fix someone else's database.

/readyz reports whether this replica should receive traffic: startup has completed, shutdown has not begun, and every registered check passes. A failing check yields 503 with a per-check body naming what failed, so a probe failure is diagnosable from the response alone rather than only from logs.

Checks run concurrently and share one timeout; a check that hangs is reported as failed rather than holding the probe open until the orchestrator's own timeout fires.

draining property

draining: bool

Whether the replica has begun shutting down.

readiness_check

readiness_check(name: Annotated[str, Doc('Name reported for this check in the probe body.')]) -> Callable[[ReadinessCheck], ReadinessCheck]

Register a readiness check under name.

The check returns True when this replica can serve traffic. Raising is treated as not-ready, so a check does not need its own try/except.

start_draining

start_draining() -> None

Mark the replica as draining so /readyz starts failing.

Call this when a shutdown signal arrives, before connections are drained: the orchestrator then stops routing new requests here while in-flight ones finish. Veloce's own shutdown calls it automatically.

install

install(app: Veloce) -> None

Register the probe routes and the lifecycle hooks that gate them.

Signals

The pub/sub primitives and the eight signals Veloce fires around the request and app-context lifecycle. Connect a receiver with request_started.connect(fn); see the Signals guide for the payload each one carries.

Signal

A named pub/sub signal - standard shape.

Receivers connect via connect(receiver, sender=ANY_SENDER) and detach via disconnect(receiver, sender=ANY_SENDER). send(sender, **kwargs) fires every receiver subscribed for that exact sender (compared by is, falling back to ==) plus every receiver subscribed for ANY_SENDER. Return values are collected into a list of (receiver, value) tuples so callers can introspect what fired, though veloce's own code ignores the return value.

asend and send_robust_async await async receivers concurrently (sync receivers still run inline in registration order first).

connect

connect(receiver: Callable, weak: bool = True, *, sender: Any = ANY_SENDER) -> Callable

Register receiver to fire when send(sender) runs.

sender=ANY_SENDER (the default) subscribes to every send. Pass a specific sender to filter - the receiver then only fires when send is called with that exact sender. Returns the receiver unchanged so it can be used as a decorator.

disconnect

disconnect(receiver: Callable, *, sender: Any = ANY_SENDER) -> None

Remove the subscription for (receiver, sender).

Mirrors connect - to detach a per-sender subscription pass the same sender. With the default sender=ANY_SENDER it removes any subscription matching receiver, regardless of which sender it was bound to (back-compat with the previous unfiltered API).

Targeted detach matches the stored sender directly, not via _matches - _matches is the send-time rule ("does this subscription fire for that sender?"), where a stored ANY_SENDER deliberately matches every send. Reusing that rule in disconnect would silently delete an ANY_SENDER subscription whenever the caller targeted a specific sender.

send

send(sender: Any = None, **kwargs: Any) -> SignalResult

Fire receivers subscribed for sender (and for ANY_SENDER).

Returns (receiver, value) pairs in registration order. With no subscriptions the call short-circuits, so callers can invoke send unconditionally rather than guarding with has_receivers_for - a single live-scan then both fires and prunes dead weakrefs.

send_robust

send_robust(sender: Any = None, **kwargs: Any) -> SignalResult

Like send, but never aborts on a failing receiver.

Returns (receiver, value) pairs in registration order. The second tuple element is the receiver's return value, OR an Exception instance if the receiver raised. Per-receiver exceptions are logged at WARNING and substituted into the result list so the caller can inspect failures while subsequent receivers still fire.

Sync-only: if a receiver is an async function (or otherwise returns a coroutine), the coroutine is closed and a TypeError is recorded in the result list instead. Use send_robust_async to await async receivers.

asend async

asend(sender: Any = None, **kwargs: Any) -> SignalResult

Async, non-robust send - awaits async receivers concurrently.

Returns (receiver, value) pairs in registration order. Sync receivers run inline immediately, preserving registration order and raising on the first sync error exactly like send. Async receivers are collected and awaited concurrently, each inside a copy of the dispatch-time context. Like send, the first failing receiver propagates its exception (non-robust contract); use send_robust_async to capture per-receiver failures instead.

Even on the non-robust path every async receiver runs to completion before this coroutine returns OR raises: the concurrent run collects all results (failures included), and only afterwards is the first exception, in receiver order, re-raised. This guarantees no receiver is still touching request-scoped state once asend has returned - a return_exceptions=False gather would instead re-raise the first failure while later receivers kept running in the background past teardown.

send_robust_async async

send_robust_async(sender: Any = None, **kwargs: Any) -> SignalResult

Async variant of send_robust - awaits async receivers concurrently.

Returns (receiver, value) pairs in registration order. The second tuple element is the receiver's return value, OR an Exception instance if the receiver raised. Sync receivers run inline first, in registration order, each wrapped so a raised exception is recorded as its result. Async receivers (or any receiver returning a coroutine) are then awaited concurrently; one failing receiver never cancels the others. Per-receiver exceptions, raised either at call time or while awaiting, are logged at WARNING and substituted into the result list.

has_receivers_for

has_receivers_for(sender: Any = None) -> bool

True if any connected receiver would fire for sender.

A side-effect-free predicate that short-circuits on the first live, matching receiver. Dead weakrefs are skipped but not pruned here; pruning is left to send / _iter_live_targets.

Namespace

A factory that returns named Signal instances, one per name.

Calling signal(name) repeatedly with the same name returns the same Signal object, so independent parts of an application can obtain a shared signal by agreeing on a name rather than passing the instance around.

Usage::

from veloce.signals import Namespace

signals = Namespace()
user_registered = signals.signal("user-registered")

@user_registered.connect
def welcome(sender, **kw):
    ...

user_registered.send(app, user=user)

signal

signal(name: str, doc: str | None = None) -> Signal

Return the Signal named name, creating it on first use.

Repeated calls with the same name return the identical instance. doc is accepted for API familiarity; it is ignored because Signal is slotted and carries no per-instance docstring.

request_started module-attribute

request_started = Signal('request-started')

request_finished module-attribute

request_finished = Signal('request-finished')

request_tearing_down module-attribute

request_tearing_down = Signal('request-tearing-down')

got_request_exception module-attribute

got_request_exception = Signal('got-request-exception')

message_flashed module-attribute

message_flashed = Signal('message-flashed')

appcontext_pushed module-attribute

appcontext_pushed = Signal('appcontext-pushed')

appcontext_popped module-attribute

appcontext_popped = Signal('appcontext-popped')

appcontext_tearing_down module-attribute

appcontext_tearing_down = Signal('appcontext-tearing-down')