Context managers must follow a crucial algorithmic constraint: each execution path must yield exactly once. Multiple yields in the same path lead to unpredictable behavior, while no yields prevent proper resource management.
When implementing context managers with @contextlib.contextmanager:
Example of correct implementation:
@contextlib.contextmanager
def valid_context_manager(condition):
# Setup code
try:
if condition:
yield "success"
return # Early return prevents reaching the next yield
yield "alternative"
finally:
# Cleanup code always executed
print("Cleaning up")
This pattern ensures the algorithm’s invariant of “yield exactly once” regardless of which execution path is taken, maintaining reliable resource acquisition and release.