v3.9.0
Highlights
WebSocket dependency injection. Path parameters and registered dependencies now inject into WebSocket handlers, completing the DI story from 3.7/3.8:
@api.route("/ws/{room}", websocket=True)
async def chat(ws, *, room, hub):
await ws.accept()
await hub.join(room, ws)Injection is opt-in by name — handlers that only take ws keep working unchanged. Providers that take a parameter receive the WebSocket, and generator teardown runs when the handler finishes.
OpenAPI 3.1 + better schemas.
openapi="3.1.0"is now supported.- Path parameters are documented automatically, typed from your route convertors (
{id:int}→ required integer parameter). - Fixed: spec paths no longer leak convertor patterns —
/users/{id:int}is now emitted as the spec-compliant/users/{id}. - The schema endpoint serves JSON via
Accept: application/json, or always withopenapi_route="/schema.json".
Content-negotiated errors. Built-in 404 and 405 responses return {"error": ...} JSON bodies to clients that ask for JSON, while browsers keep getting plain text.
Quality
- mypy: 25 errors → 0 across the codebase — real signature fixes and dead-code removal, not blanket ignores
- ruff: 11 findings → 0
- Duplicate route registration now raises
ValueErrorinstead of anassertthat disappears underpython -O
Full changelog: https://github.com/kennethreitz/responder/blob/main/CHANGELOG.md