Context Managers and the `with` statement

Page content
  • They simplify resource management patterns by abstracting their functionality.

Creating a context manager

  • Context managers can be written using classes or functions (with decorators).

i. Using classes

  • User needs to ensure that the class has the methods:
    • __enter__ returns the resource that needs to be managed.
    • __exit__ does not return anything, but performs the cleanup operations.
"""
init method called
enter method called
with statement block
exit method called
"""

class ContextManager():
  def __init__(self):
    print("init method is called")

  def __enter__(self):
    print("enter method is called")
    return self

  def __exit__(self, exc_type, exc_value, exc_traceback):
    print("exit method is called")

with ContextManager() as manager:
  print("with statement block")

ii. Using decorators

"""
Enter method called
with statement block
Exit method called 
"""
from contextlib import contextmanager

@contextmanager
def ContextManager():

  print("Enter method called")

  yield

  print("Exit method called")

with ContextManager as manager:
  print("with statement block")

File management using a context manager

class FileManager():
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None
          
    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file
      
    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.file.close()
  
# loading a file 
with FileManager('test.txt', 'w') as f:
    f.write('Test')
  
print(f.closed) # True

Database connection management with a context manager

from pymongo import MongoClient

class MongoDBConnectionManager():
    def __init__(self, hostname, port):
        self.hostname = hostname
        self.port = port
        self.connection = None
    
    def __enter__(self):
        self.connection = MongoClient(self.hostname, self.port)
        return self
  
    def __exit__(self, exc_type, exc_value, exc_traceback):
        self.connection.close()

with MongoDBConnectionManager('localhost', '27017') as mongo:
  collection = mongo.connection.SampleDb.test
  data = collection.find({'_id': 1})
  print(data.get('name'))

  • with ensures that open file descriptors are closed automatically after program execution leaves the context of the with statement.
with open('hello_world.txt', 'w') as f:
  f.write('hello, world!')

  • with statement is used effectively in threading.Lock class.
some_lock = threading.Lock()

# USE THIS
with some_lock:
  ...

# INSTEAD OF THIS
some_lock.acquire()
try:
  ...
finally:
  some_lock.release()