Context Manager

Python Context Managers are most commonly used with the with statement to manage resources like files, network connections, or locks—anything that requires setup and teardown logic.

🔥 Basic Example – File Handling

with open("hello.txt", "w") as f:
    f.write("Hello, World!")
# File is automatically closed here

🧙‍♂️ Custom Context Manager (Class-based)

You can create your own using __enter__() and __exit__() methods:

class MyContext:
    def __enter__(self):
        print("Entering context")
        return self  # Optional
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Exiting context")

with MyContext():
    print("Inside context")

Output:

Entering context
Inside context
Exiting context

🧼 Handling Exceptions Inside a Context Manager

class SafeOpener:
    def __init__(self, filename):
        self.filename = filename

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

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()
        if exc_type:
            print(f"Exception handled: {exc_val}")
        return True  # Suppress the exception

with SafeOpener("output.txt") as f:
    f.write("Safe writing...")
    raise ValueError("Something went wrong")  # This won't crash

The with statement (i.e., context manager) works with objects (not just functions) that implement the context management protocol—i.e., they must define:


But you can also create function-based context managers using @contextmanager from the contextlib module.

🧵 Using contextlib (Cleaner Way)

from contextlib import contextmanager

@contextmanager
def open_file(name, mode):
    f = open(name, mode)
    try:
        yield f
    finally:
        f.close()

with open_file("data.txt", "w") as f:
    f.write("Hello from contextlib!")

🔧 Example 1: A Timer Function Context Manager

Let's create a timer function that calculate the execution duration of a function

import time
from contextlib import contextmanager

@contextmanager
def timer():
    start = time.time()
    yield  # Do the work in between
    end = time.time()
    print(f"Elapsed: {end - start:.2f} seconds")

# usage
with timer():
    time.sleep(2)

🧹 Example 2: Changing Directory Temporarily

import os
from contextlib import contextmanager

@contextmanager
def change_dir(destination):
    original = os.getcwd()
    os.chdir(destination)
    try:
        yield
    finally:
        os.chdir(original)

with change_dir("/tmp"):
    print("Current directory:", os.getcwd())

Let's walk through how to turn a regular function into a context manager using @contextmanager from Python’s contextlib module.

🪄 Step-by-Step Example

Let's say we have a simple function that opens a file, and we want to make it safe and elegant using a context manager.
❌ Regular Function (not context-safe)

def write_file(filename, content):
    f = open(filename, 'w')
    f.write(content)
    f.close() 

If something goes wrong between open() and close(), the file might not be closed properly.


✅ Turn it into a Context Manager Function

from contextlib import contextmanager

@contextmanager
def open_file_safe(filename, mode):
    f = open(filename, mode)
    try:
        yield f  # Give control to the block using 'with'
    finally:
        f.close()  # Always close, even on error

✅ Now use it like this:

with open_file_safe('hello.txt', 'w') as f:
    f.write('Hello from context manager!')

Behind the scenes:

🧪 Example 3: Temporarily Redirect Output

from contextlib import contextmanager
import sys

@contextmanager
def silence_stdout():
    original = sys.stdout
    sys.stdout = open('/dev/null', 'w')
    try:
        yield
    finally:
        sys.stdout.close()
        sys.stdout = original

with silence_stdout():
    print("You won't see this.")
print("But you'll see this!")

✅ Using a Context Manager with SQLite

import sqlite3

def create_table():
    with sqlite3.connect('my_database.db') as conn:
        cursor = conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY,
                name TEXT
            )
        ''')
        conn.commit()

This means:

Want More Control? Create Your Context Manager for DB

from contextlib import contextmanager

@contextmanager
def db_cursor(db_name):
    conn = sqlite3.connect(db_name)
    cursor = conn.cursor()
    try:
        yield cursor
        conn.commit()
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

Usage:

with db_cursor('my_database.db') as cursor:
    cursor.execute("SELECT * FROM users")
    print(cursor.fetchall())

💡 Summary

You can call any function-based context manager as long as:

🧠 It's worth reading about the similarities and dissimilarities between Context Manager and Decorator


Thanks for Stopping by! Happy learning

Connect with me on

Github LinkedIn Gmail Youtube