Exceptions
Exception hierarchy, handling, finally, and custom exceptions.
12 questions
JuniorTheoryVery commonWhat is exception handling, and how do you raise an exception?
What is exception handling, and how do you raise an exception?
It's a mechanism to react to runtime errors: try/except catches them, raise throws one. You raise an instance (or class, auto-constructed) of BaseException or a subclass. Uncaught ones propagate up.
Common mistakes
- ✗Thinking you can
raisearbitrary objects like strings or ints rather thanBaseExceptionsubclasses - ✗Believing
raisealways crashes the program and cannot be caught by an enclosing handler
Follow-up questions
- →What is the difference between raising a class and raising an instance?
- →What happens if no handler catches the propagating exception?
JuniorTheoryCommonHow do you define a custom exception in Python?
How do you define a custom exception in Python?
Subclass Exception (not BaseException), conventionally naming it with an Error suffix. You may add attributes or a custom __init__ to carry data. Then raise MyError("msg") like any built-in exception.
Common mistakes
- ✗Subclassing
BaseExceptioninstead ofException, soexcept Exceptionmisses it - ✗Forgetting that a custom exception can carry a message and extra attributes
Follow-up questions
- →How do you build a hierarchy of related custom exceptions?
- →What should a custom
__init__call onsuper()to preserveargs?
JuniorTheoryCommonWhat is the exception class hierarchy at the top?
What is the exception class hierarchy at the top?
BaseException is the root. Exception derives from it and is the base for ordinary errors. But SystemExit, KeyboardInterrupt, and GeneratorExit derive from BaseException directly, so except Exception does not catch them.
Common mistakes
- ✗Assuming
except ExceptioncatchesSystemExitorKeyboardInterrupt - ✗Treating
BaseExceptionandExceptionas interchangeable
Follow-up questions
- →Why are
SystemExitandKeyboardInterruptdeliberately kept outsideException? - →Which base class should a bare
except:clause be thought of as catching?
JuniorTheoryCommonWhy use try/finally without an except?
Why use try/finally without an except?
The finally block always runs — whether the try succeeds, raises, or returns — so it's used for guaranteed cleanup like closing files or releasing locks, while letting any exception propagate uncaught.
Common mistakes
- ✗Believing
finallyis skipped when thetryraises — it always runs - ✗Thinking
finallyswallows the exception instead of letting it propagate
Follow-up questions
- →What happens if
finallyitself contains areturnorraise? - →How does a
withstatement relate totry/finally?
MiddleTheoryCommonIn what order must multiple except handlers be written?
In what order must multiple except handlers be written?
Handlers are checked top-to-bottom and only the first match runs, so more-specific exceptions must come before their general bases, or the general one shadows them. A bare except: must be last.
Common mistakes
- ✗Placing a general base handler before its specific subclass, shadowing the latter
- ✗Believing multiple matching handlers all run instead of just the first
Follow-up questions
- →How does
except (TypeError, ValueError)differ from two separate clauses? - →Why does Python recommend avoiding a bare
except:clause?
MiddleTheoryOccasionalWhat does the else block of a try statement do?
What does the else block of a try statement do?
The else runs only if the try block completed without raising any exception. It holds code that shouldn't be guarded by the except, keeping the protected try minimal. It runs before finally.
Common mistakes
- ✗Thinking
elseruns when an exception occurred rather than when none did - ✗Confusing
elsewithfinallyand assuming it always runs
Follow-up questions
- →Why move code into
elseinstead of leaving it insidetry? - →In what order do
elseandfinallyexecute on a clean run?
MiddleDebuggingOccasionalWhy is this ValueError handler unreachable?
Why is this ValueError handler unreachable?
except Exception is checked first and catches ValueError too (it is a subclass), so the ValueError handler is unreachable (Python even flags it). Put specific exceptions before general ones. Also avoid a bare except: — it swallows KeyboardInterrupt / SystemExit.
Common mistakes
- ✗Writing the general handler before the specific one
- ✗Thinking Python picks the most specific matching handler regardless of order
- ✗Believing
ValueErroris not a subclass ofException
Follow-up questions
- →What does Python warn about for an unreachable
exceptclause? - →Why is a bare
except:discouraged even when ordering is correct?
MiddleTheoryOccasionalHow do you catch an exception, act, then re-raise the same one?
How do you catch an exception, act, then re-raise the same one?
Use a bare raise with no argument inside the except block — it re-raises the currently handled exception while preserving its original traceback. Writing raise exc instead would reset or alter that traceback.
Common mistakes
- ✗Writing
raise excinstead of a bareraise, which mangles the traceback - ✗Thinking a bare
raiseraises a generic error rather than the current one
Follow-up questions
- →How does
raise NewError from excdiffer from a bareraise? - →What does a bare
raisedo if no exception is currently being handled?
MiddleTheoryOccasionalWhat are warnings, and how do you issue one?
What are warnings, and how do you issue one?
Warnings flag non-fatal issues without stopping execution. Warning is a subclass of Exception; warnings.warn(message, category=UserWarning) emits one. The warnings filter can suppress them or escalate them to errors.
Common mistakes
- ✗Assuming a warning stops execution like an exception does
- ✗Forgetting that
Warningitself derives fromException
Follow-up questions
- →How do you escalate a specific warning category into a hard error?
- →What does the
stacklevelargument ofwarnings.warncontrol?
SeniorTheoryOccasionalWhat is exception chaining (__context__ vs __cause__)?
What is exception chaining (__context__ vs __cause__)?
If an exception is raised while handling another, Python implicitly links the old one in __context__. Explicit raise New from old sets __cause__ for a direct cause; raise New from None suppresses the displayed context.
Common mistakes
- ✗Believing a second exception overwrites and loses the original
- ✗Confusing implicit
__context__with the explicit__cause__fromraise ... from
Follow-up questions
- →When would you deliberately use
raise New from None? - →How does
__suppress_context__change the printed traceback?
SeniorTheoryOccasionalWhat happens to an exception that no handler catches?
What happens to an exception that no handler catches?
It propagates outward to the nearest enclosing try/except; if none catches it, the interpreter prints the traceback to sys.stderr and exits. But SystemExit exits quietly, and one raised in __del__ only prints "Exception ignored".
Common mistakes
- ✗Thinking an uncaught exception is silently ignored and execution continues
- ✗Assuming
SystemExitprints a full traceback like an ordinary error
Follow-up questions
- →How can you customize what happens via
sys.excepthook? - →Why is an exception in
__del__only reported, not propagated?
SeniorTheoryRareWhen can a SyntaxError actually be caught at runtime?
When can a SyntaxError actually be caught at runtime?
A SyntaxError in the main module aborts before execution, so it can't be caught there. It is catchable at runtime: importing a module with bad syntax, or running eval/exec on a malformed string. SyntaxError subclasses Exception.
Common mistakes
- ✗Assuming a
SyntaxErrorcan never be caught under any circumstances - ✗Believing
try/exceptcan skip over malformed syntax in the current file
Follow-up questions
- →Why does the parser fail the whole module before any line runs?
- →What related error does
IndentationErrorsubclass?