Server
Core ASGI server classes.
AsgiServer
ASGI Server - main entry point for genro-asgi applications.
- Classes:
- AsgiServer – central coordinator: reads a
config.py(a ServerConfiguration builder), is the render target that configures itself, mounts apps, builds the middleware chain, handles lifespan, demultiplexes requests to the mounted apps via the Dispatcher (each app routes itself).
- AsgiServer – central coordinator: reads a
Inherits BasicAuthMixin and RoutingClass. Run with
AsgiServer(config_path).run().
WebSocket connections are handled by WsxHandler (WSX protocol over WebSocket).
- class genro_asgi.server.server.AsgiServer(config_path, host=None, port=None, reload=None, parent=None)[source]
Bases:
BasicAuthMixin,RoutingClassASGI server: a demultiplexer over the mounted applications.
The server does no routing of its own. It boots from a
config.pywhoseServerConfigurationbuilder is rendered onto it, mounting one app per URL prefix inapps; the Dispatcher selects the app from the first path segment and the app does its own routing.- config_path
Path to the
config.pythat configures this server.
- config_handler
ConfigurationHandler that renders the builder onto it.
- dispatcher
Dispatcher; demultiplexes requests to the target app.
- lifespan
ServerLifespan for startup/shutdown.
- server_application
ServerApplication with system endpoints (
_server).
- apps
Dict of mounted apps keyed by mount (the empty mount is the main app).
- wsx_handler
WsxHandler for WebSocket (WSX protocol) connections.
- storage
LocalStorage service shared by the apps.
- user_store
FileUserStore local identity source when
users=True, else None.
- resource_loader
ResourceLoader with hierarchical app/server fallback.
- session_store
Server-managed session store.
- request_registry
RequestRegistry tracking the current request.
- logger
Server logger instance.
- __init__(config_path, host=None, port=None, reload=None, parent=None)[source]
Initialize AsgiServer from a
config.pypath or a configuration class.Composes BasicAuthMixin (auth) and RoutingClass (routing). Builds the fixed parts of the server, then loads the configuration (a module with a
ServerConfigurationbuilder, or directly anAsgiConfigBuildersubclass) and renders it onto this server: the render is what mounts apps, builds the middleware chain and plugs the router. The server is the render target (seeapply_configuration).- Parameters:
config_path (
str|Path|type[AsgiConfigBuilder]) – Path to theconfig.py(its directory is the server dir), or anAsgiConfigBuildersubclass for a programmatic configuration (the server dir is the working directory).host (
str|None) – Runtime override for the host (wins over the configuration).port (
int|None) – Runtime override for the port (wins over the configuration).reload (
bool|None) – Runtime override for auto-reload (wins over the configuration).parent (
Any) – Optional parent object with shared resources and logic.
- apply_configuration(nodes)[source]
Apply the rendered configuration nodes to this server.
Each top-level ConfigNode is dispatched by tag to
_apply_<tag>. An unknown tag raises: grammar and server must stay in sync.- Return type:
- apply_persisted_config()[source]
Apply plugin_config.json to the current router tree.
Called at boot after plugins are attached.
- Return type:
- apps: dict[str, AsgiApplication]
- build_db_handler(code, descriptor)[source]
Build a db handler from a
databasedescriptor (coderemoved).db_class(required, imported by the config.py) builds the db from the remaining attributes;db_handler_class(optional, defaultAsgiDbHandlerBase) wraps it. The handler is what the registry holds.- Return type:
- config_handler
- config_path
- configuration_class: type[AsgiConfigBuilder] | None
- dispatcher: Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]
- export_factory_env()[source]
Expose this server’s configuration to
server_factoryvia env.A file configuration travels as its path (GENRO_ASGI_CONFIG); a programmatic one as the dotted
module:Classof the configuration class (GENRO_ASGI_CONFIG_CLASS), which must therefore be importable.- Return type:
- get_plugin_config(router_path='', entry='')[source]
Read live plugin configuration for a router/entry.
Uses routing.configure(“?”) for introspection via genro-routes public API.
- host
- lifespan
- logger
- login_enabled
- parent
- property plugin_config_file: Path
Plain path of plugin_config.json in server_dir (no-encryption mode).
- property plugin_config_node: LocalStorageNode
Storage node for plugin_config.json.
Routes to the encrypted
secure:mount when key material is installed (storage.encryption_active); otherwise stays plain on thesite:mount at the historical path.
- port
- reload
- property request: BaseRequest | None
Current request from registry.
- request_registry
- resource_loader
- run(workers=None)[source]
Run the server using Uvicorn.
workers> 1 runs that many uvicorn processes on one shared socket (each rebuilds the server through the module factory, like reload does); mutually exclusive with reload — uvicorn’s own constraint.- Return type:
- save_plugin_config()[source]
Persist current runtime plugin config to plugin_config.json.
Uses routing.configure(“?”) to snapshot the full tree via genro-routes public API. Writes through plugin_config_node: encrypted at rest when keys are installed, plain otherwise (with a warning).
- server_application
- session_store
- set_plugin_config(router_path='', plugin_name='', target='_all_', **config_values)[source]
Apply a plugin config change at runtime without persisting.
Delegates to routing.configure() via genro-routes public API.
- Parameters:
- Returns:
True on success, or “error” key on failure.
- Return type:
- storage
- wsx_handler
Configuration
The server is configured by a config.py whose ServerConfiguration (a
subclass of AsgiConfigBuilder) is rendered onto it.
AsgiConfigBuilder — builder dialect for genro-asgi server configuration.
The user subclasses AsgiConfigBuilder in a config.py and overrides
main(self, root) (the recipe) and optionally setup(self, data) (the
values behind ^pointers). The server mounts the builder on a
ConfigurationHandler and renders it: rendering applies the configuration to
the live server (see AsgiConfigRenderer).
Example config.py:
from genro_asgi.config import AsgiConfigBuilder
from myshop.app import Application as Shop
class ServerConfiguration(AsgiConfigBuilder):
def main(self, root):
root.server(host="127.0.0.1", port=8000, reload=True)
root.middleware(cors=True)
apps = root.applications(default="shop")
apps.application(code="shop", app_class=Shop)
root.openapi(title="Demo Shop API", version="1.0.0")
- class genro_asgi.config.asgi_config_builder.AsgiConfigBuilder(name=None)[source]
Bases:
BuilderBase,AsgiConfigElementsServer-configuration dialect. Grammar from AsgiConfigElements, application to the live server from AsgiConfigRenderer.
- property renderer_asgi: AsgiConfigRenderer
Fresh AsgiConfigRenderer bound to this builder.
AsgiConfigRenderer — object renderer for the server configuration dialect.
Unlike the markup dialects (HTML, SVG) whose render emits a string, this is an
object renderer: a node renders to a ConfigNode carrying its tag, its
already-resolved attributes (the walk resolves ^pointers and data logic)
and its rendered children. The render result is the list of top-level
ConfigNodes, delivered to the server, which configures itself from them.
- class genro_asgi.config.asgi_config_renderer.AsgiConfigRenderer(builder, handler=None)[source]
Bases:
RendererBaseObject renderer: a node renders to a ConfigNode with resolved attrs.
- finalize(result, target, **_opts)[source]
Deliver the list of top-level ConfigNodes to the target.
An object dialect does not join fragments into text.
targetis the server: it receives the ConfigNodes viaapply_configurationand configures itself.target=Nonereturns the list as-is.- Return type:
- mode: str | None = 'asgi'
The render mode this renderer serves (
"html","svg", …). A class attribute: eachrenderer_<mode>property maps one mode to one renderer class, so the mode is intrinsic, not injected. The base has none; concrete dialects set it. Used to look up the mode’s default target and to resolve sub-builder renderers.
- render_type: str = 'object'
"string"for markup dialects (HTML, SVG, CSS, XML) whose fragments are serialized strings,"object"for dialects that produce live objects (widgets, flowables). Governs whethertarget=False(return-as-value) is allowed: meaningful for"string", an error for"object".- Type:
Output type of this renderer
- class genro_asgi.config.asgi_config_renderer.ConfigNode(tag, label, attrs, children)[source]
Bases:
objectA rendered configuration node: tag, label, resolved attrs, children.
- attrs
- children
- label
- tag
ConfigurationHandler — owns the configuration builder and renders it.
The server owns one ConfigurationHandler. It mounts the user’s ServerConfiguration builder and renders it toward the server: the render target is the server itself (never the handler), and the server carries the methods that turn the configuration nodes into server state.
The handler is the place where the reactive runtime will live (Fase 2):
a live() section mutating the configuration will re-render the touched
nodes toward the same target, the server, applying changes incrementally.
GenroAsgiWorker
A minimal single-app ASGI server (owns a ThreadExecutor).
GenroAsgiWorker - a minimal single-app ASGI server.
The unit a commander spawns to distribute load: one uvicorn process serving
exactly one app. Unlike AsgiServer it owns no multi-app registry,
middleware/plugin system, sessions, storage or config orchestration — only the
bare minimum to serve one app:
a
Dispatcher(which, with a single app on the empty mount, always resolves to that app),a
ServerLifespan(so the app’son_startup/on_shutdownrun),a
WsxHandlerterminating the websocket connections the commander pipes down (issue #44): the standing connection lives in the process that owns the user’s pages, so server-initiated push is the worker’s own business,the dual parent-child relationship with the app (
app.server -> worker),the app-facing server surface the app’s CANONICAL dispatch needs: a
RequestRegistry, an emptydb_registryand a None-returningauthenticate(no server-level auth: whoever fronts the worker owns it).
The app is mounted on the empty mount (‘’) so the dispatcher’s root fallback always reaches it. It is the executor in the daemon-less worker model: the commander forwards requests to it over HTTP; it just runs the app.
Two serving paths:
run()— the blocking CLI path (uvicorn.run), unchanged;start()/wait_stopped()/request_stop()— the async path for a parent that interleaves the serving with other tasks (the pool child entry): uvicorn runs as a task and, withport=0, the OS-assigned port is read back from the bound socket, so the worker can announce where it listens.
- class genro_asgi.server.worker.GenroAsgiWorker(app, host='127.0.0.1', port=8000, max_workers=None)[source]
Bases:
RoutingClassMinimal ASGI server hosting a single app, run by uvicorn.
- app
The single mounted application (an ASGI callable).
- apps
One-entry dict {‘’: app}, so Dispatcher/ServerLifespan — which iterate
server.apps— see the app on the root fallback mount.
- host
Bind host.
- port
Bind port.
- logger
Logger for worker messages.
- lifespan
ServerLifespan driving the app’s on_startup/on_shutdown.
- dispatcher
Dispatcher delegating every request to the single app.
- wsx_handler
WsxHandler terminating websocket connections (the dispatcher’s fallback for an app with no
serve_websockethook).
- executor
Owned ThreadExecutor — the dispatch for blocking work, whose metrics report this worker’s pressure (busy/total/queue/occupancy). The app reaches it via the dual relationship (
app.server.executor).
- request_registry
RequestRegistry the app’s canonical dispatch creates and unregisters its requests on.
- db_registry
Always empty — the minimal worker registers no databases; the app’s
get_dbfalls back to its own registry.
- server
The uvicorn Server of the async path (None until
start()).
- serve_task
The task running
server.serve()on the async path.
- __init__(app, host='127.0.0.1', port=8000, max_workers=None)[source]
Initialize the worker around a single app.
- app
- apps
- authenticate(scope)[source]
No server-level auth on the minimal worker: every identity is None.
The app’s canonical dispatch delegates here (the app-facing server protocol); whoever fronts the worker (the commander, a gateway) owns the authentication of what it forwards.
- dispatcher
- executor
- host
- lifespan
- logger
- port
- request_registry
- request_stop()[source]
Ask the serving task to exit (uvicorn drains connections and returns).
- Return type:
- async start()[source]
Serve as a task in the running loop;
self.portbecomes the bound port.The async counterpart of
run(): uvicorn’sserve()runs as a task and this returns once the server has started, with the OS-assigned port (port=0) read back from the bound socket — the same discoveryChannelHub.start()does for its TCP variant.- Return type:
- async wait_stopped()[source]
Block until the serving task ends, then release the dispatch executor.
- Return type:
- wsx_handler