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

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

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

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

Ответ 1

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

>>> 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'

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

Ответ 2

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

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

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

try:

    a = 1/0

except Exception,e:

    exc_tuple = sys.exc_info()

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

  • Значение exc_tuple [0] будет " ZeroDivisionError"
  • Значение exc_tuple [1] будет " целочисленным делением или по модулю нулем" (строка, переданная как параметр в класс исключения)
  • Значение exc_tuple [2] будет " трекбэк-объект в (на некотором адресе памяти)"

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

print str(e)

Ответ 3

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

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

Обратите внимание, что даже при ''.join() вы получите многострочную строку, так как элементы format_stack() содержат \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

Мой ответ на другой вопрос может помочь проиллюстрировать детали - со ссылками! Для консервированных строк стандартный модуль трассировки библиотеки выглядит нормально. Если вы хотите получить информацию, прочитайте источник (<python install path>/Lib/traceback.py) для получения дополнительной информации.

Ответ 5

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

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))

Ответ 6

Будьте осторожны, когда вы берете объект исключения или объект трассировки из обработчика исключений, так как это вызывает циклические ссылки, и gc.collect() не сможет собрать. Это, как представляется, является особой проблемой в среде записной книжки ipython/jupyter, где объект трассировки не очищается в нужное время, и даже явный вызов gc.collect() в разделе finally ничего не делает. И это огромная проблема, если у вас есть какие-то огромные объекты, которые из-за этого не возвращают свою память (например, 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 проблема с env для ноутбуков ipython или jupyter заключается в том, что в нем есть %tb magic, который сохраняет трассировку и делает ее доступной в любой момент позже. И в результате любые locals() во всех кадрах, участвующих в трассировке, не будут освобождены, пока ноутбук не выйдет или другое исключение не перезапишет ранее сохраненную трассировку. Это очень проблематично. Он не должен хранить трассировку без очистки своих кадров. Исправление представлено здесь.