Python Context Manager Practice Guide
Cleanup is easy to forget on the success path and even easier to miss when an exception interrupts the function. A context manager puts setup and teardown around a block so the cleanup has one clear home.
Use with for paired operations
The with statement opens a resource, runs a block, and closes it when the block ends. Files are the canonical example, but the same pattern applies to locks, temporary directories, and transactions. Keep the block focused on the work that needs the resource.
from pathlib import Path
path = Path("pypractice-guide.txt")
path.write_text("hello\n", encoding="utf-8")
with path.open(encoding="utf-8") as handle:
print(handle.read().strip())
path.unlink()A managed file
Implement the protocol
__enter__ prepares the resource and returns the value bound by as. __exit__ receives the exception type, value, and traceback, then performs cleanup. A false return value lets an exception propagate; a true value suppresses it.
class Guard:
def __enter__(self):
print("enter")
return self
def __exit__(self, exc_type, exc, tb):
print("exit")
with Guard():
print("inside")Setup and teardown
Cleanup still runs on error
The body does not need a try/finally around every resource operation. If the body raises, __exit__ is still called before the exception continues. That is the guarantee that makes with useful for correctness, not just brevity.
class Guard:
def __enter__(self):
return self
def __exit__(self, *args):
print("cleaned")
try:
with Guard():
raise ValueError("boom")
except ValueError:
print("caught")Cleanup before the error escapes
Build one with contextmanager
Decorate a generator with contextlib.contextmanager. Code before yield is setup; code after yield is cleanup. The yielded value is available through as. Keep exactly one yield and put cleanup after it.
from contextlib import contextmanager
@contextmanager
def label(name):
print("start", name)
yield name
print("end", name)
with label("demo") as value:
print("body", value)A generator-based manager
Do not hide exceptions by accident
Returning True from __exit__ suppresses the exception. That can be useful for a deliberate policy, but it can also hide bugs. Return None or False when the caller should see the error. Document any suppression clearly.
class Visible:
def __enter__(self):
return self
def __exit__(self, *args):
return False
with Visible():
print("still visible")Let errors propagate
Your practice checklist
- Use with when setup and cleanup belong together.
- Return the useful object from __enter__.
- Put cleanup in __exit__, not only on the success path.
- Use contextlib.contextmanager for small generator-based managers.
- Return False or None unless suppression is intentional.
Common questions
Is with only for files?
No. Any object implementing the context manager protocol can be used, including locks, database transactions, and test fixtures.
Can a context manager suppress an exception?
Yes, if __exit__ returns a true value. Use that sparingly and document it.
What happens if __enter__ raises?
The body is not entered, and __exit__ is not called. The exception propagates to the caller.
Practice context managers
12 questions on with, __enter__, __exit__, cleanup, and contextlib.
Keep reading: