Back to Python tutorials
Basic15 min read

Exception Handling

Handle failures with try/except, custom exceptions, and context managers.

Try/Except/Else/Finally

try runs risky code; except catches specific exception types; else runs when no exception occurred; finally always runs for cleanup.

Catch specific exceptions—bare except catches KeyboardInterrupt unintentionally. Use except ValueError as err to inspect messages and chains.

Raise exceptions with raise NewError("msg") from err to preserve cause chains in logs.

  • Never use exceptions for normal control flow on hot paths
  • Define domain exception hierarchies for API layers
  • Log stack traces on unexpected errors only
try:
    data = parse_payload(raw)
except ValidationError as err:
    logger.warning("invalid payload", exc_info=err)
    raise HTTPException(400, str(err)) from err

Context Managers

with open(...) as f ensures files close. contextlib.contextmanager builds managers from generators with yield.

Custom managers implement __enter__/__exit__ or use @contextmanager decorator. __exit__ receives exception info and can suppress errors by returning True.

Async context managers use async with for database pools and HTTP clients.

  • Prefer with over manual close in finally
  • Use closing() from contextlib for objects with close()
  • Test context managers release resources on exceptions
@contextmanager
def timer(label):
    start = time.perf_counter()
    try:
        yield
    finally:
        print(f"{label}: {time.perf_counter() - start:.3f}s")

Get In Touch


Ready to discuss your next project? Drop me a message.