Skip to content

Context manager

Resource management

When developing software, there are times when we want to access and manage certain resources, such as file operations or database connections. Resources are often limited, so it is good practice to release them as soon as you stop using them.

File operations

File operations are an example of resource management.

    f = open("file.txt", 'w')
    f.write("Hello world")
    f.close()

The above lines of code are responsible for creating access to the file in write mode, saving a line of text in it and then closing it.

However, there may be an error when trying to write to the file, which will result in throwing an exception, so the file will not be closed, and consequently we will deal with memory leaks.

To protect against this, our code should look like this:

    f = open("file.txt", 'w')
    try:
        f.write("Hello world")
    except IOError:
        print("Error occured")
    finally:
        f.close()

This way we are sure that access to the file will be released, whether the execution is smooth or not.

Context manager

Using the context manager makes a lot easier here: wrapping the open () function in a with clause improves the readability of the code while ensuring that access to an open file is released, regardless of the circumstances.

    with open("file.txt", 'w') as f:
        f.write("Hello world")

Create your own managers

We also have the option of creating our own context managers. We can do this in two ways: by creating a class or a function. In this way, we can ensure, for example, support for access to the database, without having to remember to close the connection each time and thus protect ourselves against memory leaks.

Own content manager as a class

If we want to create a manager as a class, we must remember to implement the methods __enter__ and __exit__. The first one must open (file, database connections, etc.) and share resources, in __exit__ operations responsible for cleaning and releasing resources (closing a file, connecting to the base).

    class FileManager():
        def __init__(self, filename, mode):
            self.filename = filename
            self.mode = mode
            self.file = None

        def __enter__(self):
            # opening and sharing of resources
            self.file = open(self.filename, self.mode)
            return self.file

        def __exit__(self, type, value, traceback):
            # cleaning, release of resources
            self.file.close()


    if __name__ == "__main__":
        with FileManager("test.txt", 'w') as f:
            f.write("Test")

Own content manager as a function

If we want to create a manager as a function, we need to import the contextmanager decorator from the contextlib module. Next, we create a function that connects to the resources, then makes these resources available using the yield command (which returns an object like the return command, but does not block further program execution, we'll learn more about this when discussing generators and iterators). Finally, we release access to resources. We have to remember to decorate the created function by using @contextmanager.

    from contextlib import contextmanager


    @contextmanager
    def file_manager(name, mode):
        f = open(name, mode)
        yield f
        f.close()


    if __name__ == "__main__":
        with file_manager("test.txt", 'w') as f:
            f.write("Test")