Error Handling

A track of P31 · Typing & Robust Code.

Error handling as deliberate design: EAFP over LBYL, exception hierarchies you can catch at the right level, chaining that preserves the original cause, and preconditions that reject bad input at the door.

The most common error-handling code in the world is except: pass, and it is also the worst: it swallows every failure — including the KeyboardInterrupt you meant to let through — and destroys the evidence. Good error handling is the opposite of that reflex. It is designed, with the same care as the type system, and this track teaches the design.

You build an exception hierarchy — a domain base class with specific subclasses — so a caller can catch broadly (except OrderError) or narrowly (except OutOfStock), whichever it needs. You practise EAFP, Python's "try it and handle the exception" style, over the check-first LBYL that invites a time-of-check/time-of-use race. You chain errors with raise DomainError(...) from exc, translating a low-level failure into a meaningful one without losing the original cause in the traceback. And you enforce preconditions at the boundary with guard clauses that raise, so the rest of a function can trust its inputs — reserving assert for the internal invariants that -O is allowed to strip.

The through-line is a single decision, asked at every layer: raise or return? Raise for a broken precondition or a genuinely exceptional condition; return a value or None for an expected "not found" that is part of normal flow. Errors are control flow you design, not accidents you paper over.

Exception hierarchycatch at the right levelEAFP over LBYLtry it; avoid the check-use raceChaining (raise ... from)keep the original causePreconditionsreject bad input at the door
Failure, designed with the same care as the types.