Python Exceptions Quiz: Custom Errors and Context Managers
Test your Python error handling with 12 questions and three programs on try and finally, chaining, custom exceptions, context managers and exception groups.
- Course: Python study plan
- Module: Errors and exceptions
- Kind: Checkpoint — cleared at 70%
- Reading time: 25 min
- Runtime: CPython 3.11
Checkpoint — Errors and exceptions is the checkpoint that closes the Errors and exceptions module: a graded quiz and whole-program exercises, passed at 70%.
Instructions
This checkpoint covers the whole module: the try statement with else and finally, the hierarchy and how handlers match, raise with chaining, custom exception classes with data and a base per package, EAFP patterns with suppress and sentinels, context managers as classes and generators with contextlib, exception groups with except* and add_note, and assertions versus boundary validation.
How it works. Twelve questions and three programs. You need 70% on the questions and every program accepted to clear the module. You can retake it as often as you like; your best score counts.
Before you start, make sure you can answer these from memory:
- When does the
elseclause of atryrun, and when doesfinallyrun? - Why is
except Exceptionbeforeexcept ValueErrora mistake, and why is bareexcept:worse? - What does
raise X from edo, and what doesfrom Nonedo? - What must a custom exception's
__init__call, and where do handler-relevant details go? - What does
__exit__receive, and what does returningTruefrom it mean? - What does
except* ValueError as gbind? - Why must
assertnever validate input?
The three programs are a validated record loader that collects every failure into an ExceptionGroup with notes, a transaction context manager whose commit-or-rollback behaviour is observable, and a calculator whose custom exception hierarchy is caught at exactly the right level.
Common questions
What does except* ValueError as g bind?
An ExceptionGroup holding only the ValueError members of the group that was raised — not a single ValueError, even when only one member matched. Iterate over g.exceptions to reach the individual errors.
Why must assert never validate input?
Running Python with -O removes every assert, so the check disappears and bad input sails straight through. Validate input with an if that raises ValueError or TypeError, which always runs.
What must a custom exception's __init__ call?
super().__init__(message), so that str(e), e.args and the traceback show the intended message. Details a handler needs go in attributes set after that call, not only in the message text.
Exercises
Record loader
Load records name;age;email. Define MissingField(Exception) (raised when a line has fewer than three fields) and use ValueError for a non-integer or negative age. Validate every line, add a note line <n> to each failure, and raise a single ExceptionGroup if any failed. Handle it with except* MissingField and except* ValueError, printing missing: <n> / invalid: <n> followed by the notes in order. Finally print loaded <k> with the number of valid records.
Input: lines. Output: the group summaries (if any), then loaded <k>.
ada;36;a@x
bob;x;b@x
cy;1
dee;-2;d@x
prints
missing: 1
line 3
invalid: 2
line 2
line 4
loaded 1Savepoints
Implement Savepoint(state) as a context manager over a list of integers: on entry it copies the list; on an exception it restores the copy and prints rollback, otherwise prints commit; it never suppresses the exception. Input is a nested script: begin opens a savepoint (nesting allowed), end closes it, append n appends, fail raises RuntimeError — which propagates through every open savepoint until the outermost begin … end block's caller catches it and prints caught. After the script print the state.
Input: commands. Output: commit/rollback/caught lines, then state <values> (or state empty).
begin
append 1
begin
append 2
fail
end
append 3
end
prints
rollback
rollback
caught
state emptyCalculator with a hierarchy
Define CalcError(Exception), ParseError(CalcError) and DivisionError(CalcError, ZeroDivisionError). evaluate(line) parses a op b (integers, + - * /) — a malformed line raises ParseError, division by zero raises DivisionError; / is integer division. Handle each line with, in this order: except DivisionError → division: <msg>, except ParseError → parse: <msg>, except CalcError → calc: <msg>. Then show that the multiple inheritance works: a second try evaluates the same line and catches only ZeroDivisionError, printing also a ZeroDivisionError when that fires.
Input: lines. Output: the result or the handled message; plus the extra line for division errors.
6 / 3
1 / 0
1 + x
prints
2
division: division by zero
also a ZeroDivisionError
parse: bad operand 'x'In this module: Errors and exceptions
- Exceptions — try, except, else, finally, raise
- Custom exceptions — a hierarchy for your own errors
- EAFP and exception-driven flow — suppress, retries, return versus raise
- Context managers — with, __enter__/__exit__ and contextlib
- Exception groups, notes and the traceback module
- Assertions and defensive code — validate at the boundary, assert the invariant
- Checkpoint — Errors and exceptions (this lesson)
← Assertions and defensive code — validate at the boundary, assert the invariant · The iteration protocol — iter, next and StopIteration →