2016-06-03 36 views
1

Ich habe einen lang laufenden Prozess namens Updater, der über Updates (an ein ETL-System) verfügt. Die Aktualisierungen verfügen über Ressourcenanforderungen, die durch Hinzufügen eines Kontextmanagers zum ExitStack des Updaters verwaltet werden. Einige Updates beinhalten eine neue Konfiguration, was bedeutet, dass betroffene Ressourcen vom Stack freigegeben werden müssen und eine neu konfigurierte Version der Ressource hinzugefügt wird. Ich brauche so etwas wie:So entfernen Sie einen Kontextmanager aus einem 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 

Im Folgenden ist ein Beispiel für etwas, das ich zu arbeiten bekommen haben, aber es stützt sich auf geschützte Mitglieder zugreifen. Ich hatte gehofft, es könnte eine weniger ‚schmutzige‘ Lösung als diese:

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: Hinzugefügt bekannte Lösung

+1

auf den Quellcode der Suche 'ExitStack' verwendet eine' deque' Wrapper speichern für die Kontexte '.__ exit__' Methode, so dass Sie benötigen würde den Wrapper durch den ursprünglichen Kontextmanager identifizieren zu können, was meines Wissens nicht möglich ist. Möglicherweise müssen Sie die Funktionalität von 'ExitStack' neu erfinden (zumindest teilweise), um Kontexte vorzeitig entfernen zu können. –

+0

@ TadhgMcDonald-Jensen danke. Ich habe das gleiche entdeckt, als ich 'ExitStack'-Objekte in einem Jupyter-Notebook untersuchte. Sie haben eine Deque von Schließungen, deren Attribut "__self__" ein Kontextmanager ist. Ich werde Code als bekannte Lösung verwenden, aber ich habe auf eine weniger "Hack" -Lösung gehofft. Ich wollte sehen, ob es eine sauberere Lösung gab, bevor ich Python Ideas einreiche oder sie selbst hinzufüge (was ich vorher noch nie getan habe). – arachnivore

Antwort

1

contextlib.ExitStack nur auf einmal Verlassen alle Kontext-Manager unterstützt. Sie können Kontextmanager nicht einzeln aufrufen. Wenn Sie dies tun möchten, sollten Sie Ihre Kontextmanager mit etwas anderem als einem ExitStack verfolgen.

2

Sie haben ExitStack mit Ihrem eigenen pop -Methode erweitern:

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

es ist nicht '_ExitStack__self__' es ist nur '__self__', Attribute mit zwei nachgestellten Unterstrichen sind nicht privat (denken Sie nur an alle Namen der magischen Methode) Auch müssen Sie die' deque' nicht neu erstellen, Sie können einfach durch iterieren die Elemente und '.remove' derjenige, der '.__ self__ = cm hat –

+0

Danke! Das ist sehr nah an der Lösung, die ich gerade verwende, aber ich mag die Idee des Unterklassifizierens. Es scheint so sauber zu sein. – arachnivore

+0

Bleh. Sie greifen hier auf die Implementierungsdetails zu. – user2357112