2016-06-03 5 views
1

У меня есть длительный процесс, называемый Updater, который имеет обновления (к системе ETL), представленные на него. У обновлений есть требования к ресурсам, которым управляет добавление диспетчера контекстов к ExitStack Updater. Некоторые обновления будут включать новую конфигурацию, что означает, что выделенные ресурсы должны быть освобождены из стека, и будет добавлена ​​новая конфигурация ресурса. Мне нужно что-то вроде:Как удалить диспетчер контекста из 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) 

Edit: Добавлен известным решением

+1

Глядя на исходном коде 'ExitStack' использует' deque' для хранения оберток для контекстов '.__ exit__' метода так что вам нужно будет чтобы иметь возможность идентифицировать обертку исходным менеджером контекста, который, насколько мне известно, невозможен. Возможно, вам придется переосмыслить (по крайней мере частично) функциональность «ExitStack», чтобы иметь возможность удалять контексты раньше времени. –

+0

@ TadhgMcDonald-Jensen спасибо. Я обнаружил то же самое, что проверял объекты ExitStack в Jupyter Notebook. У них есть ключ закрытия, который является атрибутом '__self__', является менеджером контекста. Я добавлю некоторый код в известное решение, но я надеялся на меньшее «взломанное» решение. Я хотел посмотреть, есть ли более чистое решение, прежде чем я отправлюсь в Python Ideas или добавлю его сам (чего я никогда раньше не делал). – arachnivore

ответ

1

contextlib.ExitStack поддерживает только выход всех менеджеров контекста сразу. Вы не можете создавать контекстные менеджеры по отдельности. Если вы хотите это сделать, вы должны следить за своими менеджерами контекста за чем-то другим, кроме ExitStack.

2

Вы должны расширить ExitStack с вашим собственным pop -метода:

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) 
+1

это не '_ExitStack__self__', это просто' __self__', атрибуты с двумя минутами подчеркивания не являются частными (просто подумайте обо всех именах магических методов). Также вам не нужно воссоздавать 'deque', вы можете просто перебирать элементы и '.remove' тот, у которого есть' .__ self__ == cm' –

+0

Спасибо! Это очень близко к решению, которое я использую сейчас, но мне нравится идея подкласса. Кажется, это чище. – arachnivore

+0

Bleh. Вы входите в подробности реализации здесь. – user2357112