import falcon.asgi
from falcon_pachinko import WebSocketResource, WebSocketRouter, handles_message
class ChatResource(WebSocketResource):
async def on_connect(self, req, ws, room: str) -> bool:
self.state["room"] = room
return True # continue to message handling
@handles_message("chat.message")
async def handle_message(self, ws, payload):
await ws.send_media({"type": "echo", "text": payload.text})
app = falcon.asgi.App()
router = WebSocketRouter(receive_loop=True)
router.add_route("/chat/{room}", ChatResource)
router.attach(app, "/ws")
attach()mounts the router at/wsand registers a Falcon catch-all route for descendant paths. It also enables pass-through TEXT and BINARY input for every WebSocket route on the app, while preserving the configured outgoing serializers. It warns if custom media handlers will be replaced.- Each connection receives a fresh resource instance and a shared state proxy scoped to that connection.
- The example opts into a router-owned receive-and-dispatch session with
receive_loop=True. The default isFalse, which returns after acceptance and leaves receives and cleanup to the application.
The `add_route` signature
def add_route(
self,
path: str,
resource: type[WebSocketResource] | cabc.Callable[..., WebSocketResource],
*init_args: object,
name: str | None = None,
**init_kwargs: object,
) -> None: ...
*init_argsand**init_kwargsare stored and passed toresourcewhen a connection instantiates it, alongside the path parameters. Example passing both a positional and a keyword initializer argument:
router.add_route("/rooms/{room}", ChatResource, "lobby", history_size=100)
nameis reserved for naming the route itself and is never forwarded to the resource initializer.resourceaccepts any callable returning aWebSocketResource, not only a class, so afunctools.partialfactory covers a resource whose own initializer needs a parameter calledname:
router.add_route("/p", functools.partial(MyResource, name="x"), name="route")
Route template syntax
Route templates match their literal text exactly. Regular-expression
metacharacters such as ., +, brackets, and parentheses have no special
meaning in a route. A parameter written as {name} captures one or more
non-/ characters; parameter names must be valid Python identifiers. A path
segment may contain one parameter or an adjacent pair at its end. Other
multiple-parameter layouts are rejected to bound regex matching work.
Braces are reserved for parameters. Empty, unmatched, nested, or doubled
braces, invalid names, duplicate parameter names, and ambiguous multiple-
parameter layouts raise ValueError during route or subroute registration. A
conflict between a mount-prefix parameter and a route parameter raises
ValueError when the router is mounted. Full routes and nested subroutes are
checked in registration order, so the first matching registration selects the
resource and its hooks.