替代contextlib.nested,具有可变数量的上下文管理器

替代contextlib.nested,具有可变数量的上下文管理器,第1张

替代contextlib.nested,具有可变数量的上下文管理器

添加了新的Python
3

contextlib.ExitStack

以替代
contextlib.nested()
(请参见问题13585)。

它的编码方式可以直接在Python 2中使用:

import sysfrom collections import dequeclass ExitStack(object):    """Context manager for dynamic management of a stack of exit callbacks    For example:        with ExitStack() as stack: files = [stack.enter_context(open(fname)) for fname in filenames] # All opened files will automatically be closed at the end of # the with statement, even if attempts to open files later # in the list raise an exception    """    def __init__(self):        self._exit_callbacks = deque()    def pop_all(self):        """Preserve the context stack by transferring it to a new instance"""        new_stack = type(self)()        new_stack._exit_callbacks = self._exit_callbacks        self._exit_callbacks = deque()        return new_stack    def _push_cm_exit(self, cm, cm_exit):        """Helper to correctly register callbacks to __exit__ methods"""        def _exit_wrapper(*exc_details): return cm_exit(cm, *exc_details)        _exit_wrapper.__self__ = cm        self.push(_exit_wrapper)    def push(self, exit):        """Registers a callback with the standard __exit__ method signature        Can suppress exceptions the same way __exit__ methods can.        Also accepts any object with an __exit__ method (registering a call        to the method instead of the object itself)        """        # We use an unbound method rather than a bound method to follow        # the standard lookup behaviour for special methods        _cb_type = type(exit)        try: exit_method = _cb_type.__exit__        except AttributeError: # Not a context manager, so assume its a callable self._exit_callbacks.append(exit)        else: self._push_cm_exit(exit, exit_method)        return exit # Allow use as a decorator    def callback(self, callback, *args, **kwds):        """Registers an arbitrary callback and arguments.        Cannot suppress exceptions.        """        def _exit_wrapper(exc_type, exc, tb): callback(*args, **kwds)        # We changed the signature, so using @wraps is not appropriate, but        # setting __wrapped__ may still help with introspection        _exit_wrapper.__wrapped__ = callback        self.push(_exit_wrapper)        return callback # Allow use as a decorator    def enter_context(self, cm):        """Enters the supplied context manager        If successful, also pushes its __exit__ method as a callback and        returns the result of the __enter__ method.        """        # We look up the special methods on the type to match the with statement        _cm_type = type(cm)        _exit = _cm_type.__exit__        result = _cm_type.__enter__(cm)        self._push_cm_exit(cm, _exit)        return result    def close(self):        """Immediately unwind the context stack"""        self.__exit__(None, None, None)    def __enter__(self):        return self    def __exit__(self, *exc_details):        # We manipulate the exception state so it behaves as though        # we were actually nesting multiple with statements        frame_exc = sys.exc_info()[1]        def _fix_exception_context(new_exc, old_exc): while 1:     exc_context = new_exc.__context__     if exc_context in (None, frame_exc):         break     new_exc = exc_context new_exc.__context__ = old_exc        # Callbacks are invoked in LIFO order to match the behaviour of        # nested context managers        suppressed_exc = False        while self._exit_callbacks: cb = self._exit_callbacks.pop() try:     if cb(*exc_details):         suppressed_exc = True         exc_details = (None, None, None) except:     new_exc_details = sys.exc_info()     # simulate the stack of exceptions by setting the context     _fix_exception_context(new_exc_details[1], exc_details[1])     if not self._exit_callbacks:         raise     exc_details = new_exc_details        return suppressed_exc

使用它作为上下文管理器,然后 随意 添加 嵌套的 上下文管理器:

with ExitStack() as stack:    managers = [stack.enter_context(my_context(arg)) for arg in items]    print("processing under", managers)

对于您的示例上下文管理器,将输出:

>>> my_fn(range(3))('entering', 0)('entering', 1)('entering', 2)('processing under', [0, 1, 2])('exiting', 2)('exiting', 1)('exiting', 0)

您也可以安装

contextlib2
模块;
它包括
ExitStack
作为反向端口。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5653485.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存