The OpenViper Application
The openviper.app module contains the central OpenViper
class - the entry point for all request handling. It ties together routing,
middleware, dependency injection, exception handling, and OpenAPI schema
generation into a single ASGI application.
Quick Start
from openviper import OpenViper
app = OpenViper(title="My API", version="1.0.0")
@app.get("/")
async def index(request):
return {"message": "Hello, World!"}
The app instance is an ASGI callable, so it can be passed directly to an
ASGI server such as uvicorn:
uvicorn myproject.app:app
OpenViper Class
- class openviper.app.OpenViper(debug=None, middleware=None, title=None, version=None, description=None, openapi_url=None, docs_url=None, redoc_url=None)
The central ASGI application class. Acts as both an ASGI callable and a router decorator, so routes are registered directly on the app instance.
- Parameters:
debug – Enable debug mode (overrides
settings.DEBUGwhen set).middleware – Extra middleware entries to prepend to the stack. Each entry is either a middleware class or a
(cls, kwargs_dict)tuple.title – OpenAPI document title. Falls back to
settings.OPENAPI["title"].version – API version string. Falls back to
settings.OPENAPI["version"].description – OpenAPI description. Falls back to
settings.OPENAPI["description"].openapi_url – URL path for the OpenAPI JSON schema.
docs_url – URL path for the Swagger UI.
redoc_url – URL path for the ReDoc UI.
- get(path, **kwargs)
- post(path, **kwargs)
- put(path, **kwargs)
- patch(path, **kwargs)
- delete(path, **kwargs)
- options(path, **kwargs)
Decorator shortcuts that delegate to the internal
Router. Register a handler for the corresponding HTTP method on path.
- route(path, methods, **kwargs)
Register a handler for path matching the given methods list.
- include_router(router, prefix='')
Mount a sub-
Router. When prefix is given, it is prepended to all routes in the sub-router.
- on_startup(func)
Register a startup lifecycle handler. func may be a plain function or an
asynccoroutine. Called during the ASGIlifespan.startupevent.
- on_shutdown(func)
Register a shutdown lifecycle handler. Called during the ASGI
lifespan.shutdownevent, in registration order.
- exception_handler(exc_class)
Decorator that registers a custom exception handler for exc_class. When an exception of that type (or a subclass) escapes a handler, the registered callback is invoked with
(request, exc)and must return aResponse.
- get_openapi_schema() dict
Return the generated OpenAPI schema dict. The result is cached after the first call; use
invalidate_openapi_schema()to force regeneration.
- invalidate_openapi_schema()
Clear the cached OpenAPI schema so it is regenerated on the next request.
- invalidate_middleware_cache()
Clear the cached middleware stack. Useful when routes or middleware are added dynamically after initial setup.
- coerce_response(result) Response
Convert a handler’s return value into a proper
Response. Supports dicts, lists, strings, bytes,None, Pydantic models, and objects with amodel_dump()method.
- call_handler(handler, request) Response
Invoke handler with the appropriate parameters extracted from request. Performs automatic response coercion via
coerce_response().
- resolve_middleware(raw_middleware) list
Resolve a list of middleware entries (strings or classes) into their corresponding classes. When a
CORSMiddlewareentry is found, its keyword arguments are wired from settings automatically.
- run(host='127.0.0.1', port=8000, reload=True, log_level='info', workers=1)
Start a uvicorn development server. Prefer
viperctl start-serverfor production deployments.
- test_client(**kwargs) httpx.AsyncClient
Return an
httpx.AsyncClientconfigured to send requests directly to this app. The returned client must be used as an async context manager.
Module-Level Helpers
- openviper.app.get_handler_signature(handler)
Return
(signature, type_hints)for handler, cached by identity. Bounded by an LRU cache of 128 entries.
- openviper.app.resolve_middleware_entry(mw)
Import and return a middleware class from a dotted string, or pass through a non-string mw as-is. Raises
ImportErrorif mw is a string that cannot be imported.
Lifecycle & App Discovery
When the ASGI lifespan starts, the OpenViper application performs the
following steps in order:
Build the middleware stack (cached after first build).
Generate the OpenAPI schema (if enabled).
Call
ready()on every installed app that exposes one.Call
startup()from installed applifecycle.pymodules.Run registered
on_startuphandlers.
On shutdown the process runs in reverse:
Call
shutdown()for started lifecycle apps in reverse order.Run registered
on_shutdownhandlers.
Route Auto-Discovery
If OPENVIPER_SETTINGS_MODULE is set (e.g. "myproject.settings"),
the application automatically imports myproject.routes and registers
any route_paths list found there. Each entry in route_paths must
be a (prefix, Router) tuple.
# myproject/routes.py
from openviper.routing.router import Router
from myapp.views import user_router, order_router
route_paths = [
("/users", user_router),
("/orders", order_router),
]
Installed App Hooks
Each entry in settings.INSTALLED_APPS may expose a ready() callable
in one of three locations (checked in order):
<app>.ready- a top-level attribute on the app package.<app>.apps.ready- inside anappssub-module.<app>.lifecycle.ready- inside alifecyclesub-module.
The callable may be either a plain function or an async coroutine.
Middleware Stack
The middleware stack is built from settings.MIDDLEWARE plus any extra
middleware passed to the OpenViper constructor. String entries are
resolved via resolve_middleware_entry(). The stack is
assembled in this order (outermost first):
ServerErrorMiddleware- catches unhandled exceptions.DefaultLandingMiddleware- serves the landing page when no custom root route exists.User-configured middleware from
settings.MIDDLEWARE.RateLimitMiddleware- prepended whenRATE_LIMIT_REQUESTS > 0.Static and media file serving (debug mode only).
When CORSMiddleware is present in the middleware list, its keyword
arguments are automatically wired from the CORS_* settings.
Response Coercion
Handlers do not need to return a
Response explicitly. The
OpenViper.coerce_response() method converts common return types
automatically:
Return Type |
Response |
|---|---|
|
|
|
|
|
|
Pydantic |
|
Object with |
|
|
Passed through unchanged |
Exception Handling
Unhandled exceptions are dispatched to the most specific registered handler by walking the exception’s MRO. Built-in handling is provided for:
HTTPException- returns the status code and detail from the exception.TableNotFound- returns 503; hides the table name in production.FieldError/QueryError- returns 400; hides field names in production.All other exceptions - returns 500 with a debug traceback in debug mode, or a generic
"Internal Server Error"in production.
Custom exception handlers are registered with the
exception_handler() decorator.
Append-Slash Redirects
In production mode (DEBUG=False), if a request path without a trailing
slash does not match any route but the slash-appended path does, the
application returns a 301 redirect. The redirect target is validated
to prevent open-redirect attacks: directory traversal sequences (..)
and non-relative paths are rejected.
API Reference
- openviper.app.HAS_PYDANTIC
Truewhen thepydanticpackage is importable;Falseotherwise. Used bycoerce_response()to detect Pydantic model instances.