Как сохранить значения 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
PS Я знаю, что это легко сделать с помощью модуля traceback, но я хочу узнать об использовании объекта sys.exc_info()[2] здесь.
3 ответа
Вот как я это делаю:
>>> 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'
Однако вам следует взглянуть на документацию по трассировке, поскольку вы можете найти там более подходящие методы, в зависимости от того, как вы хотите обработать вашу переменную впоследствии...
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)
Использование 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'
Будьте осторожны, когда вы берете объект исключения или объект трассировки из обработчика исключений, так как это вызывает циклические ссылки и 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()
Протестировано с питоном 3.7.
ps проблема с env ноутбука ipython или jupyter в том, что он имеет %tb
магия, которая сохраняет трассировку и делает ее доступной в любой момент позже. И в результате любой locals()
во всех кадрах, участвующих в трассировке, не будут освобождены, пока ноутбук не выйдет или другое исключение не перезапишет ранее сохраненную трассировку. Это очень проблематично. Он не должен хранить трассировку без очистки своих кадров. Исправление представлено здесь.
Объект можно использовать как параметр в Exception.with_traceback()
функция:
except Exception as e:
tb = sys.exc_info()
print(e.with_traceback(tb[2]))
Мой ответ на другой вопрос может помочь проиллюстрировать детали - со ссылками! Для консервированных строк стандартный модуль трассировки библиотеки выглядит нормально. Если вы хотите получить подробности, прочитайте источник (<python install path>/Lib/traceback.py
) для получения дополнительной информации.