Извлечение данных трассировки из объекта исключения
учитывая объект исключения (неизвестного происхождения)есть ли способ получить его трассировку? У меня есть такой код:
def stuff():
try:
.....
return useful
except Exception as e:
return e
result = stuff()
if isinstance(result, Exception):
result.traceback <-- How?
Как я могу извлечь трассировку из объекта исключения, как только он у меня есть?
4 ответов:
ответ на этот вопрос зависит от версии Python вы используете.
В Python 3
это просто: исключения оснащены
__traceback__атрибут, содержащий обратную трассировку. Этот атрибут также доступен для записи и может быть удобно установлен с помощьюwith_tracebackметодом исключения:raise Exception("foo occurred").with_traceback(tracebackobj)эти функции минимально описаны как часть
raiseдокументация.вся заслуга в этом часть ответа должна перейти к Виктору, который впервые опубликовал эту информацию. Я включаю его здесь только потому, что этот ответ застрял наверху, и 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и a пользовательское исключение для хранения информации. Но я не совсем уверен, как это будет работать.по правде говоря, поймать и вернуть исключение-это довольно необычная вещь. Это может быть признаком того, что вам все равно нужно рефакторинг.
С Python 3.0[PEP 3109] встроенный класс
Exceptionесть__traceback__атрибут, который содержитtraceback object(С Python 3.2.3):>>> try: ... raise Exception() ... except Exception as e: ... tb = e.__traceback__ ... >>> tb <traceback object at 0x00000000022A9208>проблема в том, что после погугли
на в Python 3 документации__traceback__некоторое время я нашел только несколько статей, но ни одна из них не описывает, следует ли (не) использовать__traceback__.raiseговорит что:объект трассировки обычно создается автоматически при возникновении исключения и прикрепляется к нему как
__traceback__атрибут, который доступен для записи.поэтому я предполагаю, что он должен быть использован.
есть очень веская причина, по которой трассировка не сохраняется в исключении; поскольку трассировка содержит ссылки на локальные объекты своего стека, это приведет к циклической ссылке и (временной) утечке памяти до тех пор, пока не начнется циклический GC. (Вот почему вы должны никогда не храните обратную трассировку в локальной переменной.)
единственное, о чем я могу думать, было бы для вас monkeypatch
stuffглобалы так что, когда он думает, что он ловитExceptionэто на самом деле ловить специализированный тип и исключение распространяется на вас как вызывающий:module_containing_stuff.Exception = type("BogusException", (Exception,), {}) try: stuff() except Exception: import sys print sys.exc_info()
способ получить трассировку в виде строки из объекта исключения в Python 3:
import traceback # `e` is an exception object that you get from somewhere traceback_str = ''.join(traceback.format_tb(e.__traceback__))
traceback.format_tb(...)возвращает список строк.''.join(...)соединяет их вместе. Для получения дополнительной информации, пожалуйста, посетите: https://docs.python.org/3/library/traceback.html#traceback.format_exc
Comments