CHAPTER 24 · INTERMEDIATE4 min read

Context Managers

Use with to make resource ownership and cleanup reliable on both success and failure.

Python 3.12+Free to readModule 5 of 13
YOUR NEXT SMALL WINS

By the end, you’ll be able to…

  • Explain the entry and exit behavior of with.
  • Build a small context manager with contextlib.
  • Use ExitStack when resource counts are dynamic.

01Resource lifetime is a control-flow problem

Some objects need an explicit end to their useful lifetime: open files, acquired locks, temporary directories, and database transactions. Relying on garbage collection leaves cleanup timing unclear. A context manager defines entry and exit actions around a block, making ownership visible where the resource is used.

The with statement asks a context manager to enter, optionally binds the returned value after as, runs the body, and then exits. Exit runs even when the body raises an exception or returns from a function. The object after as need not be the context manager itself; opening a file returns a handle appropriate for the work inside the block.

closed_handle.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

02A generator can express setup and cleanup

contextlib.contextmanager converts a generator function into a context manager. Code before yield performs setup. The yielded value becomes the as binding. Put cleanup in finally so it runs when the body succeeds and when the body raises. The generator must yield exactly once.

An exception from the body is thrown back into the generator at yield. If your generator catches that exception and does not re-raise it, the surrounding with statement treats it as handled. This is powerful but easy to misuse: logging an exception and forgetting to re-raise can make failed work appear successful.

tracked_scope.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

03Compose resources in reverse cleanup order

You can put multiple managers in one with statement, and they exit in reverse entry order. This mirrors nested scopes: an inner resource is released before the outer resource it may depend on. For a known pair of files, a normal nested or combined with is usually the clearest approach.

When the number of resources is determined at runtime, ExitStack manages their cleanup registrations. enter_context enters a manager immediately and schedules its exit. If opening a later file fails, already-opened files are still closed. This avoids hand-written loops of handles and partial-failure cleanup code.

many_files.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

04Cleanup is not automatic rollback

Closing a file does not undo the bytes already written. Deleting a temporary directory is a cleanup policy, while rolling back a transaction is a consistency policy. A context manager can implement either, but the with syntax alone promises neither a successful commit nor recovery of previous state.

Prefer existing standard-library managers for standard resources. Write a custom manager when setup and cleanup form one reusable concept, not merely to hide a few unrelated lines. Document whether it suppresses exceptions and whether the yielded object may be used after exit. A closed file handle or a deleted temporary path should not escape as a supposedly usable result.

LET IT CLICK

The ideas worth keeping.

  • Use with to tie resource lifetime to a visible scope.
  • Put generator-manager cleanup in finally.
  • Distinguish resource cleanup from rollback and exception suppression.
TURN UNDERSTANDING INTO SOMETHING YOU CAN DO

Make it yours.

Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.

01

Prove cleanup on failure

Build a context manager that appends entered and exited to a supplied event list. Raise inside its body and verify cleanup occurs without suppressing the error.

your_turn_1.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.pyPython
from contextlib import contextmanager@contextmanagerdef record_scope(events):    events.append("entered")    try:        yield    finally:        events.append("exited")events = []try:    with record_scope(events):        events.append("body")        raise ValueError("test")except ValueError:    events.append("caught")assert events == ["entered", "body", "exited", "caught"]

finally guarantees the exit event. Because the exception is not swallowed, the caller still observes the failure.

02

Understand unwind order

Create two managers that log entry and exit. Enter A then B with ExitStack and show that B exits first.

your_turn_2.py🐍Python
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.pyPython
from contextlib import ExitStack, contextmanager@contextmanagerdef resource(name, events):    events.append("enter " + name)    try:        yield name    finally:        events.append("exit " + name)events = []with ExitStack() as stack:    for name in ["A", "B"]:        stack.enter_context(resource(name, events))assert events == ["enter A", "enter B", "exit B", "exit A"]

ExitStack unwinds registrations in last-in, first-out order, matching nested with statements.

A SMALL QUESTION. A USEFUL NEXT STEP.

What stayed with you?

Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.

A LITTLE ACTIVE RECALL0 / 3 answered
01A contextmanager generator catches an exception from yield and then finishes normally. What does its caller observe?
02What does a file with block guarantee after its body fails?
03When is ExitStack especially useful?
⚡CODING CHALLENGE
🔒Required to Mark Lesson Complete

Prove cleanup on failure

Build a context manager that appends entered and exited to a supplied event list. Raise inside its body and verify cleanup occurs without suppressing the error.

EXPECTED OUTPUT
[Process exited with code 0 - No output]
challenge.pyPython 3
Ctrl + ↵
Execution Console
Click "Run & Verify" (or press Ctrl + Enter) to test your code against the challenge.

Keep exploring.

The official references behind this chapter, when you want a second angle or the full details.

MODULE 05 · PRACTICAL SELF-CHECK

Pause. Put the pieces together.

You’ve reached the end of Files, Data Formats, and Errors. Before moving on, use this task to check what you can do independently.

Test valid, missing, empty, and malformed input; explain which cases should succeed and which should fail.

Try it in your own environment and write down the result. This is a self-check; reading completion does not evaluate your code.

Review this module

One chapter closer.

Reading is a start. Trying it yourself makes the connection.

Solve the hands-on coding challenge in the knowledge section to verify your code and unlock chapter completion.