Извлечение данных трассировки из объекта исключения



учитывая объект исключения (неизвестного происхождения)есть ли способ получить его трассировку? У меня есть такой код:



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

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


Как я могу извлечь трассировку из объекта исключения, как только он у меня есть?

551   4  

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>

проблема в том, что после погугли __traceback__ некоторое время я нашел только несколько статей, но ни одна из них не описывает, следует ли (не) использовать __traceback__.

на в Python 3 документации 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

    Ничего не найдено.