1.WSGI vs ASGI
WSGI and ASGI are the two contracts a Python web app can implement so any compliant server knows how to call it. WSGI defines one synchronous function; ASGI defines three async functions and adds support for WebSockets and background tasks.
Key features
- WSGI (PEP 3333): app(environ, start_response) — one synchronous call per request
- ASGI: async def app(scope, receive, send) — one async call per connection, not per request
- WSGI has no concept of WebSockets or long-lived connections; ASGI's scope["type"] can be "http", "websocket", or "lifespan"
- Flask and Django (pre-4.1 sync views) are WSGI; FastAPI, Starlette, and Django's async views are ASGI
- An ASGI server (Uvicorn) can also run a WSGI app via an adapter, but a WSGI server (Gunicorn's sync worker) cannot run an ASGI app directly
WSGI vs ASGI at a glance
- WSGI
- app(environ, start_response)
- Returns the full response body at once
- Blocks the worker until the request finishes
- No native WebSocket or streaming support
- ASGI
- async def app(scope, receive, send)
- Sends response parts as separate messages
- Awaits I/O instead of blocking the whole worker
- Same interface handles HTTP, WebSocket, and lifespan
Command
# WSGI callable — PEP 3333
def app(environ, start_response):
start_response(status, headers)
return [body_bytes]
# ASGI callable — ASGI spec, three async parameters
async def app(scope, receive, send):
message = await receive()
await send({"type": "http.response.start", ...})
await send({"type": "http.response.body", ...})WSGI: one sync function, app(environ, start_response). ASGI: one async function with three parameters, app(scope, receive, send), adding WebSockets and lifespan events.