2012-10-25 19 views
16

Czy możliwe jest wywołanie metody __exit__(), nawet jeśli istnieje wyjątek w wersji __enter__()?Wyłapywanie wyjątków w menedżerze kontekstów __enter __()

>>> class TstContx(object): 
... def __enter__(self): 
...  raise Exception('Oops in __enter__') 
... 
... def __exit__(self, e_typ, e_val, trcbak): 
...  print "This isn't running" 
... 
>>> with TstContx(): 
...  pass 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in __enter__ 
Exception: Oops in __enter__ 
>>> 

Edit

To jest tak blisko, jak mogę dostać ...

class TstContx(object): 
    def __enter__(self): 
     try: 
      # __enter__ code 
     except Exception as e 
      self.init_exc = e 

     return self 

    def __exit__(self, e_typ, e_val, trcbak): 
     if all((e_typ, e_val, trcbak)): 
      raise e_typ, e_val, trcbak 

     # __exit__ code 


with TstContx() as tc: 
    if hasattr(tc, 'init_exc'): raise tc.init_exc 

    # code in context 

W tylnej wzroku, kierownik kontekst może nie być najlepsza decyzja konstrukcja

+4

Problem polega na tym, że nie można pominąć 'with' ciała z' __enter__' (patrz [pep 377] (http://www.python.org/dev/peps/pep-0377/)) – georg

Odpowiedz

14

Jak to:

import sys 

class Context(object): 
    def __enter__(self): 
     try: 
      raise Exception("Oops in __enter__") 
     except: 
      # Swallow exception if __exit__ returns a True value 
      if self.__exit__(*sys.exc_info()): 
       pass 
      else: 
       raise 


    def __exit__(self, e_typ, e_val, trcbak): 
     print "Now it's running" 


with Context(): 
    pass 

aby program nadal na jego wesoły sposób bez wykonywania bloku kontekstowego trzeba skontrolować obiekt kontekstowe wewnątrz bloku kontekstowego i tylko robić ważne rzeczy, jeśli __enter__ udało.

O ile wiem, nie można całkowicie pominąć bloku z. Zauważ, że ten kontekst teraz pochłania w nim wyjątki. Jeśli nie chcesz połknąć wyjątków, jeśli __enter__ się powiedzie, sprawdź self.enter_ok w __exit__ i return False, jeśli jest to True.

+1

If jest wyjątek w '__enter__' i nazywasz' __exit__', czy istnieje sposób na wyjście z bloku 'with' w kodzie klienta? – tMC

+0

@tMC Zobacz zaktualizowaną odpowiedź. –

+0

lol Właśnie pomyślałem o tym w tym samym czasie. Zaktualizowałem moje pytanie z tą samą logiką. – tMC

6

Nie. Jeśli jest szansa, że ​​wystąpi wyjątek w wersji __enter__(), musisz samemu go złapać i wezwać pomocnika funkcja, która zawiera kod czyszczenia.

2

Można użyć contextlib.ExitStack (nie testowane):

with ExitStack() as stack: 
    cm = TstContx() 
    stack.push(cm) # ensure __exit__ is called 
    with ctx: 
     stack.pop_all() # __enter__ succeeded, don't call __exit__ callback 

Albo przykład z the docs:

stack = ExitStack() 
try: 
    x = stack.enter_context(cm) 
except Exception: 
    # handle __enter__ exception 
else: 
    with stack: 
     # Handle normal case 

Zobacz contextlib2 on Python <3.3.

1

jeśli nie są wymagane dziedziczenia lub złożone podprogramów, można użyć krótszego sposób:

from contextlib import contextmanager 

@contextmanager 
def test_cm(): 
    try: 
     # dangerous code 
     yield 
    except Exception, err 
     pass # do something 
+1

tak, ale to spowodowałoby "generator nie dał" w contextlib. – georg

+0

@ thg435, uzasadnione, ale możemy zawęzić 'yield' z próbą ... wreszcie – newtover

+0

czyli wydajność w końcu bloku – newtover

0
class MyContext: 
    def __enter__(self): 
     try: 
      pass 
      # exception-raising code 
     except Exception as e: 
      self.__exit__(e) 

    def __exit__(self, *args): 
     # clean up code ... 
     if args[0]: 
      raise 

Zrobiłem to w ten sposób. Wywołuje __exit __() z błędem jako argumentem. Jeśli argument [0] zawiera błąd, zmienia on wyjątek po wykonaniu kodu oczyszczającego.

Powiązane problemy