Manage multiple context managers together with Python contextlib.ExitStack

The Python standard library contextlib.ExitStack class allows you to manage multiple context managers [^ cm] at once. The advantages of the ExitStack class are listed below.

--Since the ExitStack class is also a context manager, you can specify the scope with with statement. --Register the context manager included in the instance variable of the class with the __init__ method. Can be managed collectively. --You can control the call of the __enter__ method by properly using the push method and the ʻenter_context` method.

Confirmation of operation of push method and ʻenter_context` method

The push method does not call the managed __enter__ method, and the ʻenter_context method calls the managedenter method. Normally, using the ʻenter_context method is fine.

from contextlib import ExitStack


class TestClass:
    def __init__(self):
        print("TestClass.__init__()")

    def __enter__(self):
        print("TestClass.__enter__()")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("TestClass.__exit__()")


with ExitStack() as stack:
    stack.push(TestClass())
    # TestClass.__init__()
    # TestClass.__exit__()

with ExitStack() as stack:
    stack.enter_context(TestClass())
    # TestClass.__init__()
    # TestClass.__enter__()
    # TestClass.__exit__()

Practical example: Open all the files in the directory with the ʻopen built-in function and manage them with ʻExitStack.

It is not overused due to the problem of handle resource management, but you can open and manage the files in the directory at once by combining the ʻExitStack and with` statements.

import os
from contextlib import ExitStack
from typing import List, BinaryIO

#Create / start an ExitStack object with the with statement.
with ExitStack() as stack:
    #Get the path of the file in the current directory.
    #Exclude directories as the open built-in function fails.
    paths: List[str] = [path for path in os.listdir(".") if os.path.isfile(path)]

    #Open while adding the file to ExitStack.
    files: List[BinaryIO] = [stack.enter_context(open(path, "rb")) for path in paths]

    #Process the opened file appropriately.
    file: BinaryIO
    for file in files:
        print(",".join([hex(byte).ljust(2, "0") for byte in file.read(5)]))

#The context manager registered in the ExitStack object (stack)
#Released at the end of the with statement.

Related information

--You can use the release process for asynchronous context manager in contextlib.AsyncExitStack class.

[^ cm]: Context manager: An object that implements the __enter__ and __exit__ methods. Reference: Official documentation.

Recommended Posts

Manage multiple context managers together with Python contextlib.ExitStack
Manage multiple Python versions with update-alternatives (Ubuntu)
Manage Python multiple version environment with Pythonz, virtualenv
Execution order when multiple context managers are specified in the Python with statement
Manage python environment with virtualenv
[Package cloud] Manage python packages with package cloud
Post multiple Twitter images with python
Animate multiple still images with Python
Manage each Python version with Homebrew
Multiple integrals with Python and Sympy
[Python] Creating multiple windows with Tkinter
Manage multiple execution environments using Python venv
Process multiple lists with for in Python
Question: Multiple integrals with python don't work
You can manage overlapping characters with multiple plt.text.
Use multiple versions of python environment with pyenv
Developed and verified with multiple python versions with direnv
Manage AWS nicely with the Python library Boto
[Python] Dealing with multiple call errors in ray.init
Convert multiple proto files at once with python
[Python] Mention to multiple people with Slack API
FizzBuzz with Python3
Scraping with Python
Statistics with python
Scraping with Python
Twilio with Python
Integrate with Python
Play with 2016-Python
AES256 with python
python starts with ()
Bingo with python
Zundokokiyoshi with python
Excel with Python
Microcomputer with Python
Cast with python
Write multiple records to DynamoDB with Lambda (Python, JavaScript)
Remove headings from multiple format CSV files with python