Odpowiedź na to pytanie zależy od używanej wersji Pythona.
W Pythonie 3
To proste: wyjątki są wyposażone w __traceback__
atrybut zawierający dane śledzenia. Ten atrybut jest również zapisywalny i można go wygodnie ustawić za pomocą with_traceback
metody wyjątków:
raise Exception("foo occurred").with_traceback(tracebackobj)
Te funkcje są minimalnie opisane jako część raise
dokumentacji.
Cała zasługa tej części odpowiedzi należy się Vyctorowi, który jako pierwszy opublikował te informacje . Uwzględniam to tutaj tylko dlatego, że ta odpowiedź utknęła na górze, a Python 3 staje się coraz bardziej powszechny.
W Pythonie 2
To irytująco złożone. Problem z trackbackami polega na tym, że mają one odniesienia do ramek stosu, a ramki stosu mają odniesienia do elementów traceback, które mają odniesienia do ramek stosu, które mają odniesienia do ... rozumiesz. Powoduje to problemy dla odśmiecacza. (Dzięki ecatmur za pierwsze wskazanie).
Dobrym sposobem rozwiązania tego problemu byłoby chirurgiczne przerwanie cyklu po opuszczeniu except
klauzuli, co robi Python 3. Rozwiązanie w Pythonie 2 jest dużo brzydsze: masz do dyspozycji funkcję ad-hoc sys.exc_info()
, która działa tylko wewnątrz except
klauzuli . Zwraca krotkę zawierającą wyjątek, typ wyjątku i ślad zwrotny dla dowolnego wyjątku, który jest obecnie obsługiwany.
Więc jeśli jesteś wewnątrz except
klauzuli, możesz użyć danych wyjściowych sys.exc_info()
wraz z traceback
modułem do zrobienia różnych przydatnych rzeczy:
>>> import sys, traceback
>>> def raise_exception():
... try:
... raise Exception
... except Exception:
... ex_type, ex, tb = sys.exc_info()
... traceback.print_tb(tb)
... finally:
... del tb
...
>>> raise_exception()
File "<stdin>", line 3, in raise_exception
Ale jak wskazuje twoja edycja, próbujesz uzyskać dane śledzenia, które zostałyby wydrukowane, gdyby twój wyjątek nie został obsłużony, po tym, jak został już obsłużony. To znacznie trudniejsze pytanie. Niestety sys.exc_info
wraca, (None, None, None)
gdy żaden wyjątek nie jest obsługiwany. Inne powiązane sys
atrybuty też nie pomagają. sys.exc_traceback
jest przestarzały i niezdefiniowany, gdy żaden wyjątek nie jest obsługiwany; sys.last_traceback
wydaje się idealny, ale wydaje się, że jest definiowany tylko podczas sesji interaktywnych.
Jeśli możesz kontrolować sposób wywoływania wyjątku, możesz użyć inspect
i niestandardowego wyjątku do przechowywania niektórych informacji. Ale nie jestem do końca pewien, jak to zadziała.
Prawdę mówiąc, wyłapywanie i zwracanie wyjątku jest czymś niezwykłym. Może to oznaczać, że i tak musisz dokonać refaktoryzacji.
sys.exc_info
w połączeniu z podejściem oddzwaniania, które proponuję w przypadku innego pytania.Od Pythona 3.0 [PEP 3109] wbudowana klasa
Exception
ma__traceback__
atrybut, który zawieratraceback object
(w Pythonie 3.2.3):Problem w tym, że po dłuższym szukaniu
__traceback__
w Google znalazłem tylko kilka artykułów, ale żaden z nich nie opisuje, czy i dlaczego powinieneś (nie) używać__traceback__
.Jednak dokumentacja Python 3 dla
raise
mówi, że:Więc zakładam, że ma być używany.
źródło
__
w nazwie znajduje się informacja, że jest to szczegół implementacji, a nie właściwość publiczna?__foo
jest to metoda prywatna, ale__foo__
(również z końcowymi podkreśleniami) jest metodą „magiczną” (a nie prywatną).__traceback__
atrybut jest w 100% bezpieczny w użyciu w dowolny sposób, bez wpływu na GC. Trudno to stwierdzić z dokumentacji, ale ecatmur znalazł mocne dowody .Sposób na uzyskanie danych śledzenia w postaci ciągu znaków z obiektu wyjątku w Pythonie 3:
traceback.format_tb(...)
zwraca listę ciągów.''.join(...)
łączy je razem. Więcej informacji można znaleźć pod adresem : https://docs.python.org/3/library/traceback.html#traceback.format_tbźródło
Na marginesie, jeśli chcesz faktycznie uzyskać pełne śledzenie w postaci wydrukowanej na terminalu, potrzebujesz tego:
Jeśli zastosujesz
format_tb
powyższe odpowiedzi, otrzymasz mniej informacji:źródło
etype=type(exc)
można teraz pominąć btw: "Zmieniono w wersji 3.5: argument etype jest ignorowany i wywnioskowany z typu wartości." docs.python.org/3.7/library/ ... Przetestowano w Pythonie 3.7.3.Jest bardzo dobry powód, dla którego śledzenie nie jest przechowywane w wyjątku; ponieważ traceback przechowuje odniesienia do ustawień lokalnych swojego stosu, spowodowałoby to cykliczne odwołanie i (tymczasowy) wyciek pamięci, dopóki cykliczny GC nie zadziała. (Dlatego nigdy nie należy przechowywać danych śledzenia w zmiennej lokalnej ).
Jedyną rzeczą, o której mogę pomyśleć, byłaby dla ciebie globalna
stuff
monkeypatch, więc kiedy myśli, że łapieException
, w rzeczywistości łapie wyspecjalizowany typ, a wyjątek propaguje się do ciebie jako dzwoniącego:źródło
e.__traceback__
.except
bloku, zgodnie z PEP 3110 .