with statements & contextlib.contextmanager
Clean up with context managers
Lesson 14 of 20 · 12 min · Module 4: When things go wrong
Some actions must always be undone: closing a file, releasing a lock, restoring a setting. A with block guarantees the cleanup runs, even when an error happens inside.
Start this lesson →Free. No installation: you write and run the code in your browser, and it is checked automatically.
By the end, you’ll be able to…
- Use with blocks for guaranteed cleanup
- Write a context manager with contextmanager
- Distinguish "missing" from None
The idea
@contextmanager from contextlib turns a generator into a context manager: code before yield runs when the block starts, and code in a finally after it runs when the block ends, whether it ended normally or with an exception.
from contextlib import contextmanager
@contextmanager
def announce(name):
print("start", name)
try:
yield
finally:
print("end", name)
with announce("backup"):
print("working")
01with guarantees cleanup
with open("data.txt") as f closes the file even if your code crashes halfway. Context managers bring the same safety to any setup-and-teardown pair.
02yield splits setup and cleanup
Code before yield is the setup. The body of the with block runs at the yield. finally runs afterwards in every case, including exceptions.
from contextlib import contextmanager
@contextmanager
def step(name):
print("begin", name)
try:
yield
finally:
print("done", name)
with step("deploy"):
print("…")begin deploy
…
done deploy03Sentinels
mapping.get(key) returns None for a missing key and for a stored None. A private object() is equal only to itself, so it reliably marks "there was no value".
Your turn
temporary_value(mapping, key, value). Inside the with block, mapping[key] equals value. Afterwards, the old value is restored, or the key is removed if it did not exist before. This must also happen when the block raises an exception, and the exception must not be swallowed.What the checks look for
- Sets the value inside the block and restores it after
- Removes a key that did not exist before
- Restores even when the block raises, and does not swallow the error
- Keeps a stored None value
Take this with you
Context managers pair setup with guaranteed cleanup.
Go a little further
Write a context manager timer() that measures how long the with block took using time.perf_counter().