What is the contextlib module?
Overview
The contextlib module of Python’s standard library provides utilities for resource allocation to the with statement.
The with statement in Python is used for resource management and exception handling. Therefore, it serves as a good with statement, it allocates resources temporarily. As soon as the with statement block finishes, all the resources initially consumed by with are released.
There are three functions provided by the contextlib module:
- contextmanager(func)
- nested(mgr1[, mgr2[, …]])
- closing(thing)
contextmanager(func)
A Context Manager requires two basic functions: __enter__() and __exit__(). You can implement a context manager in a class by writing __enter__() and __exit__() functions. By defining these functions in a class, we can use it alongside the with statement to provide optimal resource allocation. Take a look at the following code:
class myFileHandler():def __init__(self, fname, method):print("I'm in contructor")self.file_obj = open(fname, method)def __enter__(self):print("I'm in ENTER block")return self.file_objdef __exit__(self, type, value, traceback):print("I'm in EXIT block")self.file_obj.close()with myFileHandler("this_file.txt", "w") as example:######### read write statements #########print("I'm in WITH block")pass
The with statement calls our class myFileHandler(). The __enter__() method calls the constructor, opens a file using the parameters passed, and returns the file object to the with statement, which then assigns it to example. Then, the statements in the with block are executed. As soon as it exits the with block, the __exit__() method is invoked, and the process is gracefully terminated.
The entire process above can be replicated using the contextmanager(func) utility with the need for separate __enter__() and __exit__() methods. Take a look at the following code:
from contextlib import contextmanager@contextmanagerdef thisFunc(fname, method):print("This is the implicit ENTER block")my_file = open(fname, method)yield my_fileprint("This is the implicit EXIT block")my_file.close()with thisFunc("this_file.txt", "w") as example:######### read write statements #########print("I'm in WITH block")pass
It is fair to say that using the contextmanager(func) (as a decorator) makes the code more readable and easier to understand. The decorated contextmanager(func) has three sections:
- The section above the
yieldstatement does what an__enter__()method does. - The yield statement returns an iterator to the target in the
withstatementsasclause -example. At this stage, the control is passed to thewithstatement. - The section below the
yieldstatement does what an__exit__()method does.
nested(mgr1[, mgr2[, …]])
The contextlib module provides us with a utility to use when we need to call multiple functions with the with statement. The nested() function makes our lives easier in this scenario. Consider the following nested with statements:
with X as file_1:
with Y as file_2:
with Z as file_3:
# statements for file_1, file_2, and file_3
The nested function contracts the code above into one line
from contextlib import nested
with nested(X, Y, Z) as (file_1, file_2, file_3):
# statements for file_1, file_2, and file_3
If an __exit__() method of a Context Manager (X, Y, or Z) suppresses an exception, then the exception will not be passed to all the outer Context Managers. Similarly, If an __exit__() method of a Context Manager (X, Y, or Z) raises an exception, then the exception will be passed to all the outer Context Managers.
closing(thing)
The closing(thing) method returns a Context Manager that closes thing upon completion of the with block. For example:
from contextlib import contextmanager
import socket
@contextmanager
def closing(thing):
try:
thing.connect("localhost", 8333)
yield thing
finally:
thing.close()
sock = socket.socket()
with closing(sock) as my_sock:
my_sock.listen()
The code above sends a socket object to the decorated Context Manager closing(thing). The Context Manager establishes a connection and returns the thing object back to the with statement. When the with statement block commences, the socket object is closed.
The closing(thing) function of contextlib module lets you write the code above like this:
from contextlib import closing
import socket
with closing(socket.socket()) as my_sock:
my_sock.listen()
Free Resources