Як зберегти значення 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 Я знаю, що це можна зробити легко за допомогою модуля trackback, але я хочу знати об'єкт sys.exc_info () [2] тут.


Ви спробували надрукувати sys.exc_info () [x] .__ str __ ()?
zmbq

3
Можливо, ви неправильно зрозуміли, що відбувається у вашій програмі: те, що ви називаєте "sys.exc_info () [2] об'єкт" - це екземпляр об'єкта traceback (= ви вже використовуєте модуль trackback). Тепер ви можете маніпулювати цим об’єктом без використання допоміжних функцій у модулі відстеження, але це не змінює факту, що ви все ще використовуєте його. :)
mac

1
Тож @mac, будь ласка, допоможіть мені скористатися доступом до значення цього об'єкта з допомогою або без використання функції помічника.
codersofthedark

1
@dragosrsupercool - Як я вже згадував у своїй відповіді нижче, ви повинні ознайомитись із документацією про відстеження . Я наводив приклад того, як витягувати дані текстуально, але є й інші методи об’єкта, які дозволяють витягти ім'я винятку, рядок коду тощо ... правильний дійсно залежить від того, як ви хочете маніпулювати значення згодом ...
mac

1
Моя відповідь на інше питання може допомогти проілюструвати деталі - за посиланнями! Для консервованих рядків стандартний модуль відстеження бібліотеки здається нормальним. Якщо ви хочете отримати деталі, прочитайте джерело ( <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
Я шукав метод без використання модуля traceback. Чи є якийсь спосіб ми можемо просто надрукувати деталі відстеження з цього довідника про об'єкт? sys.exc_info () [2]
codersofthedark

Так, саме тому я думав, що ми можемо зробити щось на зразок sys.exc_info () [2] .format_exc (), але це не працює .. Таким чином, мені цікаво, як я можу отримати значення з об'єкта відстеження sys.exc_info () [2]. Будь-яка ідея?
codersofthedark

1
sys.exc_info () [2] .tb_text дає помилку слідування -> AttributeError: 'trackback' об’єкт не має атрибута 'tb_text'
codersofthedark

4
@dragosrsupercool - sys.exc_info()[2].tb_frame.f_code.co_names[3], але немає сенсу ні в чому ... Якщо tracebackв стандартній бібліотеці є модуль, який називається , є причина для цього ... :)
мак

2
@codersofthedark traceback.format_exception(*sys.exc_info())- це спосіб зробити це. Але це функціонально рівнозначно traceback.format_exc().
wizzwizz4

25

sys.exc_info () повертає кортеж з трьома значеннями (тип, значення, прослідкування).

  1. Тут тип отримує тип винятку, яким обробляється Виняток
  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)

Для Python3, exc_tuple [1] (aka значення) є екземпляром винятку, а не "Рядок, переданий як параметр". Дивіться: docs.python.org/3/library/sys.html#sys.exc_info
Jinghao Shi

Чи не повинно бути "крім винятку як e:"?
Генрік

20

Використовуйте, 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

Будьте обережні, коли ви виймаєте об'єкт винятку або об'єкт відстеження з обробника винятків, оскільки це викликає кругові посилання і gc.collect()не вдасться зібрати. Це, мабуть, викликає особливу проблему в середовищі ноутбука ipython / jupyter, де об'єкт відстеження не видаляється в потрібний час, і навіть явний виклик gc.collect()у finallyрозділі нічого не робить. І це величезна проблема, якщо у вас є кілька величезних об'єктів, які не відновлюють пам'ять через це (наприклад, CUDA не має винятків з пам'яті, що для відновлення цього рішення потрібен повний перезапуск ядра).

Загалом, якщо ви хочете зберегти об'єкт trackback, вам потрібно очистити його від посилань на 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)

Що стосується ноутбука з юпітером, ви повинні зробити це щонайменше всередині обробника винятків:

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 проблема з ipython або jupyter notebook env полягає в тому, що в ньому є %tbмагія, яка зберігає прослідкування і робить його доступним у будь-який момент пізніше. І як результат, будь-який locals()з усіх кадрів, що беруть участь у відстеженні, не буде звільнений, поки ноутбук не вийде або інший виняток не замінить раніше збережений backtrace. Це дуже проблематично. Він не повинен зберігати прослідкування без очищення кадрів. Виправлення, подане тут .


3

Об'єкт може бути використаний як параметр у Exception.with_traceback()функції:

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.