Piszę moduł i chcę mieć ujednoliconą hierarchię wyjątków dla wyjątków, które może on wywołać (np. Dziedziczenie z FooError
klasy abstrakcyjnej dla wszystkich foo
wyjątków specyficznych dla modułu). Pozwala to użytkownikom modułu na wychwycenie tych wyjątków i wyraźną obsługę ich w razie potrzeby. Ale wiele wyjątków zgłoszonych z modułu jest zgłaszanych z powodu innego wyjątku; np. niepowodzenie w wykonaniu jakiegoś zadania z powodu błędu OSError w pliku.
To, czego potrzebuję, to „zawinąć” przechwycony wyjątek w taki sposób, aby miał inny typ i inny komunikat , aby informacje były dostępne na wyższych poziomach hierarchii propagacji przez wszystko, co przechwytuje wyjątek. Ale nie chcę stracić istniejącego typu, komunikatu i śladu stosu; to wszystko jest przydatne dla kogoś, kto próbuje debugować problem. Obsługa wyjątków najwyższego poziomu nie jest dobra, ponieważ próbuję udekorować wyjątek, zanim przejdzie dalej w górę stosu propagacji, a program obsługi najwyższego poziomu jest za późno.
Jest to częściowo rozwiązane poprzez wyprowadzenie foo
specyficznych typów wyjątków mojego modułu z istniejącego typu (np. class FooPermissionError(OSError, FooError)
), Ale to nie ułatwia pakowania istniejącego wystąpienia wyjątku w nowy typ ani modyfikowania komunikatu.
Python PEP 3134 „Exception Chaining and Embedded Tracebacks” omawia zmianę zaakceptowaną w Pythonie 3.0 dla „łańcuchowego” obiektów wyjątków, aby wskazać, że nowy wyjątek został zgłoszony podczas obsługi istniejącego wyjątku.
To, co próbuję zrobić, jest powiązane: potrzebuję tego również we wcześniejszych wersjach Pythona i nie potrzebuję go do tworzenia łańcuchów, a tylko do polimorfizmu. Jaki jest właściwy sposób, aby to zrobić?
źródło
except Exception as e
->raise type(e), type(e)(e.message + custom_message), sys.exc_info()[2]
-> to rozwiązanie pochodzi z innego pytania SO . To nie jest ładne, ale funkcjonalne.Odpowiedzi:
Python 3 wprowadził łańcuch wyjątków (zgodnie z opisem w PEP 3134 ). Dzięki temu podczas zgłaszania wyjątku można zacytować istniejący wyjątek jako „przyczynę”:
W ten sposób przechwycony wyjątek staje się częścią (jest „przyczyną”) nowego wyjątku i jest dostępny dla każdego kodu przechwytującego nowy wyjątek.
Korzystając z tej funkcji,
__cause__
ustawiany jest atrybut. Wbudowana procedura obsługi wyjątków wie również, jak zgłosić „przyczynę” i „kontekst” wyjątku wraz ze śledzeniem.W Pythonie 2 wydaje się, że ten przypadek użycia nie ma dobrej odpowiedzi (jak opisali Ian Bicking i Ned Batchelder ). Porażka.
źródło
try: return 2 / 0 except ZeroDivisionError as e: raise ValueError(e)
Możesz użyć sys.exc_info (), aby uzyskać śledzenie i zgłosić nowy wyjątek za pomocą wspomnianego śledzenia (jak wspomina PEP). Jeśli chcesz zachować stary typ i wiadomość, możesz to zrobić na wyjątku, ale jest to przydatne tylko wtedy, gdy szuka go cokolwiek, co wyłapuje twój wyjątek.
Na przykład
Oczywiście to naprawdę nie jest przydatne. Gdyby tak było, nie potrzebowalibyśmy tego PEP. Nie polecałbym tego robić.
źródło
sys.exc_info()
@Devin. Mówi się, że „przypisanie wartości zwracanej przez dane śledzenia do zmiennej lokalnej w funkcji obsługującej wyjątek spowoduje odwołanie cykliczne”. Jednak następująca uwaga mówi, że od Pythona 2.2 cykl można wyczyścić, ale bardziej efektywne jest po prostu unikanie go.Możesz utworzyć własny typ wyjątku, który rozszerza każdy wychwycony wyjątek .
Ale przez większość czasu myślę, że łatwiej byłoby złapać wyjątek, obsłużyć go i albo
raise
oryginalny wyjątek (i zachować śledzenie), alboraise NewException()
. Gdybym wywoływał twój kod i otrzymałem jeden z twoich niestandardowych wyjątków, spodziewałbym się, że twój kod już obsłużył każdy wyjątek, który musiałeś złapać. Dlatego nie muszę sam uzyskiwać do niego dostępu.Edycja: znalazłem tę analizę sposobów zgłaszania własnego wyjątku i zachowania oryginalnego wyjątku. Żadnych ładnych rozwiązań.
źródło
Zauważyłem też, że wiele razy potrzebuję „zawijania” podniesionych błędów.
Obejmuje to zarówno zakres funkcji, jak i czasami zawija tylko niektóre linie wewnątrz funkcji.
Utworzono opakowanie do użycia a
decorator
icontext manager
:Realizacja
Przykłady użycia
dekorator
podczas wywoływania
do
metody nie martw sięIndexError
, po prostuMyError
menedżer kontekstu
wewnątrz
do2
,context manager
jeśliIndexError
zostanie podniesiony, zostanie zawinięty i podniesionyMyError
źródło
Najbardziej prostym rozwiązaniem dla Twoich potrzeb powinno być:
W ten sposób możesz później wydrukować swoją wiadomość i określony błąd wyrzucony przez funkcję wysyłania
źródło