Lesson 17 · Python standard library deep dive
Python contextlib Explained: Custom Context Managers | Standard Library #17
Video seventeen of the twenty-five-part series: contextlib, for building and combining context managers. The @contextmanager decorator, closing, suppress,…
- CoursePython standard library deep dive
- Lesson17 of 24
- Video17 min
- FormatJupyter notebook · 12 code cells
What you'll learn
Data
No separate download needed — the notebook creates or downloads everything it uses.
📓 Full notebook
Download .ipynbPython Standard Library Deep-Dive, Video 17: contextlib#
- Video seventeen of the twenty-five-part series: contextlib, for building and combining context managers.
- The @contextmanager decorator, closing, suppress, redirect_stdout, ExitStack, and nullcontext.
- Let's get into it.
Part 1: Recap of the with Protocol#
class Timer:
def __enter__(self):
print('starting')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('stopping')
return False
with Timer():
print('doing work')
Part 2: @contextmanager - Generator-Based Context Managers#
from contextlib import contextmanager
@contextmanager
def timer():
print('starting')
yield 'timer-object'
print('stopping')
with timer() as t:
print(f'doing work with {t}')
Part 3: Exception Handling Inside @contextmanager#
@contextmanager
def resource_guard(name):
print(f'acquiring {name}')
try:
yield name
finally:
print(f'releasing {name}')
try:
with resource_guard('db-connection'):
print('using resource')
raise ValueError('something broke')
except ValueError as e:
print(f'caught: {e}')
Part 4: contextlib.closing()#
from contextlib import closing
class Connection:
def __init__(self, name):
self.name = name
self.open = True
def close(self):
self.open = False
print(f'{self.name} closed')
with closing(Connection('conn-a')) as conn:
print(conn.open)
print(conn.open)
Part 5: contextlib.suppress()#
from contextlib import suppress
import os
with suppress(FileNotFoundError):
os.remove('file_that_does_not_exist.txt')
print('continued past the missing file')
try:
with suppress(FileNotFoundError):
result = 10 / 0
except ZeroDivisionError:
print('ZeroDivisionError was NOT suppressed, exactly as expected')
Part 6: redirect_stdout and redirect_stderr#
import io
from contextlib import redirect_stdout
buffer = io.StringIO()
with redirect_stdout(buffer):
print('this is captured, not printed')
print('so is this')
captured = buffer.getvalue()
print(f'captured text: {captured!r}')
Part 7: ExitStack - a Dynamic Number of Context Managers#
from contextlib import ExitStack
names = ['a', 'b', 'c']
with ExitStack() as stack:
resources = [stack.enter_context(resource_guard(name)) for name in names]
print(resources)
with ExitStack() as stack:
stack.callback(print, 'cleanup one')
stack.callback(print, 'cleanup two')
print('doing work inside the stack')
Part 8: nullcontext#
from contextlib import nullcontext
def process(data, lock=None):
ctx = lock if lock is not None else nullcontext()
with ctx:
return sum(data)
print(process([1, 2, 3]))
import threading
real_lock = threading.Lock()
print(process([4, 5, 6], lock=real_lock))
Part 9: Using a Context Manager as a Decorator#
@contextmanager
def logged(label):
print(f'[{label}] entering')
yield
print(f'[{label}] exiting')
@logged('demo-call')
def do_something():
print('inside do_something')
do_something()
Part 10: A Real Pattern - Temporarily Changing State#
import os
@contextmanager
def temporary_directory_change(path):
original = os.getcwd()
os.chdir(path)
try:
yield
finally:
os.chdir(original)
print(os.getcwd())
with temporary_directory_change('/tmp'):
print(os.getcwd())
print(os.getcwd())
Wrap-Up: What You Learned#
- with calls enter on entry and exit on exit, guaranteed even when an exception occurs.
- @contextmanager turns a single generator into a full context manager; before yield is enter, after yield is exit.
- Wrapping yield in try/finally guarantees cleanup runs even if the with-block raises.
- closing() adds context-manager behavior to any object that only has a close() method.
- suppress() silently ignores only the specific exception types listed, nothing else.
- redirect_stdout/redirect_stderr temporarily send output to any file-like object, like an in-memory buffer.
- ExitStack manages a dynamic, runtime-determined number of context managers and plain cleanup callbacks.
- nullcontext is a genuine do-nothing context manager for conditionally optional context managers.
- Any @contextmanager-built object also works directly as a function decorator.
- A real pattern: temporarily changing state (like a working directory) with guaranteed restoration.
- That wraps up contextlib. Next up: dataclasses, for cutting down on class boilerplate.
Found this useful?
All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.



