patternpythonfastapiModerateCanonical
Python Async Context Managers with asynccontextmanager
Viewed 0 times
Python 3.7+
asynccontextmanagerasync context managerasync withlifespanFastAPIcleanup
Error Messages
Problem
Writing async resource management code (DB connections, HTTP sessions) requires implementing __aenter__ and __aexit__ boilerplate that is verbose and error-prone.
Solution
Use @asynccontextmanager decorator to write async context managers as generators.
from contextlib import asynccontextmanager
from typing import AsyncGenerator
import httpx
@asynccontextmanager
async def http_client() -> AsyncGenerator[httpx.AsyncClient, None]:
client = httpx.AsyncClient(timeout=30.0)
try:
yield client
finally:
await client.aclose() # Always runs, even on exception
async def fetch_user(user_id: str) -> dict:
async with http_client() as client:
response = await client.get(f'/api/users/{user_id}')
response.raise_for_status()
return response.json()
# FastAPI lifespan pattern
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app: FastAPI):
await db.connect() # Startup
yield
await db.disconnect() # Shutdown
app = FastAPI(lifespan=lifespan)Why
The generator approach separates setup (before yield) and teardown (after yield). The finally block guarantees cleanup even if the body raises an exception.
Gotchas
- Yield exactly once — multiple yields or no yield raises RuntimeError.
- Use 'async with' not 'with' for async context managers.
- FastAPI's lifespan replaces the deprecated on_event('startup') and on_event('shutdown') handlers.
Revisions (0)
No revisions yet.