skip to content

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 pageshow

explore

questions

100 · 8 sections

How do you define a custom exception class in Python, and what should it inherit from?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Write 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.

open as a page

EAFP and LBYL in Python: what do the two idioms mean, and which does the language favour?

level: juniorimportance: must knowfreq 70%
basics
~20 s

EAFP 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.

open as a page

What is the difference between BaseException and Exception in Python?

level: juniorimportance: must knowfreq 68%
basics
~20 s

BaseException 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.

open as a page

When does Python raise ValueError rather than TypeError?

level: juniorimportance: must knowfreq 62%
basics
~10 s

TypeError 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.

open as a page

How do you log a caught Python exception with its full traceback rather than just its message?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Inside 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.

open as a page

How does an except* clause match an ExceptionGroup, and what happens to unmatched exceptions?

level: middleimportance: must knowfreq 50%
basics
~20 s

Each 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.

open as a page

What is Python's ExceptionGroup, and what raises one in the standard library?

level: juniorimportance: should knowfreq 38%
basics
~10 s

ExceptionGroup 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.

open as a page

How do ExceptionGroup and BaseExceptionGroup differ, and when do you need the base one?

level: middleimportance: should knowfreq 28%
basics
~10 s

BaseExceptionGroup 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.

open as a page

How do ExceptionGroup.split and subgroup let you recover from part of a group and re-raise the rest?

level: seniorimportance: should knowfreq 30%
basics
~20 s

subgroup(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.

open as a page

How does contextlib.asynccontextmanager turn an async generator function into an async context manager?

level: juniorimportance: must knowfreq 45%
basics
~20 s

contextlib.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.

open as a page

How does @contextlib.contextmanager turn a generator function into a context manager?

level: juniorimportance: must knowfreq 70%
basics
~10 s

contextlib.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.

open as a page

Does a with statement catch exceptions raised in its body, or only run cleanup?

level: juniorimportance: must knowfreq 55%
basics
~20 s

Only 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.

open as a page

In Python, what does `with obj as x` bind to `x`, and which two methods must `obj` define?

level: juniorimportance: must knowfreq 78%
basics
~20 s

x 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.

open as a page

How does contextlib.suppress(FileNotFoundError) differ from try/except/pass?

level: middleimportance: must knowfreq 50%
basics
~10 s

They 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.

open as a page

What does Python's `assert` statement do, and what happens to it under `python -O`?

level: juniorimportance: must knowfreq 48%
basics
~20 s

assert 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.

open as a page

Why does `assert (total == 0, 'total must be zero')` never fail in Python?

level: middleimportance: should knowfreq 38%
basics
~20 s

assert 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.

open as a page

A payroll CSV importer runs with PYTHONOPTIMIZE=1 and its `assert` row checks never fire — why, and what should guard those rows instead?

level: seniorimportance: should knowfreq 40%
basics
~20 s

PYTHONOPTIMIZE=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.

open as a page

Why does a DeprecationWarning raised by a library never appear when you run your app?

level: middleimportance: must knowfreq 50%
basics
~20 s

Python ships default entries in warnings.filters that ignore DeprecationWarning everywhere except when it is reported from main, because the category targets developers rather than end users. Run with -W default::DeprecationWarning, PYTHONWARNINGS or -X dev to see it.

open as a page

How does Python choose which entry in warnings.filters applies to a warning?

level: middleimportance: should knowfreq 40%
basics
~20 s

Python 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.

open as a page

Why does a nightly ETL export print a DeprecationWarning only on its first batch?

level: seniorimportance: should knowfreq 30%
basics
~20 s

The 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.

open as a page

What warnings policy would you set with -W and PYTHONWARNINGS across a large codebase?

level: principalimportance: should knowfreq 25%
basics
~20 s

Escalate 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.

open as a page

What does a bare `except:` catch that `except Exception` does not?

level: juniorimportance: must knowfreq 78%
basics
~10 s

A 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.

open as a page

In Python, what does a `return` inside a `finally` block do to the value the `try` block was returning?

level: juniorimportance: must knowfreq 52%
basics
~20 s

A 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.

open as a page

A CLI wrapped in `except BaseException` finishes normally despite calling sys.exit(1) -- what is the handler catching?

level: middleimportance: should knowfreq 44%
basics
~20 s

It 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.

open as a page

Why does a `break` or `continue` inside a Python `finally` block silently swallow an exception?

level: middleimportance: should knowfreq 30%
basics
~20 s

A 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.

open as a page

A reconciliation retry loop catching BaseException ignores Ctrl-C -- how do you diagnose and fix it?

level: seniorimportance: should knowfreq 40%
basics
~20 s

The 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.

open as a page

Does the `del` statement call an object's `__del__` method in CPython?

level: juniorimportance: must knowfreq 50%
basics
~20 s

Not 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.

open as a page

Why does weakref.finalize(obj, obj.close) never fire while the program runs?

level: middleimportance: must knowfreq 40%
basics
~20 s

A 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.

open as a page

What does weakref.finalize(obj, func) register, and when does func run?

level: juniorimportance: should knowfreq 22%
basics
~20 s

weakref.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.

open as a page

What happens to an exception raised inside a `__del__` finalizer in CPython?

level: middleimportance: should knowfreq 45%
basics
~20 s

It 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.

open as a page

How do you cancel a weakref.finalize callback, run it early, or check if it ran?

level: middleimportance: should knowfreq 26%
basics
~20 s

Call 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.

open as a page

What does logging.exception() record that logging.error() alone does not?

level: juniorimportance: must knowfreq 58%
basics
~10 s

logging.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.

open as a page

Why translate a third-party library's exception into your own exception class at a module boundary?

level: juniorimportance: must knowfreq 55%
basics
~20 s

So 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.

open as a page

Why must an exception class raised in a child process be importable in the parent?

level: middleimportance: must knowfreq 45%
basics
~20 s

Pickle 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.

open as a page

Why does a published Python package define one base exception class that all its errors subclass?

level: middleimportance: must knowfreq 58%
basics
~10 s

So 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.

open as a page

Why does calling logger.exception() and then re-raising at every layer report one failure many times?

level: middleimportance: must knowfreq 50%
basics
~20 s

A 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.

open as a page