with syntax guarantees __exit__ runs even on exceptions, and the read intent stays obvious. try/finally works but adds bracket noise. Custom context managers turn resource pairs into ONE line of intent: locks, DB sessions, temporary files, telemetry spans.
with lock: critical_section() - exit() always releases even on exception.
with open(path) as f: read() - file closes even on partial reads.
with transaction(): write() - rollback on exception, commit on success.
@contextmanager wraps a generator into a with-statement.
with cm:
protected block
__exit__ runs ALWAYS
(try/finally: same guarantee, more noise)
Use with for every resource pair. __enter__/__exit__ is the cleanest abstraction.