Context managers are Python’s answer to resource management — ensuring that files get closed, locks get released, and database connections get returned to the pool, even when exceptions occur. The with statement makes this pattern concise and reliable.

The with Statement

The most common context manager is open() for file handling:

with open("example.txt", "w") as file:
    file.write("Hello, World!")
# File is automatically closed here, even if write() raises an exception

Without with, you’d need a try/finally block:

file = open("example.txt", "w")
try:
    file.write("Hello, World!")
finally:
    file.close()

The with statement is cleaner, safer, and impossible to forget.

How Context Managers Work

A context manager is any object that implements two dunder methods:

  • __enter__() — called when entering the with block. Its return value is bound to the as variable.
  • __exit__(exc_type, exc_val, exc_tb) — called when leaving the with block, whether normally or via an exception.
class ManagedFile:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.file.close()
        return False  # Don't suppress exceptions

with ManagedFile("test.txt", "w") as f:
    f.write("Custom context manager!")

If __exit__ returns True, the exception is suppressed. Returning False (or None) lets it propagate — which is almost always what you want.

The contextlib Shortcut

Writing a full class for every context manager is verbose. The contextlib.contextmanager decorator lets you use a generator instead:

from contextlib import contextmanager

@contextmanager
def managed_file(filename, mode):
    file = open(filename, mode)
    try:
        yield file
    finally:
        file.close()

with managed_file("test.txt", "w") as f:
    f.write("Generator-based context manager!")

Everything before yield is the setup (__enter__), and everything after is the teardown (__exit__). The try/finally ensures cleanup happens even if the body raises.

Practical Examples

Database Connections

import sqlite3

with sqlite3.connect('mydatabase.db') as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM users')
    results = cursor.fetchall()
# Connection is automatically closed

Timing Code

import time
from contextlib import contextmanager

@contextmanager
def timer(label):
    start = time.perf_counter()
    yield
    elapsed = time.perf_counter() - start
    print(f"{label}: {elapsed:.4f} seconds")

with timer("Data processing"):
    total = sum(range(10_000_000))
# Output: Data processing: 0.1842 seconds

Thread Locks

import threading

lock = threading.Lock()

with lock:
    # Critical section — only one thread at a time
    shared_resource.update(new_data)
# Lock is automatically released

Temporary Directory

import tempfile
import os

with tempfile.TemporaryDirectory() as tmpdir:
    filepath = os.path.join(tmpdir, "data.txt")
    with open(filepath, "w") as f:
        f.write("temporary data")
    # Do work with the temp directory
# Directory and all contents are deleted automatically

Suppressing Exceptions

from contextlib import suppress

with suppress(FileNotFoundError):
    os.remove("file_that_might_not_exist.txt")
# No crash if the file doesn't exist

Multiple Context Managers

Python 3.10+ supports using parentheses for cleaner multi-line with statements:

with (
    open("input.txt") as infile,
    open("output.txt", "w") as outfile,
):
    for line in infile:
        outfile.write(line.upper())

When to Use Context Managers

Use them whenever you have setup/teardown pairs:

  • Opening and closing files
  • Acquiring and releasing locks
  • Connecting and disconnecting from databases
  • Starting and stopping timers
  • Creating and cleaning up temporary resources
  • Changing and restoring state (e.g., working directory, environment variables)

The pattern ensures cleanup happens reliably, which eliminates an entire category of resource leak bugs.


See also: