Free tools Windows power users keep installed
One-click scans. No signup required.
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A Python context manager wraps a block of code with setup and cleanup. The with statement calls its entry method, runs the block, and then calls its exit method—even when the block raises an exception, as long as entry completed successfully. Use this pattern for files, locks, temporary state, and other resources that need a defined lifetime.
What a context manager does
A context manager establishes a runtime context around a with block. A common example is opening a file: the file is available inside the block and is closed when the block ends. Context managers can also acquire and release locks or save and restore state. The Python Language Reference describes the precise behavior of with.
with open("notes.txt", "r", encoding="utf-8") as file:
contents = file.read()
Read this as “enter the file’s managed lifetime, use it, then leave that lifetime.” The cleanup is not merely a line that runs after the block in the normal case; it is part of the statement’s exit behavior.
How with calls __enter__ and __exit__
For with expression as target:, Python evaluates the expression, obtains the manager’s special methods, and calls __enter__(). If an as target is present, Python binds the value returned by __enter__() to it. After the block, Python calls __exit__().
#1 Best Overall
The Python Language Reference guarantees that if __enter__() returns without an error, __exit__() will be called. If entry itself fails, the block is not entered and this guarantee does not apply.
- If the block finishes normally,
__exit__()receivesNonefor the exception type, value, and traceback. - If an exception leaves the block,
__exit__()receives its type, value, and traceback. - A truthy return from
__exit__()suppresses the exception. A false value, includingNone, lets it propagate.
Therefore, returning a truthy value is a deliberate decision to handle an exception, not a default cleanup behavior. Most managers should allow unexpected exceptions to propagate.
Rank #2
Implement a manager as a class
Define __enter__() and __exit__() when the manager needs to hold state, has multiple operations, or benefits from explicit methods that reviewers can inspect.
class ManagedValue:
def __enter__(self):
self.value = acquire_value()
return self.value
def __exit__(self, exc_type, exc_value, traceback):
release_value(self.value)
return False
with ManagedValue() as value:
use(value)
This is an illustrative pattern: replace the placeholder function names with real acquisition, use, and release operations. Returning False makes clear that exceptions from the block should not be suppressed. Returning None has the same effect.
The object returned from __enter__() is what the optional as target receives; it need not be the manager object itself. Python performs the protocol lookup as part of the with statement.
Write a generator-based manager with @contextmanager
For a simpler lifetime that naturally reads as “set up, yield the resource, clean up,” use contextlib.contextmanager. Put acquisition before the single yield and cleanup in a finally block.
from contextlib import contextmanager
@contextmanager
def managed_resource():
resource = acquire_resource()
try:
yield resource
finally:
release_resource(resource)
with managed_resource() as resource:
use(resource)
The decorated function must produce a generator iterator that yields exactly once. Code before yield runs as the block is entered; code after it resumes as the block exits. The finally clause ensures the release operation is reached during normal exit and exception unwinding.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →If the block raises an exception that is not suppressed, contextmanager raises it at the yield point inside the generator. If you catch it only to log or perform cleanup, re-raise it; otherwise the generator can make the manager treat it as handled.
Best Value
Choose the class or generator pattern
| Consideration | Class-based manager | @contextmanager |
|---|---|---|
| Shape of the logic | Entry and exit are separate methods; useful when each has substantial logic. | Setup and cleanup surround one yield; concise for a single clear lifetime. |
| State and branching | Can retain state on the manager instance and represent multiple steps explicitly. | Local variables around the yield often keep simple state compact. |
| Exception handling | Inspect exception arguments in __exit__(); return truthy only to suppress intentionally. |
Handle exceptions around yield; re-raise those that should propagate. |
| Reuse, reentrancy, or async work | Can be designed for these needs, but they are not guaranteed by the protocol. | Generator-based managers should generally be recreated for each use; asynchronous lifetimes use a separate API. |
Neither implementation is universally better. Prefer the form that makes resource ownership, cleanup, and exception behavior clearest. Consult the official contextlib documentation for the standard-library utilities and their version-specific details.
Useful tools in contextlib
closing(obj)callsobj.close()when the block exits, useful for objects that haveclose()but do not implement the context-manager protocol. Prefer native support when available; for example,urlopen()already works withwith.nullcontext(value)is a no-op manager whose entry value isvalue. It can simplify a function that accepts either an already-open resource or a path from which it opens one.suppress(SomeSpecificError)ignores only the listed exception types. Use it only when continuing after those specific errors is known to be correct.ExitStackmanages a dynamic set of contexts and cleanup callbacks. It unwinds them in reverse registration order, like nestedwithstatements, and is useful when resource count is determined at runtime or a later acquisition might fail.
Use asynchronous context managers for awaited setup or cleanup
async with is a distinct protocol for asynchronous code. It uses __aenter__() and __aexit__(); contextlib.asynccontextmanager provides the generator-based form. Use these when entering or exiting the context must await asynchronous work, rather than treating synchronous with and asynchronous setup as interchangeable.
Quick Recap
Common mistakes to avoid
- Assuming cleanup suppresses errors: cleanup and suppression are separate. Unless the exit method returns truthy, the exception propagates.
- Forgetting
finallyin a generator manager: put essential release logic there so it runs when the block exits exceptionally. - Swallowing an exception after logging: re-raise it unless suppression is intentional.
- Reusing every manager instance: reuse and reentrancy depend on the particular manager. The documentation specifically cautions that file objects and managers produced by
@contextmanagershould be recreated for each use. - Using a helper without checking Python compatibility: the current Python documentation covers 3.14.8 and records version additions or changes for individual helpers. Check the documentation for the Python version your program supports before depending on a helper.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

