Master Code
On The Go

Learn. Practice. Build.

Home › Code Snippets › Python Snippets › Context Managers

Python Snippet

Python Context Managers

The with statement is more powerful than you think. Learn to build your own.

Last updated: October 2026

Quick answer: A context manager defines setup and cleanup around a block of code. Use the with statement to run it — cleanup runs even if an exception occurs. Build one by implementing __enter__ and __exit__, or use the @contextmanager decorator on a generator function for a shorter version.

🎬 Interactive Live Demo

Click through the tabs to see different context manager patterns and their simulated execution traces.

💡 Switch between tabs to see how setup/cleanup flows through each pattern.

1

The with Statement — Why It Matters

Without with, resource cleanup is error-prone:

f = open("data.txt", "r")
try:
    content = f.read()
    print(content)
finally:
    f.close()

With with, cleanup is guaranteed and the code is cleaner:

with open("data.txt", "r") as f:
    content = f.read()
    print(content)

The file closes even if f.read() raises an exception. That guarantee is the entire point of context managers.

2

Class-Based Context Manager

Implement two methods: __enter__ (setup) and __exit__ (cleanup):

import time

class Timer:
    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        elapsed = time.time() - self.start
        print(f"Elapsed: {elapsed:.3f}s")
        # Returning False (or None) propagates any exception
        return False

with Timer():
    total = sum(range(1_000_000))
# Elapsed: 0.015s

Key details:

  • __enter__ returns whatever the as variable gets.
  • __exit__ receives exception info if one occurs.
  • Returning True from __exit__ suppresses the exception.
  • Returning False (or nothing) propagates it.
3

Generator-Based Context Manager

The @contextmanager decorator turns a generator into a context manager. Cleaner for simple cases:

from contextlib import contextmanager

@contextmanager
def tag(name):
    print(f"<{name}>")
    yield
    print(f"</{name}>")

with tag("p"):
    print("Hello")
# <p>
# Hello
# </p>

The pattern: Code before yield is setup. Code after yield is cleanup. Use try/finally if the block can raise:

@contextmanager
def transaction(db):
    db.begin()
    try:
        yield db
        db.commit()
    except Exception:
        db.rollback()
        raise
4

Handling Exceptions in __exit__

When an exception occurs inside the with block, Python passes the details to __exit__:

class Suppressor:
    def __init__(self, *exceptions):
        self.exceptions = exceptions

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            return False
        # Suppress only the listed exception types
        return issubclass(exc_type, self.exceptions)

with Suppressor(ZeroDivisionError):
    result = 1 / 0  # Silently ignored
print("Continues here")

This is exactly how contextlib.suppress works internally.

5

contextlib — The Built-In Helpers

Python ships four context manager helpers you should know:

from contextlib import suppress, closing, ExitStack, redirect_stdout

# 1. suppress — ignore specific exceptions
with suppress(FileNotFoundError):
    open("missing.txt")

# 2. closing — auto-close any object with .close()
from urllib.request import urlopen
with closing(urlopen("https://example.com")) as page:
    data = page.read()

# 3. ExitStack — dynamic number of managers
with ExitStack() as stack:
    files = [stack.enter_context(open(f)) for f in filenames]
    # All files closed on exit, in reverse order

# 4. redirect_stdout — capture prints
import io
buffer = io.StringIO()
with redirect_stdout(buffer):
    print("Captured!")
print(buffer.getvalue())  # "Captured!\n"
6

Real-World Use Case: Database Transactions

from contextlib import contextmanager

@contextmanager
def transaction(connection):
    cursor = connection.cursor()
    try:
        yield cursor
        connection.commit()
    except Exception:
        connection.rollback()
        raise
    finally:
        cursor.close()

# Usage
with transaction(db) as cursor:
    cursor.execute("UPDATE users SET name = ? WHERE id = ?", ("Alice", 1))
    cursor.execute("UPDATE accounts SET balance = balance - 100 WHERE user_id = ?", (1,))
# Commits automatically on success, rolls back on any exception

This pattern appears in nearly every production Python codebase. It's the reason with exists.

🎯 Class vs @contextmanager — When to Use Each

Aspect Class-Based @contextmanager
Boilerplate More Less
Reusability Better (can subclass) Limited
Complex state Handles well Possible but awkward
Best for Reusable utilities One-off setup/cleanup

⚠️ Common Mistakes

❓ Frequently Asked Questions

What is a context manager in Python?

A context manager is an object that defines setup and cleanup behavior for a block of code. It is used with the with statement, which guarantees cleanup runs even if an exception is raised.

What does the with statement do?

The with statement calls __enter__ on the object to set up resources, runs the block, then calls __exit__ to clean up. Even if the block raises an exception, __exit__ is called.

What is the difference between class-based and generator-based context managers?

Class-based managers implement __enter__ and __exit__ methods and offer more control. Generator-based managers use the @contextmanager decorator and are more concise. Use @contextmanager for simple cases.

What does contextlib.suppress do?

contextlib.suppress is a context manager that silently ignores specific exceptions. It replaces try/except/pass blocks and makes intent explicit.

Can I have multiple context managers in one with statement?

Yes. Use with open('a') as a, open('b') as b: to chain multiple context managers. They are entered left-to-right and exited right-to-left, similar to how nested decorators work.

🎯 What's Next?

← All Python Snippets