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 else clause of a try run, and when does finally run?
  • Why is except Exception before except ValueError a mistake, and why is bare except: worse?
  • What does raise X from e do, and what does from None do?
  • What must a custom exception's __init__ call, and where do handler-relevant details go?
  • What does __exit__ receive, and what does returning True from it mean?
  • What does except* ValueError as g bind?
  • Why must assert never 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 1

Savepoints

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 empty

Calculator 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

← Assertions and defensive code — validate at the boundary, assert the invariant · The iteration protocol — iter, next and StopIteration →