중첩 된 try / except 블록에서 예외를 다시 발생시키는 방법은 무엇입니까?


109

예외를 다시 발생 raise시키려면 각 except블록 에서 인수없이 간단히 사용한다는 것을 알고 있습니다. 그러나 다음과 같은 중첩식이 주어지면

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # I'd like to raise the SomeError as if plan_B()
                 # didn't raise the AlsoFailsError

SomeError스택 추적을 끊지 않고 어떻게 다시 올릴 수 있습니까? raise이 경우에만 더 최근의 AlsoFailsError. 또는이 문제를 피하기 위해 코드를 리팩토링하려면 어떻게해야합니까?


2
성공과 예외 plan_B를 반환 True하는 다른 함수를 넣어 보셨습니까 False? 그러면 외부 except블록이 될 수 있습니다if not try_plan_B(): raise
Drew McGowen 2013-08-12

@DrewMcGowen 불행하게도 더 현실적인 경우이 임의의 객체를 받아들이는 함수 내부에 있다는 것입니다 arg내가 전화하려고 할 것 arg.plan_B()올릴 수 AttributeError인해이 할 arg계획 B를 제공하지 않는
토비아스 Kienzler


@Paco 감사합니다, 나는 것이다 (하지만 답은 이미 간단한 방법을 보여줍니다)
토비아스 Kienzler

@DrewMcGowen 나는 귀하의 의견을 기반으로 답변을 작성했는데 user4815162342의 답변 보다 비단뱀 처럼 보입니다 . 하지만 그 때문에 내도 반환 값을 가지고 싶어하고 허용하는의 plan_B인상 예외
토비아스 Kienzler에게

답변:


131

Python 3 raise e부터는 트레이스 백이 예외에 저장되므로 간단한 방법으로 (대부분) 올바른 작업을 수행합니다.

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # or raise e from None - see below

생성 된 트레이스 백에는 SomeError처리 중 발생한 추가 알림이 포함됩니다 AlsoFailsError( raise e내부에 있기 때문에 except AlsoFailsError). 실제로 발생한 것은에서 AlsoFailsError복구를 시도하는 동안 발생 하고 처리 하는 다른 방법이기 때문에 잘못된 것 입니다 SomeError. 포함하지 않는 역 추적을 얻으려면 AlsoFailsError, 교체 raise e와 함께 raise e from None.

Python 2에서는 예외 유형, 값 및 역 추적을 지역 변수에 저장하고 다음의 3 개 인수 형식을 사용합니다raise .

try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        raise t, v, tb

완벽합니다. 바로 여기 에서도 찾았 습니다 . 감사합니다! 제안이 비록 raise self.exc_info[1], None, self.exc_info[2]self.exc_info = sys.exc_info()퍼팅 - [1]어떤 이유로 첫 번째 위치로
토비아스 Kienzler을

3
@TobiasKienzler raise t, None, tb는 예외 값을 잃고 raise유형에서 다시 인스턴스화하여 덜 구체적인 (또는 단순히 잘못된) 예외 값을 제공합니다. 예를 들어 발생한 예외가 KeyError("some-key")이면 KeyError()추적에서 정확히 누락 된 키를 다시 발생 시키고 생략합니다.
user4815162342 2013-08-12

3
@TobiasKienzler 파이썬 3에서 raise v.with_traceback(tb). (이 다시 인스턴스화에 값을 제시 제외하고는 귀하의 의견도, 많이 말한다.)
user4815162342

2
또한 sys.exc_info()지역 변수 에 저장하지 말라는 빨간색 경고 는 Python 2.0 (13 년 전에 출시됨) 이전에는 의미가 있었지만 오늘날 우스꽝 스럽습니다. 모든 사소하지 않은 Python 라이브러리는 일시 중지없이주기를 생성하고 올바른 정리에 의존하기 때문에 현대 Python은주기 수집기 없이는 거의 쓸모가 없습니다.
user4815162342

1
@ user4815162342 "raise e from None"을 작성하여 "다른 오류가 발생했습니다"중첩 오류를 없앨 수 있습니다.
Matthias Urlichs

19

허용되는 솔루션 이 맞 더라도 .NET Framework를 사용하여 Python 2 + 3 솔루션이있는 Six 라이브러리 를 가리키는 것이 좋습니다 six.reraise.

육. 리 레이즈 ( exc_type , exc_value , exc_traceback = 없음)

다른 트레이스 백을 사용하여 예외를 다시 발생시킵니다. [...]

따라서 다음과 같이 작성할 수 있습니다.

import six


try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        six.reraise(t, v, tb)

1
좋은 점-Six에 대해 말하면 실패한 six.raise_from정보를 포함하려는 경우 에도 사용할 수 있습니다 plan_B().
Tobias Kienzler

1
@TobiasKienzler : 다른 사용법이라고 생각합니다 six.raise_from. 이전 예외와 연결된 새 예외를 만들면 다시 발생 하지 않으므로 추적이 다릅니다.
Laurent LAPORTE

1
내 요점은 정확히-당신 reraisesomething()던진 인상을 SomeError받으면 raise_from이것이 plan_B()실행되었지만 AlsoFailsError. 따라서 사용 사례에 따라 다릅니다. 나는 생각 raise_from디버깅을 쉽게 만든다 것
토비아스 Kienzler

9

드류 McGowen의 제안 하지만, (반환 값은 일반적인 경우를 돌보는 s존재), 여기에 대한 대안의 user4815162342의 대답은 :

try:
    s = something()
except SomeError as e:
    def wrapped_plan_B():
        try:
            return False, plan_B()
        except:
            return True, None
    failed, s = wrapped_plan_B()
    if failed:
        raise

1
이 접근 방식의 좋은 점은 Python 2와 3에서 변경되지 않고 작동한다는 것입니다.
user4815162342

2
@ user4815162342 좋은 점 :) Python3에서는 고려할 것이지만 raise from스택 추적을 통해 계획 B가 실패했습니다. 그건 그렇고 파이썬 2에서 에뮬레이션 할 수 있습니다 .
Tobias Kienzler

5

Python 3.5+는 어쨌든 트레이스 백 정보를 오류에 첨부하므로 더 이상 별도로 저장할 필요가 없습니다.

>>> def f():
...   try:
...     raise SyntaxError
...   except Exception as e:
...     err = e
...     try:
...       raise AttributeError
...     except Exception as e1:
...       raise err from None
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in f
  File "<stdin>", line 3, in f
SyntaxError: None
>>> 

2
질문에 관한 또 다른 동안 일어나는 예외 except. 하지만 당신이 맞아 내가 교체 할 때err = e 라고 말하면, raise AttributeError먼저 SyntaxError스택 트레이스 를 얻은 다음 a During handling of the above exception, another exception occurred:와 스택 트레이스 를 얻습니다 AttributeError. 알아두면 좋겠지 만 불행히도 3.5 이상이 설치되어있을 수는 없습니다. PS : FF-verstehen nicht는 도이치 vermutlich nicht)
토비아스 Kienzler

좋아, 그래서 다른 예외를 발생시키기 위해 예제를 변경했는데, (원래 질문에서 요청한대로) 첫 번째 예외를 다시 발생 시키면 무시됩니다.
Matthias Urlichs 2017

3
@TobiasKienzler 3.5+ (내가 변경 한)는 전 세계적으로 인정받는 형식 인 것 같습니다. denkst du 였나요? ;)
linusg 2018

@linusg : 합의
토비아스 Kienzler
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.