Custom Context Managers

Introduction

A Custom Context Manager is a user-defined context manager that controls how resources are acquired and released. It allows you to use the with statement with your own classes or functions.

Custom context managers are useful when you need to automatically handle setup and cleanup tasks such as:

  • Database Connections

  • File Operations

  • Browser Sessions

  • API Sessions

  • Logging

  • Thread Locks

  • Temporary Resources

In this tutorial, you will learn what custom context managers are, how they work, practical examples, automation testing use cases, common mistakes, and best practices.


What is a Custom Context Manager?

A custom context manager is a class or function that defines what should happen:

  1. When entering a with block.

  2. When exiting a with block.

Python uses two special methods:

__enter__()
__exit__()

Why Use Custom Context Managers?

Custom context managers help:

  • Automate resource cleanup

  • Reduce repetitive code

  • Prevent resource leaks

  • Improve readability

  • Handle exceptions gracefully


Basic Structure of a Custom Context Manager

Syntax

class MyContextManager:

    def __enter__(self):
        # Setup code
        return self

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):
        # Cleanup code
        pass

Simple Custom Context Manager Example

Example

class MyContextManager:

    def __enter__(self):
        print("Entering Context")
        return self

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):
        print("Exiting Context")


with MyContextManager():
    print("Inside Block")

Output

Entering Context
Inside Block
Exiting Context

Understanding enter()

The __enter__() method runs when the with block starts.

Example

class Demo:

    def __enter__(self):
        print("Resource Acquired")
        return self

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):
        pass


with Demo() as resource:
    print("Working...")

Output

Resource Acquired
Working...

The object returned by __enter__() is assigned to resource.


Understanding exit()

The __exit__() method runs when leaving the with block.

Example

class Demo:

    def __enter__(self):
        print("Start")
        return self

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):
        print("Cleanup")


with Demo():
    print("Running")

Output

Start
Running
Cleanup

Handling Exceptions in Custom Context Managers

Example

class ErrorHandler:

    def __enter__(self):
        print("Start")
        return self

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):

        if exc_type:
            print(f"Exception: {exc_value}")

        print("Cleanup")


with ErrorHandler():
    print(10 / 0)

Output

Start
Exception: division by zero
Cleanup

Suppressing Exceptions

Returning True from __exit__() suppresses exceptions.

Example

class SuppressError:

    def __enter__(self):
        return self

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):

        print("Handled Error")

        return True


with SuppressError():
    print(10 / 0)

print("Program Continues")

Output

Handled Error
Program Continues

The exception is not propagated.


Real-World Example: File Manager

Example

class FileManager:

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

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

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):
        self.file.close()


with FileManager("data.txt") as file:
    print(file.read())

The file is automatically closed.


Real-World Example: Database Connection

Example

class Database:

    def __enter__(self):

        print(
            "Database Connected"
        )

        return self

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):

        print(
            "Database Disconnected"
        )


with Database():
    print("Executing Query")

Output

Database Connected
Executing Query
Database Disconnected

Creating Context Managers with contextlib

Python provides the contextlib module.

Example

from contextlib import contextmanager

@contextmanager
def database():

    print("Connection Opened")

    yield

    print("Connection Closed")


with database():
    print("Running Query")

Output

Connection Opened
Running Query
Connection Closed

This approach is shorter than using a class.


Custom Context Manager for Timing

Example

import time

class Timer:

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

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):

        end = time.time()

        print(
            f"Elapsed Time: {end-self.start}"
        )


with Timer():

    time.sleep(2)

Output

Elapsed Time: 2.0...

Custom Context Managers in Selenium Automation

Example

class BrowserSession:

    def __enter__(self):

        print(
            "Browser Opened"
        )

        return self

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):

        print(
            "Browser Closed"
        )


with BrowserSession():
    print("Running Test")

Output

Browser Opened
Running Test
Browser Closed

Real Selenium Usage

from selenium import webdriver

class Browser:

    def __enter__(self):
        self.driver = webdriver.Chrome()
        return self.driver

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):
        self.driver.quit()


with Browser() as driver:
    driver.get(
        "https://example.com"
    )

Browser closes automatically.


Custom Context Managers in API Automation

Example

import requests

class APISession:

    def __enter__(self):

        self.session = requests.Session()

        return self.session

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):

        self.session.close()


with APISession() as session:

    response = session.get(
        "https://api.example.com"
    )

Session closes automatically.


Common Mistakes Beginners Make

Forgetting enter()

Incorrect

class Demo:

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
    ):
        pass

Error

AttributeError:
__enter__

Forgetting exit()

Incorrect

class Demo:

    def __enter__(self):
        return self

Error

AttributeError:
__exit__

Not Returning a Value from enter()

Incorrect

def __enter__(self):
    print("Started")

as variable will receive None.


Correct

def __enter__(self):
    return self

Forgetting Cleanup Logic

Always release resources inside __exit__().


Best Practices

Keep Setup Logic in enter()

def __enter__(self):

Acquire resources here.


Keep Cleanup Logic in exit()

def __exit__(self):

Release resources here.


Use contextlib for Simple Cases

@contextmanager

Less boilerplate code.


Handle Exceptions Gracefully

Use the exception parameters:

exc_type
exc_value
traceback

Always Release Resources

Examples:

  • Files

  • Database Connections

  • Browser Drivers

  • API Sessions


Advantages of Custom Context Managers

  • Automatic cleanup

  • Cleaner code

  • Resource safety

  • Exception handling

  • Reusable logic


Limitations of Custom Context Managers

  • Extra code for simple tasks

  • Can be confusing for beginners

  • Improper implementation may hide errors


Class-Based vs Function-Based Context Managers

Feature Class-Based Function-Based
Uses enter/exit Yes No
Uses @contextmanager No Yes
More Flexible Yes Moderate
Less Code No Yes

Conclusion

Custom context managers allow you to create your own resource management logic and integrate it seamlessly with Python’s with statement. They help automate setup and cleanup operations, making code safer, cleaner, and easier to maintain.

Whether you’re working with files, databases, Selenium WebDriver sessions, API connections, or temporary resources, custom context managers ensure that resources are properly released even when exceptions occur.

Mastering custom context managers is an important step toward writing professional, production-quality Python applications.


Frequently Asked Questions (FAQs)

What is a custom context manager?

A user-defined context manager that controls resource acquisition and cleanup.


Which methods are required?

__enter__()
__exit__()

What does __enter__() do?

It runs when entering the with block.


What does __exit__() do?

It runs when exiting the with block and performs cleanup.


Can I create context managers without classes?

Yes.

Using:

from contextlib import contextmanager

and the @contextmanager decorator.


Key Takeaways

  • Custom context managers work with the with statement.

  • They use __enter__() and __exit__() methods.

  • __enter__() handles setup operations.

  • __exit__() handles cleanup operations.

  • Exceptions can be handled inside __exit__().

  • Returning True from __exit__() suppresses exceptions.

  • contextlib.contextmanager provides a simpler alternative.

  • Useful for files, databases, Selenium WebDriver, and API sessions.

  • They help prevent resource leaks.

  • Custom context managers improve code readability and maintainability.