예외 개체에서 추적 정보 추출


111

출처를 알 수없는 예외 객체가 주어지면 추적을 얻을 수있는 방법이 있습니까? 다음과 같은 코드가 있습니다.

def stuff():
   try:
       .....
       return useful
   except Exception as e:
       return e

result = stuff()
if isinstance(result, Exception):
    result.traceback <-- How?

Exception 객체가있는 경우 어떻게 트레이스 백을 추출 할 수 있습니까?

답변:


92

이 질문에 대한 답은 사용중인 Python 버전에 따라 다릅니다.

Python 3에서

간단합니다. 예외 __traceback__에는 트레이스 백을 포함 하는 속성이 제공됩니다. 이 속성은 쓰기 가능하며 with_traceback예외 방법을 사용하여 편리하게 설정할 수 있습니다 .

raise Exception("foo occurred").with_traceback(tracebackobj)

이러한 기능은 raise문서의 일부로 최소한으로 설명됩니다 .

답변의이 부분에 대한 모든 크레딧 은이 정보처음 게시 한 Vyctor에게 가야합니다 . 이 답변이 맨 위에 붙어 있고 Python 3가 점점 보편화되고 있기 때문에 여기에 포함되었습니다.

Python 2에서

성가 시게 복잡합니다. 역 추적의 문제점은 스택 프레임에 대한 참조를 가지고 있고, 스택 프레임은 프레임 스택에 대한 참조를 가지고 역 추적에 대한 참조해야한다는 것입니다 에 ... 참조가 당신이 아이디어를 얻을 수 있습니다. 이로 인해 가비지 수집기에 문제가 발생합니다. ( 처음 지적한 ecatmur 에게 감사드립니다 .)

이 문제를 해결하는 좋은 방법은 절 을 떠난 후 외과 적 으로주기를 중단except 하는 것입니다. 이것이 Python 3이하는 일입니다. Python 2 솔루션은 훨씬 더 추악합니다. 내에서만 작동sys.exc_info() 하는 임시 함수를 제공 합니다 . 예외, 예외 유형 및 현재 처리중인 예외에 대한 추적을 포함하는 튜플을 반환합니다.except

따라서 except절 내부에 있다면 모듈 sys.exc_info()과 함께의 출력을 사용하여 traceback다양한 유용한 작업을 수행 할 수 있습니다.

>>> import sys, traceback
>>> def raise_exception():
...     try:
...         raise Exception
...     except Exception:
...         ex_type, ex, tb = sys.exc_info()
...         traceback.print_tb(tb)
...     finally:
...         del tb
... 
>>> raise_exception()
  File "<stdin>", line 3, in raise_exception

당신의 편집 표시하지만, 당신은 추적 얻을하려는 이 후, 당신의 예외가 처리되지 않은 경우 인쇄 한을 이미 처리되었습니다. 그것은 훨씬 더 어려운 질문입니다. 불행히도 예외가 처리되지 않으면 sys.exc_info반환 (None, None, None)됩니다. 다른 관련 sys속성도 도움이되지 않습니다. sys.exc_traceback예외가 처리되지 않으면 더 이상 사용되지 않으며 정의되지 않습니다. sys.last_traceback완벽 해 보이지만 대화 형 세션에서만 정의 된 것처럼 보입니다.

예외 발생 방법을 제어 할 수있는 경우 inspect사용자 지정 예외 를 사용하여 일부 정보를 저장할 수 있습니다. 그러나 나는 그것이 어떻게 작동하는지 완전히 확신하지 못합니다.

진실을 말하면 예외를 포착하고 반환하는 것은 특이한 일입니다. 어쨌든 리팩토링해야한다는 신호일 수 있습니다.


나는 예외를 반환하는 것이 어떻게 든 비 전통적이라는 데 동의하지만 이에 대한 근거에 대해서는 다른 질문 을 참조하십시오 .
georg

@ thg435, 좋아, 이것은 더 의미가 있습니다. 다른 질문에 제안 sys.exc_info하는 콜백 접근 방식 과 함께 사용 하는 위의 솔루션을 고려하십시오 .
senderle


69

이후 파이썬 3.0 [3109 PEP] (가) 클래스에 내장은 Exception__traceback__포함 속성 traceback object(파이썬 3.2.3로)를 :

>>> try:
...     raise Exception()
... except Exception as e:
...     tb = e.__traceback__
...
>>> tb
<traceback object at 0x00000000022A9208>

문제는 인터넷 검색__traceback__ 후 잠시 동안 몇 개의 기사 만 찾았지만 .NET을 사용해야하는 이유 또는 이유를 설명하는 기사가 없다는 것 __traceback__입니다.

그러나에 대한 Python 3 문서는 다음과raise 같이 말합니다.

트레이스 백 객체는 일반적으로 예외가 발생하고 __traceback__쓰기 가능한 속성 으로 첨부 될 때 자동으로 생성됩니다 .

그래서 나는 그것이 사용되어야한다고 생각합니다.


4
예, 사용하기위한 것입니다. From What 's New In Python 3.0 "PEP 3134 : 예외 객체는 이제 트레이스 백 속성 으로 트레이스 백을 저장 합니다. 이것은 이제 예외 객체에 예외와 관련된 모든 정보가 포함되어 있으며 sys.exc_info ()를 사용할 이유가 적다는 것을 의미합니다. 후자는 제거되지 않았지만). "
마치에이 Szpakowski

이 답변이 왜 그렇게 주저하고 모호한 지 이해하지 못합니다. 문서화 된 속성입니다. 왜이 것 없다 "사용하기위한 것"수?
Mark Amery

2
@MarkAmery 아마도 __공개 속성이 아니라 구현 세부 사항임을 나타내는 이름이 있습니까?
Basic

4
@Basic은 여기에서 나타내는 것이 아닙니다. 관습 적으로 파이썬 __foo에서는 private 메서드이지만 __foo__(뒤에 밑줄도 함께) "마법"메서드 (개인이 아님)입니다.
Mark Amery

1
참고로이 __traceback__속성은 GC 영향없이 원하는대로 사용하기에 100 % 안전합니다. 문서에서 그것을 말하기는 어렵지만 ecatmur는 확실한 증거를 찾았 습니다 .
senderle dec.


21

제쳐두고, 터미널에 인쇄 된 것처럼 전체 트레이스 백을 실제로 얻으려면 다음을 원합니다.

>>> try:
...     print(1/0)
... except Exception as e:
...     exc = e
...
>>> exc
ZeroDivisionError('division by zero')
>>> tb_str = traceback.format_exception(etype=type(exc), value=exc, tb=exc.__traceback__)
>>> tb_str
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: division by zero\n']
>>> print("".join(tb_str))
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

format_tb위의 답변으로 사용 하면 더 적은 정보를 얻을 수 있습니다.

>>> tb_str = "".join(traceback.format_tb(exc.__traceback__))
>>> print("".join(tb_str))
  File "<stdin>", line 2, in <module>

4
드디어! 이것이 최고의 답변이어야합니다. 고마워요, 다니엘!
Dany

3
아아, 나는 이것을 발견하기 전에 이것을 알아 내려고 지난 20 분을 보냈다. :-) etype=type(exc)이제 생략 할 수있다. btw : "버전 3.5에서 변경 : etype 인자는 무시되고 값의 유형에서 유추된다." docs.python.org/3.7/library/… Python 3.7.3에서 테스트되었습니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

8

트레이스 백이 예외에 저장되지 않는 아주 좋은 이유가 있습니다. 트레이스 백은 스택의 로컬에 대한 참조를 보유하기 때문에 순환 GC가 시작될 때까지 순환 참조 및 (임시) 메모리 누수가 발생합니다. (이것이 트레이스 백을 로컬 변수에 저장 해서는 안되는 이유 입니다.)

내가 생각할 수있는 유일한 것은 당신이 monkeypatch stuff의 전역을 잡는 것이라고 생각할 때 Exception실제로 특수 유형을 잡는 것이고 예외는 호출자로서 당신에게 전파되는 것입니다.

module_containing_stuff.Exception = type("BogusException", (Exception,), {})
try:
    stuff()
except Exception:
    import sys
    print sys.exc_info()

7
이것은 잘못되었습니다. Python 3은 트레이스 백 객체를 e.__traceback__.
Glenn Maynard

6
@GlennMaynard Python 3 exceptPEP 3110에 따라 블록 을 종료 할 때 예외 대상을 삭제하여 문제를 해결합니다 .
ecatmur
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.