Examples of Context Managers in Python: 3 Practical Patterns You’ll Actually Use

If you’ve written Python for more than a week, you’ve already seen context managers in action — even if you didn’t know their name. The classic `with open(...) as f:` pattern is one of the most common examples of context managers in Python: 3 practical examples can already be found in the standard library alone. But context managers go way beyond files. They’re a clean way to guarantee setup and teardown logic runs correctly, even when your code throws exceptions. In this guide, we’ll walk through several real examples of context managers in Python, starting with the familiar ones and then building up to your own custom `with` blocks. We’ll talk about how they work under the hood, when to write your own, and how modern Python (3.10–3.12) has made them easier to use. Along the way, you’ll see how context managers simplify resource management, database work, testing, and even performance profiling — all using readable, idiomatic Python.
Written by
Jamie
Published

Let’s skip the theory and start with code. When people ask for examples of context managers in Python: 3 practical examples come up again and again:

  • Managing files
  • Managing database transactions
  • Managing temporary state (like changing directories or environment variables)

From those three patterns, you can build a surprising number of real examples.


Pattern 1: File and resource management (the classic example of a context manager)

The most familiar example of a context manager is file handling:

with open("data.txt", "r", encoding="utf-8") as f:
    for line in f:
        process(line)

Here, open returns a context manager. When the with block exits, Python guarantees f.close() is called, even if process(line) raises an exception.

This pattern generalizes nicely to other resources that need to be opened and then reliably closed:

import gzip
from pathlib import Path

path = Path("logs/data.log.gz")

with gzip.open(path, mode="rt", encoding="utf-8") as gz_file:
    for line in gz_file:
        handle_log(line)

More real examples of file-like context managers

You’ll see similar examples include:

import tarfile

with tarfile.open("archive.tar.gz", "r:gz") as tar:
    tar.extractall("./extracted")

Or for working with JSONL logs in 2024-style data pipelines:

import json

with open("events.jsonl", "r", encoding="utf-8") as f:
    events = [json.loads(line) for line in f]

These are some of the best examples of context managers in Python because they illustrate the core idea: acquire a resource, use it safely, and release it automatically.


Pattern 2: Database transactions as real examples of context managers in Python

Modern Python codebases lean heavily on context managers for database work. This is one of the best examples of context managers in Python: 3 practical examples often start with transactions, connections, and sessions.

Example: SQLite transaction context manager

import sqlite3

conn = sqlite3.connect("app.db")

with conn:  # context manager
    conn.execute("INSERT INTO users (name) VALUES (?)", ("Alice",))
    conn.execute("INSERT INTO users (name) VALUES (?)", ("Bob",))

In this example, conn acts as a context manager. On success, it commits; on error, it rolls back. No manual conn.commit() or conn.rollback() calls scattered around your code.

Example: SQLAlchemy session context manager (2024-style)

With SQLAlchemy 2.x, the recommended pattern is even more context-manager-heavy:

from sqlalchemy import create_engine, text
from sqlalchemy.orm import Session

engine = create_engine("sqlite:///app.db", echo=False)

with Session(engine) as session:  # context manager
    with session.begin():         # transaction context manager
        session.execute(text("INSERT INTO users (name) VALUES (:name)"), {"name": "Carol"})

Here you see two nested examples of context managers in Python:

  • Session(engine) manages the lifecycle of the session object.
  • session.begin() manages the transaction (commit/rollback).

This pattern is common in production apps in 2024 and beyond because it keeps transaction boundaries explicit and safe.

For broader background on why transactional integrity matters, the U.S. National Institute of Standards and Technology (NIST) has long-standing guidance on database reliability and ACID properties (see: nist.gov). While they’re not Python-specific, the same principles motivate using context managers for database work.


Pattern 3: Temporary state and configuration (one of the best examples of custom context managers)

Another category where examples of context managers in Python: 3 practical examples really shine is temporary state: things you want to tweak for a short period and then restore.

Example: Temporarily changing the working directory

import os
from contextlib import contextmanager

@contextmanager
def working_directory(path: str):
    prev_cwd = os.getcwd()
    os.chdir(path)
    try:
        yield
    finally:
        os.chdir(prev_cwd)


## Usage
with working_directory("/tmp"):
    run_some_task()
## back to original directory here

This is a custom context manager created using the @contextmanager decorator from contextlib. It’s a clean example of how to manage setup (chdir) and teardown (restore previous directory) in a single, readable block.

Example: Temporarily overriding environment variables

import os
from contextlib import contextmanager

@contextmanager
def temp_env(var: str, value: str):
    old_value = os.environ.get(var)
    os.environ[var] = value
    try:
        yield
    finally:
        if old_value is None:
            os.environ.pop(var, None)
        else:
            os.environ[var] = old_value


## Usage
with temp_env("APP_ENV", "test"):
    run_tests_against_test_env()

This is one of those real examples of context managers in Python that shows up constantly in test suites and CI pipelines.


How context managers work under the hood

So far we’ve walked through several examples of context managers in Python: 3 practical examples and more. Underneath the with statement, Python looks for two methods on the context manager object:

  • __enter__(self) – called at the start of the with block
  • __exit__(self, exc_type, exc_val, exc_tb) – called at the end, even if an exception occurs

Example: A manual timer context manager (class-based)

import time
from typing import Optional

class Timer:
    def __init__(self, label: str = ""):
        self.label = label
        self.start: Optional[float] = None

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        end = time.perf_counter()
        duration = end - (self.start or end)
        print(f"{self.label} took {duration:.4f} seconds")
#        # Returning False means exceptions are not suppressed
        return False


## Usage
with Timer("heavy computation"):
    result = expensive_function()

This is a clear example of a custom context manager that doesn’t manage I/O or state, but still provides a nice abstraction. In 2024, this pattern is still widely used for quick performance profiling, even as external profilers and observability tools get better.


Using contextlib for lighter-weight examples of context managers in Python

Python’s contextlib module gives you shortcuts for writing context managers without defining a full class.

Example: Timer with @contextmanager

from contextlib import contextmanager
import time

@contextmanager
def timer(label: str = ""):
    start = time.perf_counter()
    try:
        yield
    finally:
        end = time.perf_counter()
        print(f"{label} took {end - start:.4f} seconds")


with timer("load data"):
    load_data()

This is one of the best examples of context managers in Python when teaching newcomers: it’s short, it’s clear, and it shows the try/finally pattern that context managers are really encoding.

Example: suppress for ignoring specific exceptions

contextlib.suppress is a handy standard-library context manager:

from contextlib import suppress

with suppress(FileNotFoundError):
    cleanup_temp_file("/tmp/app.lock")

This is a real example of a context manager that’s ideal in scripts and maintenance tasks: you want to try something, but you don’t care if a specific exception happens.

The Python documentation has a solid reference for these patterns in the contextlib section of the standard library docs at docs.python.org.


Advanced examples: Async context managers in modern Python

As of Python 3.12, asynchronous programming is mainstream in web servers, async ORMs, and external API clients. Naturally, async code has its own examples of context managers in Python: 3 practical examples in the async with world.

Example: Async HTTP client session

import asyncio
import aiohttp

async def fetch_json(url: str):
    async with aiohttp.ClientSession() as session:  # async context manager
        async with session.get(url) as response:    # another async context manager
            return await response.json()


asyncio.run(fetch_json("https://api.github.com"))

Here, both ClientSession and session.get(...) are async context managers. They handle connection pooling and response cleanup in a way that matches the async event loop.

Example: Async database session

Many async ORMs (like SQLAlchemy’s async engine or Tortoise ORM) use async context managers for sessions and transactions. A sketch with SQLAlchemy’s async API looks like this:

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession

engine = create_async_engine("sqlite+aiosqlite:///app.db")
SessionLocal = async_sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)


async def create_user(name: str):
    async with SessionLocal() as session:          # async context manager
        async with session.begin():                # async transaction
            session.add(User(name=name))

These async patterns are now standard in high-throughput APIs and microservices.

For a deeper background on concurrency concepts that motivate these patterns, the University of California, Berkeley has long-standing course material on parallel and concurrent programming at berkeley.edu, which, while not Python-only, helps frame why structured cleanup like this matters.


Why context managers are everywhere in production Python

By now we’ve seen many examples of context managers in Python: 3 practical examples and several more:

  • Files and compressed files
  • Database connections and transactions
  • Temporary working directories and environment variables
  • Timers and profiling
  • Async HTTP and database sessions
  • Exception suppression

All of these examples include the same pattern: a clear start, a clear end, and guaranteed cleanup in between.

In large codebases, this pattern reduces bugs around resource leaks and half-finished operations. It also makes code easier to reason about. When you see a with or async with block, you immediately know: something is being acquired, something is being released, and the interesting logic lives in the middle.

If you’re teaching or mentoring newer developers, walking through these real examples of context managers in Python is far more effective than starting from abstract definitions.

For more general programming best practices (including resource management and error handling), the U.S. National Institute of Standards and Technology provides software engineering guidance at nist.gov, which pairs nicely with Python’s own documentation.


FAQ: common questions about examples of context managers in Python

What are the most common examples of context managers in Python?

The most common examples of context managers in Python are:

  • File handling with with open(...) as f:
  • Database connections and transactions (with conn:, with session.begin():)
  • Temporary configuration or state (like a temporary working directory or environment variable)
  • Timers and profiling wrappers for measuring code execution
  • Async sessions for HTTP clients and async ORMs using async with

Can I write my own example of a context manager without a class?

Yes. Use contextlib.contextmanager and write a generator-style function with a try/finally block. That’s often the fastest way to create small, focused context managers for tests, configuration tweaks, or logging.

Are context managers still relevant with modern frameworks in 2024–2025?

Absolutely. Modern frameworks use context managers heavily under the hood: FastAPI, Django, SQLAlchemy, and popular async libraries all expose context-managed sessions, transactions, and resources. If anything, the rise of async programming has made with and async with more central, not less.

How do I decide whether to use a context manager or a decorator?

Use a context manager when you’re managing a block of code where setup and teardown surround arbitrary logic. Use a decorator when you want to wrap a function consistently. In many cases, you can offer both: a context manager for flexible blocks and a decorator built on top of it for convenience.

Where can I learn more and see additional examples?

The official Python documentation has a good section on with statements and contextlib at docs.python.org. For broader software reliability topics related to these patterns, NIST’s software engineering resources at nist.gov and university course materials at berkeley.edu are also helpful for context.

Explore More Python Code Snippets

Discover more examples and insights in this category.

View All Python Code Snippets