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:
-
When entering a
withblock. -
When exiting a
withblock.
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
withstatement. -
They use
__enter__()and__exit__()methods. -
__enter__()handles setup operations. -
__exit__()handles cleanup operations. -
Exceptions can be handled inside
__exit__(). -
Returning
Truefrom__exit__()suppresses exceptions. -
contextlib.contextmanagerprovides 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.
