Errors and Context Managers
How Python signals failure and guarantees cleanup: the exception tree, try/except/else/finally, groups, the with protocol and contextlib, assertions and warnings. Interviewers judge the unhappy path.
part ofPythonoverview, primer and where to startread it →on this pageshowhide
explore
- The Exception Model31 questions
- Exception Hierarchy and Builtins4 questions
- try / except / else / finally Flow4 questions
- Exception Chaining4 questions
- Designing Custom Exceptions4 questions
- EAFP vs LBYL4 questions
- The Cost of Raising4 questions
- Attaching Notes with add_note3 questions
- Traceback Objects4 questions
- Exception Groups4 questions
- Context Managers and the with Statement23 questions
- The with Statement Protocol4 questions
- Exception Suppression in __exit__4 questions
- contextlib.contextmanager4 questions
- ExitStack and Dynamic Cleanup4 questions
- contextlib Helpers4 questions
- Async contextlib Helpers3 questions
- Assertions and the -O Flag3 questions
- The warnings Module and Filters4 questions
- Handler Syntax Forms7 questions
- Overbroad except Clauses4 questions
- Control Flow Inside finally3 questions
- Finalization and Cleanup Order7 questions
- __del__ and Resurrection3 questions
- weakref.finalize Callbacks4 questions
- Failures at API Boundaries21 questions
- Translating Exceptions Across Layers4 questions
- Library Exception Contracts4 questions
- Retry Decorators and Backoff5 questions
- Exceptions Across Processes4 questions
- Log, Raise, or Swallow4 questions
questions
100 · 8 sectionsHow do you define a custom exception class in Python, and what should it inherit from?
basics
~20 sWrite a class that inherits from Exception; an empty body with a docstring is enough. Name it with an Error suffix, give the module one base class of its own, and derive every specific error from that base.
EAFP and LBYL in Python: what do the two idioms mean, and which does the language favour?
basics
~20 sEAFP means easier to ask forgiveness than permission: attempt the operation and catch the exception it raises. LBYL means look before you leap: check the preconditions with an if first. Python favours EAFP, because a separate guard can drift away from the operation it protects.
What is the difference between BaseException and Exception in Python?
basics
~20 sBaseException is the root of Python's exception tree; Exception is the branch that ordinary errors inherit from. SystemExit, KeyboardInterrupt and GeneratorExit sit outside Exception deliberately, so shutdown signals are not swept up with programming errors.
When does Python raise ValueError rather than TypeError?
basics
~10 sTypeError means the object is the wrong kind for the operation; ValueError means the type is right but the particular value is unusable. int([]) raises TypeError, int('twelve') raises ValueError.
How do you log a caught Python exception with its full traceback rather than just its message?
basics
~20 sInside the except block call logging.exception(msg), or pass exc_info=True to any logging call, so the record carries the active exception and its traceback. traceback.format_exc() returns that same text as a string; logging str(exc) throws the stack away.
How does an except* clause match an ExceptionGroup, and what happens to unmatched exceptions?
basics
~20 sEach except* clause splits the group by type: the matching members arrive as a group, the rest move on to the next clause. Several clauses can run for one raise, and whatever stays unmatched is re-raised after the try statement.
What is Python's ExceptionGroup, and what raises one in the standard library?
basics
~10 sExceptionGroup is a built-in exception added in Python 3.11 that carries several exceptions at once in its exceptions tuple. asyncio.TaskGroup raises one when its child tasks fail, so no failure is silently discarded.
How do ExceptionGroup and BaseExceptionGroup differ, and when do you need the base one?
basics
~10 sBaseExceptionGroup derives from BaseException and can hold any exception, including KeyboardInterrupt, SystemExit and asyncio.CancelledError. ExceptionGroup derives from Exception and rejects those with a TypeError. Constructing BaseExceptionGroup with only ordinary exceptions returns an ExceptionGroup.
How do ExceptionGroup.split and subgroup let you recover from part of a group and re-raise the rest?
basics
~20 ssubgroup(condition) returns a new group holding only the members that match, or None if none do. split(condition) returns both halves as a (matching, non_matching) pair, either of which may be None. Nesting, tracebacks and notes are preserved.
How does contextlib.asynccontextmanager turn an async generator function into an async context manager?
basics
~20 scontextlib.asynccontextmanager decorates an async generator function that yields exactly once. Calling it builds an object for use with async with: everything before the yield is the setup, the yielded value is what as binds, and everything after the yield is the teardown.
How does @contextlib.contextmanager turn a generator function into a context manager?
basics
~10 scontextlib.contextmanager decorates a generator function that yields exactly once. Everything before the yield runs on entry, the yielded value is what the as clause binds, and everything after the yield runs on exit.
Does a with statement catch exceptions raised in its body, or only run cleanup?
basics
~20 sOnly cleanup. The with statement calls the manager's exit on the way out, so the file gets closed or the lock released, but the exception keeps travelling up unless exit deliberately returns a truthy value.
In Python, what does `with obj as x` bind to `x`, and which two methods must `obj` define?
basics
~20 sx is bound to whatever __enter__ returns, not to obj itself. Any object usable in a with statement must define both __enter__ and __exit__; __exit__ runs on the way out whether the block succeeded or raised.
How does contextlib.suppress(FileNotFoundError) differ from try/except/pass?
basics
~10 sThey do the same thing: contextlib.suppress(FileNotFoundError) is the with-statement spelling of try/except FileNotFoundError: pass. It reads as deliberate rather than accidental, and since Python 3.12 it also strips matching exceptions out of an ExceptionGroup.
What does Python's `assert` statement do, and what happens to it under `python -O`?
basics
~20 sassert expr, msg raises AssertionError with msg when expr is falsy and does nothing otherwise. Starting the interpreter with -O, or with PYTHONOPTIMIZE set, removes every assert statement at compile time, so the check never runs.
Why does `assert (total == 0, 'total must be zero')` never fail in Python?
basics
~20 sassert is a statement, so those parentheses build a two-element tuple rather than passing two arguments. A non-empty tuple is always truthy, so the condition holds for every value of total and the assertion can never fire.
A payroll CSV importer runs with PYTHONOPTIMIZE=1 and its `assert` row checks never fire — why, and what should guard those rows instead?
basics
~20 sPYTHONOPTIMIZE=1 is equivalent to -O, so the compiler emits no bytecode for any assert statement and the row checks were never in the deployed program. Data arriving from a CSV file needs an explicit raise; assertions can only carry internal invariants.
How does Python choose which entry in warnings.filters applies to a warning?
basics
~20 sPython walks warnings.filters from the front and stops at the first entry whose message, category and module all match; that entry's action decides the outcome. filterwarnings and simplefilter insert at the front, so the newest entry is checked first.
Why does a nightly ETL export print a DeprecationWarning only on its first batch?
basics
~20 sThe default warning action shows a message once per location — the text, category and reported line — and records that in a per-module registry, so later identical warnings from the same line are skipped entirely. Switch the filter to always to see every occurrence.
What warnings policy would you set with -W and PYTHONWARNINGS across a large codebase?
basics
~20 sEscalate where someone can act and observe where they cannot: -W error scoped to your own modules in CI, warnings recorded and aggregated in staging, plain defaults in production. Libraries emit warnings; only applications configure filters.
What does a bare `except:` catch that `except Exception` does not?
basics
~10 sA bare except: catches every BaseException subclass, including KeyboardInterrupt, SystemExit and GeneratorExit. except Exception stops one level lower, so Ctrl-C and an exit request still propagate. Catch Exception, or a narrower type, instead.
In Python, what does a `return` inside a `finally` block do to the value the `try` block was returning?
basics
~20 sA return inside finally overrides everything already in flight: the try block's return value, a pending break or continue, and even an exception on its way out. The function returns the finally value, and the exception disappears silently.
A CLI wrapped in `except BaseException` finishes normally despite calling sys.exit(1) -- what is the handler catching?
basics
~20 sIt is catching SystemExit. sys.exit(1) does not end the process directly -- it raises SystemExit, which derives from BaseException rather than Exception, so a bare except: or except BaseException intercepts the exit request and execution simply continues.
Why does a `break` or `continue` inside a Python `finally` block silently swallow an exception?
basics
~20 sA finally block runs while the exception is still in flight and re-raises it only when the block ends normally. A break or continue leaves the block early, so the held exception is dropped and the loop simply carries on.
A reconciliation retry loop catching BaseException ignores Ctrl-C -- how do you diagnose and fix it?
basics
~20 sThe loop's handler catches BaseException, so KeyboardInterrupt is treated as a retryable failure and each Ctrl-C only aborts the current attempt. Narrow the handler to Exception, or keep it broad, run cleanup, and end it with a bare raise.
Does the `del` statement call an object's `__del__` method in CPython?
basics
~20 sNot by itself. The del statement removes a name binding and drops one reference. CPython runs the object's del finalizer only when the last reference to that object disappears, which may be later, or during cycle collection, or never.
Why does weakref.finalize(obj, obj.close) never fire while the program runs?
basics
~20 sA bound method holds a strong reference to its instance, so storing obj.close as the callback keeps obj permanently reachable. The object is never collected, and the finalizer only runs during the interpreter's exit pass. Capture the raw resource instead.
What does weakref.finalize(obj, func) register, and when does func run?
basics
~20 sweakref.finalize registers func as a cleanup callback for obj and returns a finalizer object. Python calls func exactly once: when obj is garbage collected, or at interpreter exit if obj is still alive. Registering does not keep obj alive.
What happens to an exception raised inside a `__del__` finalizer in CPython?
basics
~20 sIt never reaches the surrounding code. CPython hands it to sys.unraisablehook, whose default prints an "Exception ignored in" traceback to stderr and returns. The rest of the finalizer is skipped, and the program carries on as if nothing failed.
How do you cancel a weakref.finalize callback, run it early, or check if it ran?
basics
~20 sCall the finalizer object to run the cleanup now and mark it dead; call its detach() method to cancel it so it never runs; read its alive property to see whether it is still pending. peek() inspects the registration without cancelling it.
What does logging.exception() record that logging.error() alone does not?
basics
~10 slogging.exception() logs at ERROR level and attaches the traceback of the exception currently being handled, because it passes exc_info=True for you. logging.error() logs only the message unless you pass exc_info=True yourself.
Why translate a third-party library's exception into your own exception class at a module boundary?
basics
~20 sSo callers depend on your abstraction rather than on the library. If a database driver's error class escapes your function, every caller must import that driver to catch it, and replacing the driver breaks all of them.
Why must an exception class raised in a child process be importable in the parent?
basics
~20 sPickle does not serialize a class body. It stores the class by reference -- module name plus qualified name -- and the receiving process re-imports that module and looks the name up. If the lookup fails there, your exception never reconstructs.
Why does a published Python package define one base exception class that all its errors subclass?
basics
~10 sSo a caller can write one except PackageError to mean 'this library failed' without resorting to except Exception. The base marks the package's public error surface; the subclasses let callers who care be specific.
Why does calling logger.exception() and then re-raising at every layer report one failure many times?
basics
~20 sA re-raised exception keeps travelling with its full traceback, so every layer that logs it emits a complete report of the same failure. One error becomes N log records, N alerts and N counted errors. Log it once, where it stops.