Python Context Managers & contextlib

๐Ÿ Python 3.12+ ๐ŸŸข Chapter 38 of 65 ๐Ÿ“‚ Phase 8: Advanced Python ๐Ÿ“… 2026 Edition
๐Ÿ“Œ Covered in this chapter: __enter__() and __exit__() Protocol ยท Suppressing Exceptions ยท @contextmanager Generator ยท Database Transaction & Timer Contexts
Master resource lifecycle management with Python Context Managers: building class-based context managers with __enter__ and __exit__, handling exceptions gracefully, and using the contextlib.contextmanager generator decorator for clean resource management.
1The Context Manager Protocol (__enter__ and __exit__)

The Python Context Manager Protocol powers the with statement, guaranteeing deterministic resource acquisition and release.

A class implements the protocol by defining two methods:

  1. __enter__(self): Runs when entering the with block. Its return value is bound to the variable after as.
  2. __exit__(self, exc_type, exc_val, exc_tb): Runs when exiting the with block. If an exception occurred inside the block, its details are passed into these arguments. Returning True from __exit__ suppresses the exception; returning False allows it to propagate!
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ with ManagedResource() as res: โ”‚ โ”‚ 1. __enter__() is executed (Allocates resource) โ”‚ โ”‚ 2. Body code executes โ”‚ โ”‚ 3. __exit__() is executed (Releases resource 100%)โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
๐Ÿ’ป Example 1: Class-based Context Manager with __enter__ and __exit__
# Class-based Custom File / Resource Context Manager:
class ManagedFile:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None

    def __enter__(self):
        print(f"๐Ÿ”“ [__enter__] Opening file: {self.filename}")
        self.file = open(self.filename, self.mode, encoding="utf-8")
        return self.file # Bound to 'as f'

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f"๐Ÿ”’ [__exit__] Closing file: {self.filename}")
        if self.file:
            self.file.close()
        # Returning False allows any exception inside the block to propagate properly
        return False

# Using custom context manager:
with ManagedFile("demo_context.txt", "w") as f:
    f.write("Hello from custom ManagedFile context manager! ๐Ÿš€\n")

print("Is file closed?", f.closed) # True!
๐Ÿ” Resource Safety:

Even if a divide-by-zero or network error occurs inside the with block, __exit__ is guaranteed to execute and close the file descriptor.

2The contextlib.contextmanager Generator Decorator

Writing a full class with __enter__ and __exit__ for small tasks can feel verbose. The standard library @contextlib.contextmanager decorator turns any Python generator with a single yield into a complete context manager!

1. Code before yield ==> Runs during __enter__ 2. Value in yield ==> Bound to 'as variable' 3. Code after yield ==> Runs during __exit__ (inside finally!)
๐Ÿ’ป Example 2: Elegant Generator Context Manager with contextlib
import time
from contextlib import contextmanager

# 1. Performance Stopwatch Context Manager in 8 lines:
@contextmanager
def code_timer(label="Operation"):
    start = time.perf_counter()
    print(f"โณ [{label}] Started...")
    try:
        yield # Body of with block executes here!
    finally:
        end = time.perf_counter()
        duration_ms = (end - start) * 1000
        print(f"โŒ› [{label}] Finished in {duration_ms:.3f} ms")

# Using the generator context manager:
with code_timer("Data Processing Pipeline"):
    # Simulate data computation:
    results = [x ** 2 for x in range(300_000)]
    time.sleep(0.05)
๐Ÿ” Mandatory try-finally Rule:

Always place the code after yield inside a finally: block to guarantee cleanup runs even if an exception occurs inside the with block.

โš ๏ธ Common Developer Pitfall: Omitting try-finally in @contextlib.contextmanager Generators

If you write code after yield without wrapping it in a try-finally block, an exception inside the caller's with block will prevent your cleanup code from executing at all!

๐Ÿ’ป Hands-on Interactive Practice Challenge

Build a context manager temporary_state(data_dict, key, temp_val) that sets a key to a temporary value inside the block and automatically restores the original value upon exit.

Python 3 Practice Challenge โ–ถ Run in Compiler
from contextlib import contextmanager

@contextmanager
def temporary_setting(config_dict, key, temp_val):
    original_val = config_dict.get(key)
    config_dict[key] = temp_val
    try:
        yield
    finally:
        config_dict[key] = original_val

settings = {"debug": False, "env": "production"}
print("Initial Settings:", settings)

with temporary_setting(settings, "debug", True):
    print("Inside with block:", settings)

print("Outside with block:", settings)
Run This Challenge in Online Python IDE โ†’
โ“ Frequently Asked Questions (FAQ)

Q How can a context manager suppress an exception?

In a class-based context manager, returning True from __exit__() informs Python that the exception has been handled and should not propagate.

Q Can multiple context managers be combined in a single with statement?

Yes! You can chain multiple context managers with commas: with open("in.txt") as f_in, open("out.txt", "w") as f_out:.

Q What is contextlib.suppress in Python?

contextlib.suppress(*exceptions) is a built-in context manager that silently suppresses specified exceptions: with contextlib.suppress(FileNotFoundError): os.remove("file.tmp").

OC
Written by Our Compiler Technical Editorial Team
Reviewed for accuracy & tested on Python 3.12+ runtime ยท Last updated August 2026