Python Context Manager

File Context Manager

Solving: not closing opened files.

A file context manager class contains:

1. An __init__() method that sets up the object.

2. __enter__() opens and returns the file.

3. __exit__() just closes the file

class File():
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        
    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, *args):
        self.file.close()
        
files = []
for i in range(10000):
    with File("abc.txt", "r") as f:
        files.append(f)

Crucially, the variable “f” only exists within the indented block below the with statement.

Contextlib

The @contextmanager decorator is a decorator to create context managers. To use it, decorate a generator function that calls yield exactly once. Everything before the call to yield is considered the code for __enter__(). Everything after is the code for __exit__().

from contextlib import contextmanager

@contextmanager
def open_file(path, mode):
    the_file = open(path, mode)
    yield the_file
    the_file.close()

files = []

for x in range(100000):
    with open_file('foo.txt', 'w') as infile:
        files.append(infile)

for f in files:
    if not f.closed:
        print('not closed')

ContextDecorator

Define a context manager using the class-based approach, but inheriting from contextlib.ContextDecorator.

from contextlib import ContextDecorator

class makeparagraph(ContextDecorator):
    def __enter__(self):
        print('<p>')
        return self

    def __exit__(self, *exc):
        print('</p>')
        return False

@makeparagraph()
def emit_html():
    print('Here is some non-HTML')

emit_html()

Reference: https://jeffknupp.com/blog/2016/03/07/python-with-context-managers/