A context manager owns entry and exit behavior around a with block, including cleanup during exceptions.
Python context managers: clean up on success and failure
Operation contract
The staging context records entry, yields its temporary list, and clears that list in finally. The caller raises an import error, catches it outside the context, then checks that cleanup happened and retained state is empty. The original error is not swallowed to make the import appear successful.
Failure and ownership boundary
The fixture owns only a process-local list. File, database and network contexts have their own close and transaction semantics; closing a resource is not the same as rolling back completed external work. Python sqlite3 transactions: bind values and roll back failed batches and Python pathlib files: specify encoding and close the resource owner need those rules stated separately.
Working program
from contextlib import contextmanager
@contextmanager
def staging_buffer(events):
staged = []
events.append("opened")
try:
yield staged
finally:
staged.clear()
events.append("closed")
events = []
try:
with staging_buffer(events) as retained:
retained.append("R41")
raise ValueError("import failed")
except ValueError:
print("failure preserved")
print(events)
print(retained)Output
failure preserved
['opened', 'closed']
[]Costs and limits
The list retains k staged values until exit and cleanup releases its references. The context cannot undo external side effects that occurred before failure.
Common Mistakes
- Do not suppress an error unless that is the public contract.
- Cleanup and transactional rollback are different operations.
Connected lessons
Python exceptions: translate an input error without hiding its cause, Python sqlite3 transactions: bind values and roll back failed batches, Java try-with-resources: close order and suppressed failures.
Related Python operation checks
Python ExitStack: unwind partially acquired resources.
Follow the ownership and update boundary
Python ExitStack pop_all: transfer cleanup without discarding it.
Check this related boundary
Python context manager exit: returning true can hide a failure.
