Nesting Python context managers

后端 未结 2 720
情歌与酒
情歌与酒 2020-12-10 16:14

In this question, I defined a context manager that contains a context manager. What is the easiest correct way to accomplish this nesting? I ended up calling self.te

相关标签:
2条回答
  • 2020-12-10 16:19

    contextlib.contextmanager works great for functions, but when I need a classes as context manager, I'm using the following util:

    class ContextManager(metaclass=abc.ABCMeta):
      """Class which can be used as `contextmanager`."""
    
      def __init__(self, filename):
        self.filename = filename
    
      def __init__(self):
        self.__cm = None
    
      @abc.abstractmethod
      @contextlib.contextmanager
      def contextmanager(self):
        raise NotImplementedError('Abstract method')
    
      def __enter__(self):
        self.__cm = self.contextmanager()
        return self.__cm.__enter__()
    
      def __exit__(self, exc_type, exc_value, traceback):
        return self.__cm.__exit__(exc_type, exc_value, traceback)
    

    This allow to declare contextmanager classes with the generator syntax from @contextlib.contextmanager. It makes it much more natural to nest contextmanager, without having to manually call __enter__ and __exit__. Example:

    class MyClass(ContextManager):
    
      def __init__(self, filename):
        self._filename = filename
    
      @contextlib.contextmanager
      def contextmanager(self):
        with tempfile.TemporaryFile() as temp_file:
          yield temp_file
          ...  # Post-processing you previously had in __exit__
    
    
    with MyClass('filename') as x:
      print(x)
    

    I wish this was in the standard library...

    0 讨论(0)
  • 2020-12-10 16:37

    The easy way to create context managers is with contextlib.contextmanager. Something like this:

    @contextlib.contextmanager
    def write_on_change_file(filename):
        with tempfile.TemporaryFile('r+') as temporary_file:
            yield temporary_file
            try:
                 ... some saving logic that you had in __exit__ ...
    

    Then use with write_on_change_file(...) as f:.
    The body of the with statement will be executed “instead of” the yield. Wrap the yield itself in a try block if you want to catch any exceptions that happen in the body.

    The temporary file will always be properly closed (when its with block ends).

    0 讨论(0)
提交回复
热议问题