Context Managers
Context managers are Python's clearest way to express resource ownership and scope. Files, DB sessions, locks, timeouts, lifespan wiring, and test cleanup can all be modeled with the same enter/exit shape.
Quick takeaway: `with` is structured `try/finally`. It is the most Pythonic way to make resource scope, transaction scope, and application lifespan explicit in code.
See the Execution Shape
A Small Class-Based Context Manager
class SessionScope:
def __enter__(self) -> str:
print("open resource")
return "session"
def __exit__(self, exc_type, exc, tb) -> bool:
print("close resource")
return False
with SessionScope() as session:
print("using", session)contextlib Is Often the Cleaner Tool
from collections.abc import Iterator
from contextlib import contextmanager
@contextmanager
def transaction_scope() -> Iterator[str]:
print("begin")
try:
yield "tx"
print("commit")
except Exception:
print("rollback")
raise
finally:
print("close")
with transaction_scope() as tx:
print("inside", tx)When the lifecycle is simple, `@contextmanager` is often more readable than a dedicated class. If you need reusable stateful objects, a class-based context manager may fit better.
Async Context Managers Matter Too
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan_scope() -> AsyncIterator[str]:
print("startup")
try:
yield "app-state"
finally:
print("shutdown")Practical Connections
- database session scope
- FastAPI lifespan wiring
- test fixtures and cleanup behavior
Checklist
Make scope explicit
If a resource has a clear open/close lifecycle, make that scope visible with `with` or `async with`.
Treat exception paths seriously
Commit vs rollback, startup vs shutdown, and acquire vs release should all be designed together.
Pick class vs helper intentionally
Classes fit reusable stateful objects; `contextlib` helpers fit simple lifecycle wrappers.
Use async context for async resources
Async clients, async DB sessions, and lifespan state should usually be managed with `async with`.