Applications
Mountable ASGI application types.
AsgiApplication
Base class for all genro-asgi applications.
AsgiApplication package.
- class genro_asgi.applications.asgi_application.AsgiApplication(**kwargs)[source]
Bases:
RoutingClassBase class for apps mounted on AsgiServer.
Routes live on the app’s single router (the lazy
routeproperty) and provide a default index() method. Subclasses define openapi_info for metadata and add routes with the @route() decorator; sub-trees are built by attaching child RoutingClass instances.Example:
class MyApp(AsgiApplication): openapi_info = {"title": "My API", "version": "1.0.0"} @route() def hello(self): return "Hello!" def on_init(self, **kwargs): # Sub-tree under /backoffice via a child instance self.attach_instance(Backoffice(self), name="backoffice")
- __init__(**kwargs)[source]
Set up mount name, app path, db registry and middleware slot, then call on_init.
- Parameters:
**kwargs (
Any) –base_diranddb_nameare consumed here; the rest is forwarded toon_init().
- property app_path: Path
Filesystem directory where this app is located.
Defaults to the directory of the module that defines the app’s class; the server may override it at mount time. Resources, templates and other per-app files are resolved relative to this path.
- authenticate(scope)[source]
Resolve the identity for a request. Default: delegate to the server.
Override to give an app its own authentication. Returning the server’s auth keeps the current shared behaviour.
- bind_kwargs(node, request)[source]
Return the kwargs to call
nodewith. Base: the request kwargs.Subclasses with a wire dialect (REST/JSON) override to reconcile payload conventions with the handler signature (see OpenApiApplication).
- configure(node)[source]
Interpret this app’s own configuration node, after mounting.
The app receives its whole
applicationnode (attributes already used as constructor kwargs, plus children) and interprets the tags of its own grammar. The server does not interpret an app’s config: an app may extend the grammar with tags the core does not know.Called by the server right after
attach_instance(soself.serveris available). Adatabaseschild builds handlers via the server’sbuild_db_handlerand fillsself.db_registry; themiddlewareattribute builds this app’s own middleware chain (self._chain).- Return type:
- get_db(name)[source]
Return a db handler by name: the app registry first, then the server.
Returns
Nonewhen no database is registered undernamein either registry (the app’s own databases shadow the server’s shared ones).- Return type:
- async handle_request(scope, receive, send)[source]
Resolve the request in this app’s router and send the response.
This is the per-app routing engine: authenticate, resolve the handler in
self.routefrom the path relative to the mount, build the context, execute and respond. Subclasses with a foreign protocol (e.g. a WSGI proxy) override this entirely.- Return type:
- load_resource(*args, name)[source]
Load a resource file as
(content_bytes, mime_type)orNone.Mounted: tries the server’s ResourceLoader first, then falls back to the app’s own
resources_dir. Standalone: readsresources_dirdirectly, with a small suffix-based mime map (octet-stream default).
- make_callable(node, request)[source]
Build the complete call this app wants to execute for
node.Not just the kwargs: the whole call, arguments AND lifecycle — packaged in the vehicle the handler needs. The node declares its handler’s nature (genro-routes marks async entries so
iscoroutinefunction(node)is honest); the caller runs the result viasmartasync(...)()either way:sync handler -> sync zero-arg callable -> executor thread, which is where thread-local resource teardown belongs (a thread-local db connection must be closed on the same thread it opened, which the request-level cleanup on the loop cannot do);
async handler -> async zero-arg callable awaited on the event loop, with
route_cleanuprunning there AFTER the handler — an async handler must not lean on thread-local resources.
Two hooks vary the behaviour, this wrapper does not:
bind_kwargsdecides the arguments (REST spread, etc.);route_cleanupruns after the handler. Both default to a no-op-ish base; subclasses/mixins override the one they own.
- monitor_descriptor()[source]
How this app’s panel LOOKS on the server monitor: a static descriptor.
The presentation complement of
monitor_state(): the state carries the data (polled), the descriptor tells the monitor shell which panel renders it (fetched once — it is a per-class constant).panelnames a renderer in the shell’s registry; this default (and any name the shell does not know) falls back to the generic panel — raw state as key/value rows and tables. Subclasses override to declare their specific panel and the extra endpoints it needs (seeSpaMultiWorkerApplication).
- monitor_state()[source]
This app’s contribution to the server monitor: identity facts.
The server-level monitor aggregates one entry per mounted app by calling this on each. Subclasses override to add their specific panel data (registries, pools, gauges) on top of these base facts.
- on_route_resolved(node, request)[source]
Hook called right after route resolution, before the handler runs.
A no-op seam on the base (FIXED): subclasses observe which route a request resolved to — e.g. a pool worker stamps per-connection activity here for routes not marked as system traffic (
@route(meta_sysrpc=True)).- Return type:
- on_shutdown()[source]
Called when server stops. Override for custom cleanup.
Can be sync or async. Called in reverse order of startup.
- Return type:
- on_startup()[source]
Called when server starts. Override for custom initialization.
Can be sync or async. Called after all apps are mounted.
- Return type:
- path_in_app(path)[source]
Return the request path relative to this app’s mount.
The server demultiplexes on the first path segment; an app resolves the remainder in its own router. The empty mount keeps the whole path.
- Return type:
- property resources_dir: Path | None
Return path to app’s resources directory.
Derived from
app_path(the app’s own location), so an app finds its resources whether mounted on the server or used standalone.
- route_cleanup()[source]
Teardown after the handler, in the executor thread. Base: nothing.
A subclass/mixin that owns a resource (e.g. a legacy db connection) overrides this to release it here — where it is thread-correct, unlike the request-level cleanup that runs on the loop.
- Return type:
- property server: AsgiServer | None
Return the server that mounted this app (semantic alias for _routing_parent).
- spread_over_params(node, data)[source]
Spread a dict of values over the handler’s declared parameters.
Shared by every wire dialect (REST body, MCP arguments): a structured payload is fitted to a scalar-parameter handler. Keeps
datawhole when the handler declares no signature (fieldsis None — no pydantic plugin) or accepts**kwargs; otherwise keeps only the declared names, dropping extras. An emptyfieldslist means a known no-parameter handler: everything is dropped. Reads the neutralparamsblock.
- static(file='')[source]
Serve a static file from the app’s resources directory.
Returns Path - set_result() handles mime type detection.
- Raises:
HTTPNotFound – If no file is named, or the resource does not exist.
- Return type:
OpenApiApplication
Application that serves an OpenAPI schema and Swagger UI.
OpenApiApplication package.
- class genro_asgi.applications.openapi_application.McpOpenApiApplication(**kwargs)[source]
Bases:
OpenApiApplicationOpenApiApplication that also exposes its API router as MCP tools.
- on_init(routing_class=None, module=None, **kwargs)[source]
Initialize; with no routing_class/module, direct mode (see module doc).
In direct mode the app’s own
@routemethods are the API: pydantic is plugged on the app router (signatures for schema and adaptation) and the channel plugin with defaultrest(a method joins the MCP face only via@route(channel_channels="mcp,...")). The attached MCP face inherits the pydantic plug from the app router.- Return type:
- class genro_asgi.applications.openapi_application.OpenApiApplication(**kwargs)[source]
Bases:
AsgiApplicationWrap a RoutingClass (or the app’s own @route methods) as REST + OpenAPI.
Adds meta endpoints under
_meta/(schema, docs, splash) alongside the API: - /{app}/_meta/schema_json — OpenAPI 3.1 JSON schema - /{app}/_meta/docs — Swagger UI page - /{app}/_meta/index — splash pageTwo ways to supply the API: - mounted mode: pass
routing_class=(ormodule=); the class isattached under
api_nameat /{app}/{api_name}/endpoint.direct mode: subclass and write
@routemethods on the app itself; endpoints sit at the app root /{app}/endpoint.
Example (mounted):
app = OpenApiApplication(routing_class=api) server.mount("sourcerer", app)
Example (direct):
class ShopApi(OpenApiApplication): @route(media_type="application/json") def items(self) -> dict: ...
- property api_info: dict[str, Any]
the mounted class’s, else the app’s, else empty.
- Type:
OpenAPI info dict
- bind_kwargs(node, request)[source]
Reconcile a JSON body with a REST handler’s scalar parameters.
A JSON body arrives as a single
body_datadict (request.py). A classic REST handler declares scalar parameters, notbody_data, so the dict must be spread over the fields the handler accepts. The handler signature comes from the node’s neutralparamsblock (never inspect).The whole
body_datais kept when the handler itself declaresbody_dataor accepts**kwargs— it wants the raw object or any key. Otherwise the dict is spread over the declared names, extras dropped.
- on_init(routing_class=None, module=None, docs='swagger', api_name='api', **kwargs)[source]
Initialize with a routing class instance or import path.
- Parameters:
routing_class (
RoutingClass|None) – RoutingClass instance to mount.module (
str|None) – Import path “package.module:ClassName” (alternative to routing_class).docs (
str) – Documentation style — “swagger”, “redoc”, or “off”.api_name (
str) – Path segment the routing class is attached under.**kwargs (
Any) – Passed to the routing class constructor if module is used.
- Return type:
McpApplication
MCP Streamable HTTP transport application.
McpApplication package.
- class genro_asgi.applications.mcp_application.McpApplication(**kwargs)[source]
Bases:
AsgiApplicationMCP Streamable HTTP transport as a genro-asgi application.
Accepts an external genro-routes Router and exposes its @route entries as MCP tools via JSON-RPC 2.0 over HTTP POST.
Mounts inside the server like any other app and participates in the full middleware chain (auth, CORS, errors). The
indexroute handles all JSON-RPC messages via the default_entry mechanism.Usage:
class SourcererMcpApp(McpApplication): mcp_name = "sourcerer" mcp_version = "1.0.0" def on_init(self, **kwargs): api = SourcererAPI(...) self.set_router(api.route)
- index()[source]
JSON-RPC 2.0 endpoint for MCP Streamable HTTP.
Reads the parsed body from
request.data(populated by the framework via asgi_data) and delegates dispatch to the MCP engine. Notifications (no “id”) get HTTP 202 and no body; requests get a JSON-RPC response.