如何从 ExitStack 中删除上下文管理器

Posted

技术标签:

【中文标题】如何从 ExitStack 中删除上下文管理器【英文标题】:How to remove a context manager from an ExitStack 【发布时间】:2016-10-03 01:53:38 【问题描述】:

我有一个名为 Updater 的长期运行进程,它向它提交了更新(到 ETL 系统)。更新具有通过将上下文管理器添加到更新程序的 ExitStack 来管理的资源需求。一些更新将包括新配置,这意味着必须从堆栈中释放受影响的资源,并且将添加新配置的资源版本。我需要类似的东西:

with ExitStack() as stack:
    ctx_manager = open("file.txt")
    f = stack.enter_context(ctx_manager)
    ...
    ctx_pop(ctx_manager, stack)  # remove the given context manager from the stack

下面是我已经开始工作的一个例子,但它依赖于访问受保护的成员。我希望可能有比这更“肮脏”的解决方案:

def ctx_pop(cm, stack):
    for item in stack._exit_callbacks:
        if item.__self__ is cm:
            break
    else:
        raise KeyError(repr(cm))
    stack._exit_callbacks.remove(item)
    item(None, None, None)

编辑:添加已知解决方案

【问题讨论】:

查看源代码 ExitStack 使用 deque 存储上下文 .__exit__ 方法的包装器,因此您需要能够通过原始上下文管理器识别包装器,如据我所知,是不可能的。您可能需要重新发明(至少部分)ExitStack 的功能,以便能够提前删除上下文。 @TadhgMcDonald-Jensen 谢谢。我在 Jupyter Notebook 中检查 ExitStack 对象时发现了同样的事情。他们有一个闭包双端队列,__self__ 属性是一个上下文管理器。我会把一些代码作为一个已知的解决方案,但我希望有一个不那么“黑客”的解决方案。在我提交给 Python Ideas 或自己添加之前,我想看看是否有更清洁的解决方案(我以前从未这样做过)。 【参考方案1】:

你必须用你自己的pop-方法扩展ExitStack

from contextlib import ExitStack
from collections import deque

class ExitStackWithPop(ExitStack):
    def pop(self, cm):
        callbacks = self._exit_callbacks
        self._exit_callbacks = deque()
        found = None
        while callbacks:
            cb = callbacks.popleft()
            if cb.__self__ == cm:
                found = cb
            else:
                self._exit_callbacks.append(cb)
        if not found:
            raise KeyError("context manager not found")
        found(None, None, None)

【讨论】:

它不是_ExitStack__self__,它只是__self__,带有两个下划线的属性不是私有的(想想所有神奇的方法名称)你也不需要重新创建@987654328 @,您可以遍历项目和.remove 具有.__self__ == cm的项目 谢谢!这与我现在使用的解决方案非常接近,但我喜欢子类化的想法。这样看起来更干净。 嘘。您将在此处了解实施细节。 因为this changes 在contextlib 它必须是if cb[1].__self__ == cm: 才能工作。 (无论如何它对 contextlib 的依赖非常低) 奇怪的是,ExitStack 中不包含此功能。它真的应该。显然,有些用例需要动态管理对象。您是否考虑过提交增强请求?【参考方案2】:

contextlib.ExitStack 只支持一次退出所有上下文管理器。您不能单独弹出上下文管理器。如果你想这样做,你应该使用 ExitStack 以外的东西来跟踪你的上下文管理器。

【讨论】:

以上是关于如何从 ExitStack 中删除上下文管理器的主要内容,如果未能解决你的问题,请参考以下文章

如何从“右键单击”上下文菜单(Windows 资源管理器)中选择多个文件以启动同一个应用程序

Jenkins在tomcat上部署删除管理器和用户

如何在上下文管理器之外打开 websocket 连接?

如何在 Windows 资源管理器中添加“在此处打开 git-bash ...”上下文菜单?

如何将项目添加到 UWP 应用程序的资源管理器上下文菜单

从C#走进python上下文管理器