Recent chats
ASKGEEZ Q&A GUIDETechnology & devices

What should a context manager do when the managed block raises an exception?

Quick answer

Put cleanup in a finally path so it runs when the managed block exits normally or raises. Then let the exception propagate unless your context manager is deliberately designed to suppress that specific exception. In a generator-based context manager, an exception is raised at the yield point; catching it without re-raising can suppress it, so distinguish handling or logging from cleanup. [S1]

LET’S WORK THROUGH IT

The question, the details, the next step.

Question

What should a context manager do if code inside its with block raises? I want cleanup to happen, but I don't want an error to disappear by accident.

Geez

Separate the two jobs: perform cleanup on exit, and decide independently whether the exception should continue outward. For a generator-based context manager, Python raises an exception from the managed block at the yield point. A try/finally around that point is a way to ensure the cleanup step is attempted on either kind of exit. If you catch an exception only to log it or take another action, re-raise it unless suppressing it is intended. [S1]

Here is a small illustration. The events list stands in for observing what happened; it is not a real resource manager.

from contextlib import contextmanager

events = []

@contextmanager
def tracked_block():
    events.append("entered")
    try:
        yield
    finally:
        events.append("cleanup")

try:
    with tracked_block():
        raise ValueError("example failure")
except ValueError:
    events.append("error propagated")

print(events)

The expected list is ['entered', 'cleanup', 'error propagated']: cleanup runs, and the ValueError is still visible to the caller. This example demonstrates control flow only; replace the list updates with the cleanup appropriate to your own context.

Follow-up question

What if I need to catch the exception briefly to log it? Is that different from putting cleanup in finally?

Geez

Yes. Catching an exception gives your code a chance to inspect or respond to it; finally provides a place for cleanup regardless of whether the managed block completed or raised. If you catch an exception in a generator-based context manager and do not re-raise it, the generator can make the exception appear handled, which means the with statement may suppress it. The documented guidance is to re-raise when catching is only for logging or action and suppression is not intended. [S1]

For example, keep the cleanup path distinct from the handling path:

@contextmanager
def logged_block(log):
    try:
        yield
    except ValueError as exc:
        log.append(f"saw: {exc}")
        raise
    finally:
        log.append("cleanup")

Here, the illustrative log is a list supplied by the caller. The raise preserves the exception; finally records that the cleanup point was reached. In real code, consider whether the cleanup operation itself can fail and how that should be handled. This example does not promise that a cleanup operation will succeed.

Follow-up question

When is it appropriate to suppress an exception instead of propagating it?

Geez

Suppress an exception only when that is an explicit part of the context manager's intended behavior and the exception is one you deliberately mean to ignore. The contextlib.suppress utility, for example, is specifically for suppressing the exception types named to it; that purpose is distinct from performing cleanup. [S1]

Before suppressing, ask: would the caller be safe and correct to continue as though this operation had no error? If the answer is unclear, let the exception propagate rather than silently converting failure into apparent success. A finally block can still perform cleanup whether you suppress an exception or allow it to escape. Do not treat “cleanup ran” as evidence that the managed operation succeeded.

Follow-up question

Can you give me a reusable review checklist for an existing context manager?

Geez

Review its normal and exceptional exits separately. For a generator-based manager, follow the code from yield: exceptions from the managed block arrive there, so check what is caught, what is re-raised, and what cleanup runs. [S1] Use this short review record:

Managed operation: [what the with block does]
Cleanup step: [what should happen on exit]
Normal exit: [cleanup behavior and expected result]
Exceptional exit: [cleanup behavior and whether the error propagates]
Suppressed exceptions, if any: [specific exception and reason]
Cleanup failure: [what the caller should expect if cleanup itself fails]

Treat the final line as a design question, not a guarantee: the cited guidance explains cleanup structure and suppression, but it does not establish that a cleanup action cannot fail or prescribe the right policy for every resource. Review your intended cases in your own code, including a normal block and a block that raises.

A practical checklist

  • Identify the cleanup action and place it on a path reached after both normal and exceptional exits.

  • For a generator-based manager, inspect what happens when an exception is raised at yield.

  • If an exception is caught only for logging or another side action, re-raise it unless suppression is deliberate. [S1]

  • List any exception types intentionally suppressed and record why callers should continue after them.

  • Consider separately what should happen if the cleanup action itself raises.

A few more useful answers

Does putting cleanup in `finally` mean the cleanup is guaranteed to succeed?

No. It provides a cleanup path that runs on exit, but the cleanup operation can still fail. Decide how that failure should be handled for your particular manager.

Does logging an exception automatically suppress it?

Logging alone does not require suppression. In a generator-based context manager, catch-and-log code should re-raise the exception when it is not meant to be suppressed. [S1]

Is exception suppression just another name for cleanup?

No. Cleanup concerns what happens on exit; suppression concerns whether a particular exception is allowed to propagate. They are separate decisions. [S1]

Sources

References used for this guide.

  1. [S1] contextlib — Utilities for with-statement contexts (Python 3.15.2)
    docs.python.org · Checked

YOUR QUESTION. YOUR DETAILS.

Let’s work through your situation.

Get a tailored answer or help with your next task. Starts at $1, with a quote before you pay.

Ask your own question
← Back to Geez at Work