Ответ на этот вопрос зависит от версии 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
и настраиваемое исключение для хранения некоторой информации. Но я не совсем уверен, как это сработает.
По правде говоря, перехват и возврат исключения - дело необычное. Это может быть признаком того, что вам все равно нужно провести рефакторинг.
sys.exc_info
в сочетании с подходом обратного вызова, который я предлагаю по вашему другому вопросу.Начиная с Python 3.0 [PEP 3109] встроенный класс
Exception
имеет__traceback__
атрибут, который содержитtraceback object
(с Python 3.2.3):Проблема в том, что после некоторого времени в Google
__traceback__
я нашел только несколько статей, но ни одна из них не описывает, стоит ли вам (не) использовать__traceback__
.Однако в документации Python 3
raise
говорится, что:Так что я предполагаю, что это предназначено для использования.
источник
__
в названии указано, что это деталь реализации, а не публичное свойство?__foo
это частный метод, но__foo__
(также с завершающими символами подчеркивания) это «волшебный» метод (а не частный).__traceback__
атрибут на 100% безопасен для использования, как вам нравится, без последствий для сборки мусора. Трудно сказать это из документации, но ecatmur нашел веские доказательства .Способ получить трассировку в виде строки из объекта исключения в Python 3:
traceback.format_tb(...)
возвращает список строк.''.join(...)
объединяет их вместе. Для получения дополнительной информации посетите: https://docs.python.org/3/library/traceback.html#traceback.format_tb.источник
Кстати, если вы действительно хотите получить полную трассировку, как если бы вы видели ее на своем терминале, вам нужно следующее:
Если вы используете
format_tb
приведенные выше ответы, вы получите меньше информации:источник
etype=type(exc)
теперь можно опустить, кстати: «Изменено в версии 3.5: аргумент etype игнорируется и выводится из типа значения». docs.python.org/3.7/library/… Протестировано в Python 3.7.3.Есть очень веская причина, по которой трассировка не сохраняется в исключении; поскольку трассировка содержит ссылки на локальные переменные своего стека, это приведет к циклической ссылке и (временной) утечке памяти до тех пор, пока не сработает циклический сборщик мусора (вот почему никогда не следует сохранять трассировку в локальной переменной ).
Единственное, о чем я могу думать, - это использовать
stuff
глобальные переменные monkeypatch , чтобы, когда он думает, что ловит,Exception
он на самом деле перехватывает специализированный тип, и исключение распространяется на вас как вызывающего:источник
e.__traceback__
.except
блока согласно PEP 3110 .