2012-10-25 6 views
16

__enter__()에 예외가 있어도 __exit__() 메서드가 호출되도록 보장 할 수 있습니까?컨텍스트 관리자에서 캐칭 예외 __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__ 
>>> 

편집

이 내가 얻을 수있는만큼 가까운 ... 뒤의 광경에서

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 

, 컨텍스트 관리자는 최고의 디자인 결정

+4

문제는 '__enter__'안에있는 'with'본문을 건너 뛰는 것이 불가능하다는 것입니다 ([pep 377] (http://www.python.org/dev/peps/pep-0377/) 참조) – georg

답변

14

:

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 

이 프로그램은 상황에 맞는 블록 내부의 상황에 맞는 개체를 검사 할 필요 __enter__가 성공했을 경우에만 중요한 물건을 할 상황에 맞는 블록을 실행하지 않고 메리 길을 계속 할 수 있습니다.

class Context(object): 
    def __init__(self): 
     self.enter_ok = True 

    def __enter__(self): 
     try: 
      raise Exception("Oops in __enter__") 
     except: 
      if self.__exit__(*sys.exc_info()): 
       self.enter_ok = False 
      else: 
       raise 
     return self 

    def __exit__(self, e_typ, e_val, trcbak): 
     print "Now this runs twice" 
     return True 


with Context() as c: 
    if c.enter_ok: 
     print "Only runs if enter succeeded" 

print "Execution continues" 

내가 판단 할 수있는 한 완전히 with 블록을 건너 뛸 수 없습니다. 그리고이 컨텍스트는 이제 모두 예외를 삼킨다는 점에 유의하십시오. __enter__이 성공한 경우 예외를 삼키고 싶지 않은 경우 인 경우 self.enter_ok__exit__return False으로 확인하십시오.

+1

If '__enter__ '에 예외가 있고'__exit__'를 호출하면 클라이언트 코드에서'with' 블록을 벗어날 수있는 방법이 있습니까? – tMC

+0

@tMC 업데이트 된 답변보기. –

+0

lol 나는 그 사실을 동시에 생각했다. 내 질문을 같은 논리로 업데이트했습니다. – tMC

6
를하지 수도

아니요. __enter__()에서 예외가 발생할 가능성이있는 경우 직접 잡아서 도우미에게 전화해야합니다. 함수는 정리 코드를 포함합니다. 이처럼

2

당신은 contextlib.ExitStack (테스트하지)를 사용할 수 있습니다

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 

또는 the docs에서 예 :

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

contextlib2 on Python <3.3를 참조하십시오.

from contextlib import contextmanager 

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

, 당신은 짧은 방법을 사용할 수 있습니다. 인수로 오류가있는 __exit __()을 호출합니다. args [0]에 오류가있는 경우 정리 코드를 실행 한 후 예외를 다시 발생시킵니다.

+1

예.하지만이 경우 contextlib에서 "generator did yield"가 발생합니다. – georg

+0

@ thg435, 합리적이지만 try ...와 같이 'yield'를 마침내 지정할 수 있습니다. finally 블록 – newtover

+0

이 있습니다. 해결 방법은 많이 있지만 문제의 근원은 건너 뛸 수 없다는 것입니다. 'with '블록 전체. 따라서 우리가 어떻게 든 '입력'에서 예외를 처리 할지라도 블록은 여전히 ​​실행되며 '없음'또는 다른 쓰레기가 인수로 실행됩니다. – 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 

이 같은 그것을 한 적이 : 상속 또는 복잡한 서브 루틴이 필요하지 않은 경우