Moje doświadczenie jest w C # i niedawno zacząłem programować w Pythonie. Gdy wyjątek jest zgłaszany, zwykle chcę zawinąć go w inny wyjątek, który dodaje więcej informacji, jednocześnie pokazując pełny ślad stosu. W C # jest to dość łatwe, ale jak to zrobić w Pythonie?
Na przykład. w C # zrobiłbym coś takiego:
try
{
ProcessFile(filePath);
}
catch (Exception ex)
{
throw new ApplicationException("Failed to process file " + filePath, ex);
}
W Pythonie mogę zrobić coś podobnego:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file ' + filePath, e)
... ale to powoduje utratę śledzenia wewnętrznego wyjątku!
Edycja: chciałbym zobaczyć zarówno komunikaty o wyjątkach, jak i oba ślady stosu i skorelować je. Oznacza to, że chcę zobaczyć w danych wyjściowych, że wyjątek X wystąpił tutaj, a następnie wyjątek Y tam - tak samo, jak w C #. Czy jest to możliwe w Pythonie 2.6? Wygląda na to, że najlepsze, co do tej pory mogę zrobić (na podstawie odpowiedzi Glenna Maynarda), to:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]
Obejmuje to zarówno komunikaty, jak i oba elementy śledzenia, ale nie pokazuje, który wyjątek wystąpił w miejscu śledzenia.
Odpowiedzi:
Python 2
To proste; przekazuje dane śledzenia jako trzeci argument do podniesienia.
Rób to zawsze podczas wychwytywania jednego wyjątku i ponownego zgłaszania innego.
źródło
raise MyException(str(e)), ...
itp.raise E() from tb
i.with_traceback(...)
raise
jest wartością przekazywaną do wyjątku (w przypadku, gdy pierwszy argument jest klasą wyjątku, a nie instancją). Więc jeśli chcesz wyjątkami typu swap, zamiast robićraise MyException(str(e)), None, sys.exc_info()[2]
, to lepiej korzystać z tego:raise MyException, e.args, sys.exc_info()[2]
.from future.utils import raise_
iraise_(ValueError, None, sys.exc_info()[2])
.Python 3
W Pythonie 3 możesz wykonać następujące czynności:
To da coś takiego:
źródło
raise ... from ...
jest rzeczywiście właściwym sposobem na zrobienie tego w Pythonie 3. To wymaga więcej głosów pozytywnych.Nakedible
Myślę, że to dlatego, że niestety większość ludzi nadal nie używa Pythona 3.future
pakietu, aby to osiągnąć: python-future.org/compatible_idioms.html#raising-exceptions Np .from future.utils import raise_
iraise_(ValueError, None, sys.exc_info()[2])
.Python 3 ma klauzulę
raise
...from
do łączenia wyjątków. Odpowiedź Glenna jest świetna dla Pythona 2.7, ale używa tylko śledzenia oryginalnego wyjątku i odrzuca komunikat o błędzie i inne szczegóły. Oto kilka przykładów w Pythonie 2.7, które dodają informacje o kontekście z bieżącego zakresu do oryginalnego komunikatu o błędzie, ale pozostałe szczegóły pozostają nienaruszone.Znany typ wyjątku
Ten
raise
rodzaj instrukcji przyjmuje typ wyjątku jako pierwsze wyrażenie, argumenty konstruktora klasy wyjątku w krotce jako drugie wyrażenie, a śledzenie zwrotne jako trzecie wyrażenie. Jeśli używasz wersji wcześniejszej niż Python 2.2, zobacz ostrzeżenia nasys.exc_info()
.Dowolny typ wyjątku
Oto kolejny przykład, który jest bardziej ogólny, jeśli nie wiesz, jakie wyjątki może przechwytywać Twój kod. Wadą jest to, że traci typ wyjątku i po prostu zgłasza błąd RuntimeError. Musisz zaimportować
traceback
moduł.Zmodyfikuj wiadomość
Oto inna opcja, jeśli typ wyjątku pozwoli ci dodać do niego kontekst. Możesz zmodyfikować komunikat wyjątku, a następnie ponownie go podnieść.
To generuje następujący ślad stosu:
Możesz zobaczyć, że pokazuje on wiersz, w którym
check_output()
został wywołany, ale komunikat o wyjątku zawiera teraz wiersz poleceń.źródło
ex.strerror
pochodzi? Nie mogę znaleźć żadnego odpowiedniego trafienia w dokumentacji Pythona. Nie powinno byćstr(ex)
?IOError
pochodzi zEnvironmentError
@hheimbuerger, który udostępnia atrybutyerrorno
istrerror
.Error
, np. ValueError, wRuntimeError
przechwytującException
? Jeśli powtórzę twoją odpowiedź w tej sprawie, ślad stosu zostanie utracony.W Pythonie 3.x :
lub po prostu
który będzie się propagował,
MyException
ale wypisze oba wyjątki, jeśli nie będzie obsługiwany.W Pythonie 2.x :
Możesz zapobiec drukowaniu obu wyjątków, zabijając
__context__
atrybut. Tutaj piszę menedżera kontekstu, który używa go do przechwytywania i zmiany wyjątku w locie: (zobacz http://docs.python.org/3.1/library/stdtypes.html, aby dowiedzieć się, jak działają)źródło
e.__traceback__
atrybutu!Nie sądzę, że możesz to zrobić w Pythonie 2.x, ale coś podobnego do tej funkcji jest częścią Pythona 3. Z PEP 3134 :
Porównanie do C #:
Zauważ również, że Java, Ruby i Perl 5 również nie obsługują tego typu rzeczy. Cytując ponownie:
źródło
Aby uzyskać maksymalną zgodność między Pythonem 2 i 3, możesz użyć
raise_from
wsix
bibliotece. https://six.readthedocs.io/#six.raise_from . Oto Twój przykład (nieco zmodyfikowany dla przejrzystości):źródło
Możesz użyć mojej klasy CausedException do tworzenia łańcuchów wyjątków w Pythonie 2.x (a nawet w Pythonie 3 może to być przydatne w przypadku, gdy chcesz podać więcej niż jeden przechwycony wyjątek jako przyczynę nowo podniesionego wyjątku). Może to ci pomoże.
źródło
Może mógłbyś pobrać odpowiednie informacje i przekazać je dalej? Myślę o czymś takim:
źródło
Zarozumiały:
raise ... from
rozwiązanie)możesz użyć prostego rozwiązania z dokumentacji https://docs.python.org/3/tutorial/errors.html#raising-exceptions :
Wyjście:
Wygląda na to, że kluczowym elementem jest uproszczone słowo kluczowe „podwyżka”, które występuje samodzielnie. Spowoduje to ponowne podniesienie wyjątku w bloku except.
źródło