Courses/Python/Context Managers

    Lesson 18 • Advanced

    Context Managers & the with Statement In Depth

    Context managers are one of Python's most elegant and powerful tools — used everywhere from file handling, database transactions, network requests, locks, concurrency, and resource safety.

    What You'll Learn in This Lesson

    • • What a context manager is and how with works under the hood
    • • Creating class-based context managers with __enter__ and __exit__
    • • Using @contextmanager from contextlib for simpler patterns
    • • Handling errors inside context managers gracefully
    • • Real-world uses: database sessions, file I/O, timers, locks

    What You'll Learn

    If you've ever written with open("file.txt") as f: …you've already used a context manager.

    This lesson teaches you:

    ✔ How context managers work

    ✔ How to create your own using classes

    ✔ How to create them using generator functions (contextlib.contextmanager)

    ✔ How real systems use them for safe resource handling

    ✔ How to build production-grade context managers

    ✔ How to combine context managers with decorators & advanced design patterns

    🔥 1. What Is a Context Manager?

    A context manager controls a setup phase and a cleanup phase.

    PhaseWhat HappensHotel Analogy
    __enter__Resource is acquired/preparedCheck in, get room key
    with blockYour code runs using the resourceEnjoy your stay
    __exit__Resource is cleaned up/releasedCheckout, room cleaned

    The common structure:

    Context Manager Structure

    Basic with statement structure

    Try it Yourself »
    Python
    with something as value:
        # do work

    The moment execution enters the with block, the context manager prepares a resource. When the block exits — even if an error occurs — the resource is cleaned up.

    Typical use cases:

    • Files
    • Network connections
    • Database sessions
    • Locks (threading, multiprocessing)
    • Temporary directories
    • Timers
    • Log writers
    • Mocking in unit tests

    ⚙️ 2. How Python Processes the with Statement

    Under the hood:

    With Statement

    Simple with statement usage

    Try it Yourself »
    Python
    with manager as x:
        work()

    is equivalent to:

    With Statement Equivalent

    What happens under the hood

    Try it Yourself »
    Python
    manager_obj = manager.__enter__()
    try:
        x = manager_obj
        work()
    finally:
        manager.__exit__(*sys.exc_info())

    So a context manager must define:

    ✔ __enter__(self)

    • Runs when entering the block
    • Returns an optional value

    ✔ __exit__(self, exc_type, exc, tb)

    • Runs when leaving
    • Cleans up resources
    • Can suppress errors by returning True

    🧠 3. Creating Your Own Context Manager (Class-Based)

    Let's build a simple context manager that logs entering/exiting:

    Class-Based Context Manager

    Creating a logger context manager

    Try it Yourself »
    Python
    class Logger:
        def __enter__(self):
            print("Entering block...")
            return "Ready!"
    
        def __exit__(self, exc_type, exc, tb):
            print("Exiting block...")
            if exc:
                print("Error occurred:", exc)
            return False   # do not suppress errors

    Usage:

    Using Logger Context Manager

    Using the custom logger

    Try it Yourself »
    Python
    with Logger() as msg:
        print(msg)

    Output:

    Entering block... Ready! Exiting block...

    🧩 4. A Real Project Example — Timing Block Execution

    Timer Context Manager

    Timing code execution

    Try it Yourself »
    Python
    import time
    
    class Timer:
        def __enter__(self):
            self.start = time.time()
            return self
    
        def __exit__(self, exc_type, exc, tb):
            self.end = time.time()
            print(f"⏱ Elapsed: {self.end - self.start:.4f}s")

    Usage:

    Using Timer

    Timing a computation

    Try it Yourself »
    Python
    with Timer():
        sum([x for x in range(5000000)])

    🧨 5. A More Advanced Example — Database Connections

    This pattern exists in ORMs like SQLAlchemy & Django:

    Transaction Context Manager

    Database transaction pattern

    Try it Yourself »
    Python
    class Transaction:
        def __enter__(self):
            print("Start transaction")
            return self
    
        def __exit__(self, exc_type, exc, tb):
            if exc_type:
                print("Rollback")
            else:
                print("Commit")

    Usage:

    Using Transaction

    Using the transaction manager

    Try it Yourself »
    Python
    with Transaction():
        print("Updating user...")

    🧊 6. Using contextlib.contextmanager (Generator Context Managers)

    For simpler cases, Python provides a shortcut.

    Generator Context Manager

    Using contextlib.contextmanager

    Try it Yourself »
    Python
    from contextlib import contextmanager
    
    @contextmanager
    def open_file(path):
        f = open(path)
        try:
            yield f        # __enter__()
        finally:
            f.close()      # __exit__()

    Usage:

    Using Generator Context Manager

    Using the file opener

    Try it Yourself »
    Python
    with open_file("data.txt") as file:
        print(file.read())

    This is extremely common in:

    • testing tools
    • temporary environment changes
    • IO helpers
    • locking functions
    • resource wrappers

    🔧 7. Building a Temporary Directory Context Manager

    Temporary Directory Manager

    Auto-cleanup temp directory

    Try it Yourself »
    Python
    import tempfile
    import shutil
    from contextlib import contextmanager
    
    @contextmanager
    def tempdir():
        path = tempfile.mkdtemp()
        try:
            yield path
        finally:
            shutil.rmtree(path)

    Usage:

    Using Temp Directory

    Using the temp directory manager

    Try it Yourself »
    Python
    with tempdir() as path:
        print("Working in", path)

    🔐 8. Context Managers for Locking & Thread Safety

    Example using threading lock:

    Thread Lock Context Manager

    Thread-safe locking

    Try it Yourself »
    Python
    from threading import Lock
    
    lock = Lock()
    
    with lock:
        print("Critical section")

    Locks are native context managers. This pattern protects shared memory in concurrent programs.

    🧬 9. Context Managers Used With Decorators (Advanced Pattern)

    You can combine context managers with decorators to automatically wrap function execution:

    Decorator + Context Manager

    Combining patterns

    Try it Yourself »
    Python
    from contextlib import contextmanager
    from functools import wraps
    import time
    
    @contextmanager
    def timer_block():
        start = time.time()
        yield
        print(f"Elapsed: {time.time() - start:.4f}s")
    
    def timed(fn):
        @wraps(fn)
        def wrapper(*a, **k):
            with timer_block():
                return fn(*a, **k)
        return wrapper
    
    @timed
    def process():
        time.sleep(0.2)

    Usage:

    Using Timed Decorator

    Calling the timed function

    Try it Yourself »
    Python
    process()

    🧪 10. Suppressing Errors (But Carefully!)

    If __exit__ returns True, exceptions are swallowed.

    Example:

    Error Suppressing Manager

    Suppressing exceptions

    Try it Yourself »
    Python
    class IgnoreErrors:
        def __exit__(self, exc_type, exc, tb):
            return True

    Usage:

    Suppressing Division Error

    Error is silently handled

    Try it Yourself »
    Python
    with IgnoreErrors():
        1 / 0     # error suppressed

    Be careful — this is useful ONLY for:

    • logging
    • cleanup tasks
    • retry logic
    • safe shutdown

    Never suppress runtime errors silently in real systems.

    📚 11. Nested Context Managers

    Old way:

    Nested Context Managers (Old)

    Traditional nested approach

    Try it Yourself »
    Python
    with A() as a:
        with B() as b:
            ...

    Modern way:

    Nested Context Managers (Modern)

    Clean single-line approach

    Try it Yourself »
    Python
    with A() as a, B() as b:
        ...

    This is cleaner and avoids deep nesting.

    ⚡ 12. Combining Multiple Context Managers Dynamically

    Useful for frameworks or plugin systems:

    ExitStack for Dynamic Managers

    Managing multiple resources dynamically

    Try it Yourself »
    Python
    from contextlib import ExitStack
    
    with ExitStack() as stack:
        files = [stack.enter_context(open(f)) for f in file_list]
        # all files now managed safely

    This is extremely powerful for:

    • ML pipelines
    • batch processing
    • plugin environments

    🏗 13. Context Managers in Real Frameworks

    ✔ FastAPI dependency injection

    FastAPI DB Dependency

    Async database context

    Try it Yourself »
    Python
    async def db():
        async with engine.begin() as conn:
            yield conn

    ✔ PyTorch CUDA device switching

    PyTorch No Grad

    Disabling gradient computation

    Try it Yourself »
    Python
    with torch.no_grad():
        ...

    ✔ Django transaction atomicity

    Django Atomic Transaction

    Database transaction safety

    Try it Yourself »
    Python
    with transaction.atomic():
        ...

    ✔ NumPy print options

    NumPy Print Options

    Temporary print formatting

    Try it Yourself »
    Python
    with np.printoptions(suppress=True):
        ...

    Context managers are everywhere.

    🧱 14. Build a Production-Ready Context Manager

    Let's create a file-write-safe manager:

    Safe File Writer

    Atomic file writing pattern

    Try it Yourself »
    Python
    class SafeWriter:
        def __init__(self, path):
            self.path = path
    
        def __enter__(self):
            self.temp = open(self.path + ".tmp", "w")
            return self.temp
    
        def __exit__(self, exc_type, exc, tb):
            self.temp.close()
            if exc_type:
                # remove corrupted temp file
                import os
                os.remove(self.path + ".tmp")
                return False
            
            import os
            os.replace(self.path + ".tmp", self.path)

    This guarantees:

    • No half-written files
    • Atomic writes
    • OS-level safety

    This is similar to how:

    • VS Code
    • Config managers
    • Database migrations

    work internally.

    🎓 15. Mini Project — Build Your Own Resource Manager

    Create a context manager that:

    • ✔ tracks memory before/after
    • ✔ logs execution time
    • ✔ logs exceptions
    • ✔ sends results to a log file
    • ✔ optionally suppresses errors

    Usage:

    Monitor Context Manager

    Build your own resource monitor

    Try it Yourself »
    Python
    with Monitor("log.txt", suppress=False):
        run_expensive_task()

    This gives you portfolio-ready experience.

    🎉 Conclusion

    You now understand context managers:

    ✔ How the with statement works internally

    ✔ How to build class-based managers

    ✔ How to build generator-based managers

    ✔ How to combine decorators and context managers

    ✔ How to suppress or inspect exceptions

    ✔ How they power major frameworks

    ✔ How to create production-grade resource managers

    📋 Quick Reference — Context Managers

    SyntaxWhat it does
    with open(f) as fh:Open file safely, auto-closes on exit
    __enter__(self)Called when entering the with block
    __exit__(self, *args)Called on exit, even if an error occurs
    @contextmanagerBuild a manager from a generator function
    contextlib.suppress(E)Ignore a specific exception type

    🏆 Lesson Complete!

    You can now build and use context managers for any resource lifecycle — files, databases, locks, timers, and more.

    Up next: Generators — produce values lazily for memory-efficient data pipelines.

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service