Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes
Python

Python Context Managers: Beyond the with Statement

Explore advanced context manager techniques in Python, including exception handling, ExitStack, ContextDecorator, and simplified creation with @contextmanager.

July 2026 5 min read 2 views 0 hearts

Here is the article you requested.


Python Context Managers: Beyond the with Statement

Most Python developers know the with statement. We use it for files, database connections, and locks. It’s clean, safe, and feels natural. But the context manager protocol itself is more powerful than just closing a file handle. Once you understand the mechanics behind with, you unlock a whole new way to write cleaner, more expressive code for Pythonskillset projects.

Let’s step past the basic open() example and see what else context managers can do for you.

The Protocol is Two Methods

The magic happens with __enter__ and __exit__. You probably know this. But what many miss is that __exit__ receives three arguments: the exception type, value, and traceback. This means a context manager can handle exceptions inside the with block, not just clean up.

Think about that for a second. You can write a context manager that suppresses certain exceptions, logs them, or even retries the operation. That’s not possible with a simple try/finally block without extra boilerplate.

class SuppressError:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is ValueError:
            print(f"Suppressed: {exc_val}")
            return True  # Suppress the error
        return False  # Let other errors propagate

with SuppressError():
    result = int("not_a_number")
print("Execution continues here")

The return True is the key. It tells Python "I handled this, move along." This is how contextlib.suppress works under the hood. You can build your own version tailored to your Pythonskillset application’s error handling needs.

Using contextlib.ContextDecorator for Clean Reuse

Writing a class with __enter__ and __exit__ is fine, but it gets verbose. Python’s contextlib module gives us ContextDecorator. This lets you reuse the same context manager as both a with statement and a function decorator. No duplication of logic.

from contextlib import ContextDecorator

class LogTiming(ContextDecorator):
    def __enter__(self):
        self.start = time.perf_counter()
        return self

    def __exit__(self, *exc):
        elapsed = time.perf_counter() - self.start
        print(f"Took {elapsed:.3f} seconds")
        return False

@LogTiming()
def slow_function():
    # Simulate work
    sum(range(10**6))

slow_function()
# Output: Took 0.012 seconds

You now have one definition that works both as a decorator and as a context manager. For a Pythonskillset API wrapper, you could wrap every external call with a timing context manager to monitor performance without cluttering your business logic.

Managing Multiple Resources Cleanly

The with statement can handle multiple context managers in one line. But it gets ugly fast.

with open('a.txt') as f1, open('b.txt') as f2:
    pass

Better is to use contextlib.ExitStack. This is a context manager that manages a dynamic stack of context managers. You can add resources inside a loop, which is impossible with the standard with syntax.

from contextlib import ExitStack

files = []
with ExitStack() as stack:
    for filename in ['a.txt', 'b.txt', 'c.txt']:
        f = open(filename)
        stack.push(f)
        files.append(f)
    # All files are automatically closed when exiting the stack

If opening one file fails, ExitStack correctly closes all previously opened files. This is a lifesaver when dealing with variable numbers of resources, like connecting to multiple databases or opening multiple network sockets in a scraper.

Redirecting Standard Output Temporarily

Here’s a practical trick for testing or logging. You can write a context manager that temporarily redirects stdout to a string buffer, then restore it when done.

from io import StringIO
import sys

class RedirectStdout:
    def __enter__(self):
        self._original_stdout = sys.stdout
        self._string_io = StringIO()
        sys.stdout = self._string_io
        return self._string_io

    def __exit__(self, *exc):
        sys.stdout = self._original_stdout

with RedirectStdout() as output:
    print("This goes to the buffer")
    print("So does this")

# output.getvalue() now contains printed text

You can use this for capturing logs generated by third-party libraries during testing, or for building a silent mode in your Pythonskillset tool.

Writing Your Own Context Manager with contextmanager

Finally, the easiest way to write most context managers is with @contextmanager from contextlib. You write a generator with a yield in the middle.

from contextlib import contextmanager

@contextmanager
def temp_change_dir(new_dir):
    original = os.getcwd()
    os.chdir(new_dir)
    try:
        yield
    finally:
        os.chdir(original)

with temp_change_dir('/tmp'):
    # Do work in /tmp
    pass
# Automatically back to original directory

This reads like a story. Setup, yield, teardown. No classes, no boilerplate. For Pythonskillset projects where you need local file operations or temporary environment variables, this pattern is clean and maintainable.

Final Thought

Context managers are not just for files. They are a design pattern for any resource that has a clear "start" and "stop". Once you start seeing these boundaries in your code—database connections, timing, temporary changes, error suppression—you can replace scattered try/finally blocks with readable, reusable context managers. Your future self and your colleagues will thank you.

Next time you write a function that sets something up and tears it down, ask yourself: could this be a context manager? The answer is almost always yes.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.