What is context manager in Python?

Python provides a statement that enables the developer to execute the concept of the runtime contexti.e., allocating resources.

This can be broken up and learned using the analogy of the local variable inside the function. The local variable is created when a function is called automatically, and is ideally deleted when a function has completed execution.

In a case like this, we are releasing memory when we’re done with it. Similarly, in the case of context managers, the resource is locked or marked as ‘in use’ when the interpreter enters the context manager block. As soon as it’s exited, the lock is released.

Managing resources

In any programming language, the resource usagelike database connections or file operations is widespread, but these resources are limited in supply. Therefore, the main issue lies in making it compulsory to release after using these resources.

If these resources are not released, they will lead to resource leakage and cause the system to either crash or slow down, which will be instrumental if a user has a teardown of resources and automatic setup. In Python, this can be realized with context managers, which facilitate the proper handling of resources.

Managing resources using a context manager

If a block of code lifts an exception or has a complex algorithm with multiple return paths, it will become cumbersome to close the file in all the available places.

Generally, in different languages, when working with files, try-except-finally is used to confirm that the file resource is closed after usage even if there is an exception. However, Python gives an easy way to manage resources with context managers.

Context managers can be written using classes or functions (with decorators). When it gets evaluated, it should result in an object that performs context management.

Creating a context manager

When we create context managers using classes, users need to ensure that the class has the _enter() and __exit() methods.

The __enter() method returns the resource that needs to be managed; while, the __exit_() method does not return anything and performs the clean-up operations.

In this case, a ContextManager object is created. This object is assigned to the variable after the keywordi.e., manager. Upon running the above program, the following will get executed in sequence.

  • _init_()
  • _enter_()
  • statement body (code inside the with block)
  • _exit_() (the parameters in this method are used to manage exceptions)

File management using a a context manager

Let’s apply the above concept to create a class that helps with file resource management. The File Manager class helps with opening a file, writing/reading contents, and then closing it.

Implementing a context manager as a class

Our _exit_ method accepts three arguments. They are useful by every _exit_ method, a part of the ContextManager class. Let’s talk about what will happen under the hood:

  1. The with statement will store the _exit_ method of a File class.
  2. It will call the _enter_ method of a File class.
  3. The _enter_ method will open the file and return it.
  4. The opened file handle is passed to opened_file.
  5. We write to a file using .write().
  6. The with statement calls the stored _exit_ method.
  7. The _exit_ method closes the file.

Handling exceptions

We did not discuss the type, value, or traceback arguments of an _exit_ method. If an exception occurs between the 4th and the 6th step, Python passes a type, value, and traceback of an exception to an _exit_ method. It allows an _exit_ method to decide how to close a file, and if any further steps are needed.

Database connection management using context manager

Let’s create a simple connection management system for a database. The number of database connections that can be opened at one time is also limited (just like the file descriptors). Therefore, the context managers are helpful in managing connections to the database, as there may be a chance that the programmer will forget to close the connection.

Code

Basic context manager

class ContextManager():
def __init__(self):
print('init method called')
def __enter__(self):
print('enter method called')
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
print('exit method called')
with ContextManager() as manager:
print('with statement block')

Loading a file

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)

Handling exception

class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, type, value, traceback):
print("Exception has been handled")
self.file_obj.close()
return True
with File('demo.txt', 'w') as opened_file:
opened_file.undefined_function()

Free Resources