Jak zapisać wartości traceback / sys.exc_info () w zmiennej?

127

Chcę zapisać nazwę błędu i szczegóły śledzenia w zmiennej. Oto moja próba.

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]

Wynik:

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

Pożądane wyjście:

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

PS Wiem, że można to łatwo zrobić za pomocą modułu traceback, ale chcę poznać tutaj użycie obiektu sys.exc_info () [2].

codersofthedark
źródło
Czy próbowałeś wydrukować sys.exc_info () [x] .__ str __ ()?
zmbq
3
Mogłeś źle zrozumieć, co się dzieje w twoim programie: to, co nazywasz „sys.exc_info () [2] obiekt”, jest instancją obiektu traceback (= używasz już modułu traceback). Teraz możesz manipulować tym obiektem bez korzystania z funkcji pomocniczych w module traceback, ale to nie zmienia faktu, że nadal go używasz. :)
mac
1
Więc @mac, pomóż mi w korzystaniu z dostępu do wartości z tego obiektu z lub bez użycia funkcji pomocnika.
codersofthedark
1
@dragosrsupercool - Jak wspomniałem w mojej odpowiedzi poniżej, powinieneś spojrzeć na dokumentację śledzenia . Podałem przykład, jak pobierać dane tekstowo, ale istnieją inne metody obiektu, które pozwalają wyodrębnić nazwę wyjątku, wiersz kodu itp. ... właściwy naprawdę zależy od tego, jak chcesz manipulować wartość później ...
mac
1
Moja odpowiedź na inne pytanie może pomóc zilustrować szczegóły - za pomocą linków! W przypadku ciągów w puszkach standardowy moduł śledzenia biblioteki wydaje się być w porządku. Jeśli chcesz poznać szczegóły, przeczytaj źródło ( <python install path>/Lib/traceback.py), aby uzyskać więcej informacji.
pythonlarry

Odpowiedzi:

180

Oto jak to robię:

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

Powinieneś jednak przyjrzeć się dokumentacji śledzenia , ponieważ możesz znaleźć bardziej odpowiednie metody, w zależności od tego, jak chcesz później przetwarzać zmienną ...

prochowiec
źródło
1
Szukałem metody bez użycia modułu traceback. Czy jest jakiś sposób, abyśmy mogli po prostu wydrukować szczegóły śledzenia z tego odniesienia do obiektu? sys.exc_info () [2]
codersofthedark
Racja, właśnie dlatego pomyślałem, że możemy zrobić coś takiego jak sys.exc_info () [2] .format_exc (), ale to nie działa .. Dlatego zastanawiam się, jak mogę wyodrębnić wartość z obiektu śledzenia wstecz sys.exc_info () [2]. Dowolny pomysł?
codersofthedark
1
sys.exc_info () [2] .tb_text wyświetla błąd śledzenia -> AttributeError: obiekt 'traceback' nie ma atrybutu 'tb_text'
codersofthedark
4
@dragosrsupercool - sys.exc_info()[2].tb_frame.f_code.co_names[3]ale to w ogóle nie ma sensu ... Jeśli tracebackw standardowej bibliotece jest jakiś moduł , to jest ku temu powód ... :)
mac
2
@codersofthedark traceback.format_exception(*sys.exc_info())jest sposobem na to. Ale to funkcjonalnie odpowiada traceback.format_exc().
wizzwizz4
25

sys.exc_info () zwraca krotkę z trzema wartościami (typ, wartość, śledzenie).

  1. Tutaj typ pobiera typ wyjątku obsługiwanego wyjątku
  2. wartość to argumenty przekazywane do konstruktora klasy wyjątku
  3. traceback zawiera informacje o stosie, takie jak miejsce wystąpienia wyjątku itp.

Na przykład w następującym programie

try:

    a = 1/0

except Exception,e:

    exc_tuple = sys.exc_info()

Teraz, jeśli wydrukujemy krotkę, wartości będą takie.

  1. exc_tuple [0] ma wartość „ ZeroDivisionError
  2. Wartość exc_tuple [1] będzie „ dzieleniem całkowitym lub modulo przez zero ” (ciąg przekazany jako parametr do klasy wyjątku)
  3. Wartość exc_tuple [2] to „ obiekt śledzenia pod adresem (jakiś adres pamięci)

Powyższe szczegóły można również pobrać, po prostu drukując wyjątek w formacie łańcucha.

print str(e)
keya
źródło
W przypadku Python3 exc_tuple [1] (inaczej wartość) jest instancją wyjątku, a nie „ciągiem przekazanym jako parametr”. Zobacz: docs.python.org/3/library/sys.html#sys.exc_info
Jinghao Shi
Czy nie powinno to być „z wyjątkiem Wyjątku jako e:”?
Henrik
20

Użyj, traceback.extract_stack()jeśli chcesz mieć wygodny dostęp do nazw modułów i funkcji oraz numerów linii.

Użyj, ''.join(traceback.format_stack())jeśli potrzebujesz tylko ciągu, który wygląda jak dane traceback.print_stack()wyjściowe.

Zauważ, że nawet z ''.join()tobą otrzymasz ciąg wieloliniowy, ponieważ elementy format_stack()zawierają \n. Zobacz dane wyjściowe poniżej.

Pamiętaj o tym import traceback.

Oto wynik z traceback.extract_stack(). Dodano formatowanie w celu zwiększenia czytelności.

>>> 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)
]

Oto wynik z ''.join(traceback.format_stack()). Dodano formatowanie w celu zwiększenia czytelności.

>>> ''.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'
Nathan
źródło
4

Zachowaj ostrożność podczas wyjmowania obiektu wyjątku lub obiektu śledzenia z procedury obsługi wyjątków, ponieważ powoduje to cykliczne odwołania i gc.collect()nie powiedzie się ich zebranie. Wydaje się, że jest to szczególny problem w środowisku notebooków ipython / jupyter, w którym obiekt śledzenia nie jest czyszczony we właściwym czasie, a nawet jawne wywołanie gc.collect()w finallysekcji nic nie daje. I to jest ogromny problem, jeśli masz jakieś ogromne obiekty, które nie odzyskują pamięci z tego powodu (np. CUDA z wyjątków pamięci, które bez tego rozwiązania wymagają pełnego restartu jądra, aby odzyskać).

Ogólnie rzecz biorąc, jeśli chcesz zapisać obiekt śledzenia, musisz wyczyścić go z odniesień do locals(), na przykład:

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)

W przypadku notatnika jupyter musisz to zrobić przynajmniej wewnątrz obsługi wyjątków:

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()

Przetestowano w Pythonie 3.7.

ps problem z ipython lub jupyter notebook env polega na tym, że ma %tbmagię, która zapisuje dane śledzenia i udostępnia je w dowolnym momencie później. W rezultacie żadna locals()we wszystkich ramkach uczestniczących w śledzeniu nie zostanie zwolniona, dopóki notebook nie wyjdzie lub inny wyjątek nadpisze poprzednio zapisany ślad. To jest bardzo problematyczne. Nie powinien przechowywać danych śledzenia bez czyszczenia ramek. Poprawka przesłana tutaj .

stason
źródło
3

Obiekt może służyć jako parametr w Exception.with_traceback()funkcji:

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))
Hansen D'Silva
źródło