A realistic context manager: commit or roll back
Databases and payment code use exactly this shape: change things inside the block, keep the changes if it finishes, undo them if it fails. The except branch undoes the change and then re-raises, so the caller still sees the error.
contextlib lets you write your own with blocks using a generator and @contextmanager, and gives tools for common jobs: suppress to ignore an error, ExitStack to manage a changing number of resources, closing, redirect_stdout and nullcontext.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 6 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write a @contextmanager named banner(title) that prints === title === before the block and === end === after it (in finally). Use it around one print.
Use suppress so that deleting a missing key from a dictionary does not raise: del data["zzz"], then print data.
Write a @contextmanager named temporary_value(box, key, value) that sets box[key] = value for the block and restores the old value afterwards. Show the dictionary before, inside and after.
Explain it without notes
What does the with statement guarantee, and how do __enter__ and __exit__ fit in?
How does @contextmanager turn a generator into a context manager, and what must you do around the yield?
When would you use ExitStack?
What do suppress, nullcontext and closing each do?
Expected output
committed
after success: {'balance': 70}
rolled back because: insufficient funds
after failure: {'balance': 70}