contextlib is python’s standard library. The "with" command in python is utilized for resource management and exception handling. It will allocate resources temporarily when the code reaches the “with” statement. And when the "with" statement block ends, all resources originally consumed by the "with" statement are released.
The functions provided by the contextlib module are:
a) contextmanager(func)
b) nested(mgr1[, mgr2[, …]])
c) closing(thing)
contextmanager(func)
The two basic functions in contextmanager are __enter()__ and __exit__(). We can use it along with the "with" statement to ensure optimal resource allocation by defining these functions in a class.
Refer to the following code:
class WithExample():
def __init__(self, fname, method):
print("In contructor")
self.file_obj = open(fname, method)
def __enter__(self):
print("In ENTER block")
return self.file_obj
def __exit__(self, type, value, traceback):
print("In EXIT block")
self.file_obj.close()
with WithExample("this_file.txt", "w") as example:
print("In WITH block")
Pass
Output:
In contructor
In ENTER block
In WITH block
In EXIT block
The with statement calls the WithExample() class. __enter__() method will call the constructor then opens the file using the passed parameters, and returns a file object with the statement, then it will assign to the example. After that, the statements in the with block will execute. Then the __exit__() method is called and terminates the process.
The entire process we can replicate using by contextmanager(fun) with separate __enter__() and __exit__() methods.
Refer following code:
from contextlib import contextmanager
@contextmanager
def thisFunc(fname, method):
print("This is implicit ENTER block")
my_file = open(fname, method)
yield my_file
print("This is implicit EXIT block")
my_file.close()
with thisFunc("this_file.txt", "w") as example:
print("In WITH block")
pass
Output:
This is implicit ENTER block
In WITH block
This is implicit EXIT block
The contextmanager(func) has three sections:
a) The yield statement will do the __enter__() does.
b) The yield statement returns an iterator to the target "with" statements as a clause - an example.
c) At this stage, control is passed to the "with" statement.
nested(mgr1[, mgr2[, …]])
This module provides a handler that is used when call multiple functions using the "with" statement. The nested() function makes life easier for us in this scenario.
Refer to the following nested “with” statements:
with X as file_1:
with Y as file_2:
with Z as file_3:
A nested function pulls the above code into one line
from contextlib import nested
with nested(A, B, C) as (file_1, file_2, file_3):
If the __exit__() method of a context manager(A, B, or C) throws an exception, the exception will not be passed to outer context managers. The exception will be passed to all outer context managers if the __exit__() method throws an exception.
closing(thing)
It returns a Context Manager that closes the “thing” after the “with” block is complete.
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()
Then the code sends the socket object to the decorated contextmanager close(thing).context manager will create the connection and returns the thing object back to “with” statement. When the "with" statement block begins, the socket object is closed.
The “closing(thing)” of the module allows writing the above code like this:
from contextlib import closing
import socket
with closing(socket.socket()) as my_sock:
my_sock.listen()
Thus, ContextManager is a very useful Python function for efficient resource management in a program.