Mounting applications
Status: Draft; implementation checked against the development source on 2026-09-08.
A BaseApplication has a code (its registry name) and mount (its first URL
segment). Both may be class attributes or constructor kwargs. By default the
code is the lowercase class name and the mount is the code. mount="" is the
root application; mount=None requests the default mount.
from genro_asgi import AsgiServer, RoutedApplication
from genro_routes import route
class Greeting(RoutedApplication):
@route()
def index(self):
return {"application": self.code}
server = AsgiServer(applications=[
Greeting(code="home", mount=""),
Greeting(code="catalog", mount="shop"),
])
/index reaches home; /shop/index reaches catalog with path="/index".
A named mount wins over the root application. Without a root application,
default="catalog" makes / redirect to /shop/ with status 307 and preserves
the query string. Other unmatched paths remain 404. A mount is one segment;
put deeper routing inside the application.
Hosting another ASGI application directly
Wrap an existing ASGI callable in BaseApplication. This small adapter delegates
HTTP and explicitly delegates WebSocket scopes to the hosted application:
from genro_asgi import BaseApplication
class HostedApplication(BaseApplication):
def __init__(self, asgi_app, **kwargs):
self.asgi_app = asgi_app
super().__init__(**kwargs)
async def __call__(self, scope, receive, send):
await self.asgi_app(scope, receive, send)
async def serve_websocket(self, scope, receive, send):
await self.asgi_app(scope, receive, send)
Compose it with HostedApplication(existing_asgi_app, code="external"). The
server strips the mount from path; it does not add it to root_path.
If a hosted framework needs a public URL prefix for generated links, configure
or adapt that framework’s scope handling explicitly. This adapter does not
forward ASGI lifespan events: initialize and close the hosted framework through
the adapter’s on_startup and on_shutdown hooks as required by that framework.
The raw WebSocket seam owns its handshake and Origin/auth checks. Read WebSockets before exposing it. For hosted ASGI/WSGI inside a worker pool, use the distinct multiworker SPA integration; its HTTP path buffers request and response bodies.