Как сохранить значения traceback / sys.exc_info () в переменной?

127

Я хочу сохранить имя ошибки и детали трассировки в переменной. Вот моя попытка.

import sys
try:
    try:
        print x
    except Exception, ex:
        raise NameError
except Exception, er:
    print "0", sys.exc_info()[0]
    print "1", sys.exc_info()[1]
    print "2", sys.exc_info()[2]

Вывод:

0 <type 'exceptions.NameError'>
1 
2 <traceback object at 0xbd5fc8>

Желаемый результат:

0 NameError
1
2 Traceback (most recent call last):
  File "exception.py", line 6, in <module>
    raise NameError

PS Я знаю, что это легко сделать с помощью модуля трассировки, но я хочу узнать об использовании здесь объекта sys.exc_info () [2].

codersofthedark
источник
Вы пробовали напечатать sys.exc_info () [x] .__ str __ ()?
zmbq
3
Возможно, вы неправильно поняли, что происходит в вашей программе: то, что вы называете «объектом sys.exc_info () [2]», является экземпляром объекта трассировки (= вы уже используете модуль трассировки). Теперь вы можете управлять этим объектом, не используя вспомогательные функции в модуле трассировки, но это не меняет того факта, что вы все еще используете его. :)
mac
1
Итак, @mac, пожалуйста, помогите мне использовать доступ к значению из этого объекта с использованием вспомогательной функции или без нее.
codersofthedark 01
1
@dragosrsupercool - как я уже упоминал в своем ответе ниже, вам следует ознакомиться с документацией по трассировке . Я привел пример того, как получить данные в текстовом виде, но есть и другие методы объекта, которые позволяют вам извлекать имя исключения, строку кода и т. Д ... правильный выбор действительно зависит от того, как вы хотите манипулировать значение потом ...
mac
1
Мой ответ на другой вопрос может помочь проиллюстрировать детали - со ссылками! Для консервированных строк подходит стандартный модуль трассировки библиотеки. Если вы хотите получить подробности, прочтите source ( <python install path>/Lib/traceback.py) для получения дополнительной информации.
pythonlarry

Ответы:

180

Вот как я это делаю:

>>> import traceback
>>> try:
...   int('k')
... except:
...   var = traceback.format_exc()
... 
>>> print var
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'k'

Однако вам следует взглянуть на документацию по трассировке , так как вы можете найти там более подходящие методы, в зависимости от того, как вы хотите впоследствии обрабатывать свою переменную ...

макинтош
источник
1
Я искал метод без использования модуля трассировки. Есть ли способ просто распечатать данные трассировки из этой ссылки на объект? sys.exc_info () [2]
codersofthedark
Правильно, поэтому я подумал, что мы можем сделать что-то вроде sys.exc_info () [2] .format_exc (), но это не работает .. Таким образом, мне интересно, как я могу извлечь значение из объекта трассировки sys.exc_info () [2]. Любая идея?
codersofthedark
1
sys.exc_info () [2] .tb_text выдает ошибку следования -> AttributeError: объект 'traceback' не имеет атрибута 'tb_text'
codersofthedark
4
@dragosrsupercool - sys.exc_info()[2].tb_frame.f_code.co_names[3], но в этом нет никакого смысла ... Если tracebackв стандартной библиотеке есть вызываемый модуль , то для этого есть причина ... :)
mac
2
@codersofthedark traceback.format_exception(*sys.exc_info())- способ сделать это. Но это функционально эквивалентно traceback.format_exc().
wizzwizz4
25

sys.exc_info () возвращает кортеж с тремя значениями (тип, значение, трассировка).

  1. Здесь type получает тип исключения обрабатываемого исключения
  2. значение - это аргументы, которые передаются конструктору класса исключения
  3. traceback содержит информацию о стеке, например, место возникновения исключения и т. д.

Например, в следующей программе

try:

    a = 1/0

except Exception,e:

    exc_tuple = sys.exc_info()

Теперь, если мы напечатаем кортеж, значения будут такими.

  1. exc_tuple [0] значение будет " ZeroDivisionError "
  2. exc_tuple [1] значение будет " целочисленным делением или по модулю нуля" " (строка передается как параметр в класс исключения)
  3. exc_tuple [2] значение будет " объект отслеживания по (некоторый адрес памяти) "

Вышеуказанные данные также можно получить, просто распечатав исключение в строковом формате.

print str(e)
Keya
источник
Для Python3 exc_tuple [1] (также известный как значение) является экземпляром исключения, а не «строкой, переданной как параметр». См .: docs.python.org/3/library/sys.html#sys.exc_info
Цзинхао Ши
Разве это не должно быть «кроме исключения как e:»?
Хенрик
20

Используйте, traceback.extract_stack()если вам нужен удобный доступ к названиям модулей и функций и номерам строк.

Используйте, ''.join(traceback.format_stack())если вам просто нужна строка, которая выглядит как traceback.print_stack()результат.

Обратите внимание, что даже с помощью ''.join()вы получите многострочную строку, поскольку элементы из format_stack()файла contain \n. См. Вывод ниже.

Запомни import traceback .

Вот результат traceback.extract_stack(). Форматирование добавлено для удобства чтения.

>>> traceback.extract_stack()
[
   ('<string>', 1, '<module>', None),
   ('C:\\Python\\lib\\idlelib\\run.py', 126, 'main', 'ret = method(*args, **kwargs)'),
   ('C:\\Python\\lib\\idlelib\\run.py', 353, 'runcode', 'exec(code, self.locals)'),
   ('<pyshell#1>', 1, '<module>', None)
]

Вот результат ''.join(traceback.format_stack()). Форматирование добавлено для удобства чтения.

>>> ''.join(traceback.format_stack())
'  File "<string>", line 1, in <module>\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 126, in main\n
       ret = method(*args, **kwargs)\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 353, in runcode\n
       exec(code, self.locals)\n  File "<pyshell#2>", line 1, in <module>\n'
Натан
источник
4

Будьте осторожны при извлечении объекта исключения или объекта трассировки из обработчика исключений, поскольку это вызывает циклические ссылки и gc.collect()не удастся собрать. Это, по-видимому, является особой проблемой в среде записной книжки ipython / jupyter, где объект трассировки не очищается в нужный момент, и даже явный вызов gc.collect()in finallysection ничего не делает. И это огромная проблема, если у вас есть огромные объекты, память которых не восстанавливается из-за этого (например, исключения из памяти CUDA, которые без этого решения требуют полного перезапуска ядра для восстановления).

В общем, если вы хотите сохранить объект трассировки, вам необходимо очистить его от ссылок на locals(), например:

import sys, traceback, gc
type, val, tb = None, None, None
try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
# some cleanup code
gc.collect()
# and then use the tb:
if tb:
    raise type(val).with_traceback(tb)

В случае ноутбука jupyter вы должны сделать это, по крайней мере, внутри обработчика исключений:

try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
    raise type(val).with_traceback(tb)
finally:
    # cleanup code in here
    gc.collect()

Протестировано на Python 3.7.

ps проблема с ipython или jupyter notebook env заключается в том, что у него есть %tbмагия, которая сохраняет трассировку и делает ее доступной в любой момент позже. И в результате любые locals()кадры, участвующие в трассировке, не будут освобождены до тех пор, пока записная книжка не выйдет из записной книжки или другое исключение не перезапишет ранее сохраненную трассировку. Это очень проблематично. Он не должен сохранять трассировку без очистки своих фреймов. Исправление представлено здесь .

Stason
источник
3

Объект можно использовать как параметр в Exception.with_traceback()функции:

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))
Хансен Д'Сильва
источник