contextlib.contextmanager lets you define with-block behavior using a function with yield.
Code before yield runs on enter, and code in finally runs on exit.
In C#, this is closest to using / IDisposable lifecycle control.
Python can do class-based context managers too, but @contextmanager provides a compact function-based style.
banner("deploy") prints start:deploy, then yields control to the with block.
Inside the block, inside is printed.
When the block exits, the finally section runs and prints end:deploy.
This happens even if an exception occurs inside the block.
Use this for setup/cleanup boundaries such as timing, tracing, temporary environment changes, lock management, or transactional wrappers. It keeps resource and lifecycle logic centralized.
Forgetting try/finally around yield, which can skip cleanup on errors.
Putting too much business logic inside the context manager instead of keeping it focused on lifecycle concerns.
from contextlib import contextmanager
@contextmanager
def banner(label: str):
print(f"start:{label}")
try:
yield
finally:
print(f"end:{label}")
def main() -> None:
with banner("deploy"):
print("inside")
if __name__ == "__main__":
main()uv run src/csharp_to_python_learning/python_only_features/feature_04_contextlib_magic.pystart:deployinsideend:deploy