Czy jest sposób, aby wiedzieć (w czasie kodowania), jakich wyjątków można się spodziewać podczas wykonywania kodu w Pythonie? W końcu łapię podstawową klasę wyjątków w 90% przypadków, ponieważ nie wiem, który typ wyjątku może zostać wyrzucony (i nie mów mi, żebym czytał dokumentację. Wiele razy wyjątek można propagować z głębi. dokumentacja nie jest aktualizowana lub poprawna). Czy jest jakieś narzędzie do sprawdzenia tego? (np. czytając kod Pythona i biblioteki)?
89
raise
używać łańcuchów, a nie tylkoBaseException
podklas. Więc jeśli wywołujesz kod biblioteki, który jest poza twoją kontrolą, nawetexcept Exception
nie jest wystarczający, ponieważ nie przechwytuje wyjątków ciągów. Jak zauważyli inni, szczekasz tutaj na niewłaściwe drzewo.except Exception
działa dobrze do przechwytywania wyjątków ciągów w Pythonie 2.6 i nowszych.Odpowiedzi:
Myślę, że rozwiązanie może być nieprecyzyjne tylko z powodu braku statycznych reguł pisania.
Nie znam jakiegoś narzędzia, które sprawdza wyjątki, ale możesz wymyślić własne narzędzie pasujące do twoich potrzeb (dobra okazja, aby pobawić się trochę analizą statyczną).
Jako pierwszą próbę możesz napisać funkcję, która buduje AST, wyszukuje wszystkie
Raise
węzły, a następnie próbuje znaleźć typowe wzorce zgłaszania wyjątków (np. Bezpośrednie wywołanie konstruktora)Niech
x
będzie następujący program:x = '''\ if f(x): raise IOError(errno.ENOENT, 'not found') else: e = g(x) raise e '''
Zbuduj AST za pomocą
compiler
pakietu:Następnie zdefiniuj
Raise
klasę gości:class RaiseVisitor(object): def __init__(self): self.nodes = [] def visitRaise(self, n): self.nodes.append(n)
I przejdź przez
Raise
węzły zbierające AST :v = RaiseVisitor() compiler.walk(tree, v) >>> print v.nodes [ Raise( CallFunc( Name('IOError'), [Getattr(Name('errno'), 'ENOENT'), Const('not found')], None, None), None, None), Raise(Name('e'), None, None), ]
Możesz kontynuować, rozwiązując symbole za pomocą tabel symboli kompilatora, analizując zależności danych itp. Możesz też po prostu wywnioskować, że
CallFunc(Name('IOError'), ...)
„zdecydowanie powinno oznaczać podniesienieIOError
”, co jest całkiem OK dla szybkich praktycznych rezultatów :)źródło
v.nodes
powyższą wartość, nie możesz właściwie powiedzieć, co to jestName('IOError')
lubName('e')
. Nie wiesz, jakie wartości teIOError
ie
mogą wskazywać, ponieważ są to tak zwane zmienne swobodne. Nawet jeśli ich kontekst wiązania byłby znany (tutaj wchodzą w grę tabele symboli), powinieneś przeprowadzić jakąś analizę zależności danych, aby wywnioskować ich dokładne wartości (powinno to być trudne w Pythonie).['IOError(errno.ENOENT, "not found")', 'e']
wyświetlanych użytkownikowi jest w porządku. Ale nie możesz wywnioskować rzeczywistych klas wartości zmiennych reprezentowanych przez ciągi znaków :) (przepraszam za ponowneexc_class = raw_input(); exec "raise " + exc_class
. Chodzi o to, że tego rodzaju statyczna analiza nie jest naprawdę możliwa w dynamicznym języku, takim jak Python.find /path/to/library -name '*.py' | grep 'raise '
uzyskać podobne wyniki :)Powinieneś łapać tylko wyjątki, które będziesz obsługiwać.
Łapanie wszystkich wyjątków według ich konkretnych typów jest nonsensem. Powinieneś wychwycić określone wyjątki, które możesz obsłużyć i które będziesz obsługiwać. W przypadku innych wyjątków możesz napisać ogólny catch, który przechwytuje „podstawowy wyjątek”, rejestruje go (użyj
str()
funkcji) i kończy działanie programu (lub robi coś innego, co jest odpowiednie w przypadku awarii).Jeśli naprawdę zamierzasz obsłużyć wszystkie wyjątki i jesteś pewien, że żaden z nich nie jest fatalny (na przykład, jeśli uruchamiasz kod w jakimś środowisku piaskownicy), to twoje podejście do przechwytywania ogólnego BaseException pasuje do twoich celów.
Możesz być także zainteresowany odniesieniem do wyjątków językowych , a nie odniesieniem do biblioteki, której używasz.
Jeśli odniesienie do biblioteki jest naprawdę słabe i nie rzuca ponownie własnych wyjątków podczas przechwytywania wyjątków systemowych, jedynym użytecznym podejściem jest uruchomienie testów (może dodać je do zestawu testów, ponieważ jeśli coś jest nieudokumentowane, może się to zmienić!) . Usuń plik kluczowy dla twojego kodu i sprawdź, który wyjątek jest generowany. Podaj zbyt dużo danych i sprawdź, jaki błąd powoduje.
I tak będziesz musiał uruchomić testy, ponieważ nawet gdyby istniała metoda uzyskiwania wyjątków przez kod źródłowy, nie dałoby ci to żadnego pojęcia, jak powinieneś sobie z tym poradzić . Może powinien pojawić się komunikat o błędzie „Nie znaleziono pliku potrzebnego.txt!” kiedy złapiesz
IndexError
? Tylko test może powiedzieć.źródło
Odpowiednim narzędziem do rozwiązania tego problemu są unittesty. Jeśli masz wyjątki wywołane przez rzeczywisty kod, których unittesty nie powodują, potrzebujesz więcej unittestów.
Rozważ to
def f(duck): try: duck.quack() except ??? could be anything
kaczką może być dowolny przedmiot
Oczywiście możesz mieć,
AttributeError
jeśli kaczka nie ma szarlatanów, aTypeError
jeśli kaczka ma szarlatanę, ale nie można jej wywołać. Nie masz jednak pojęcia, coduck.quack()
może wywołać, może nawetDuckError
czy cośPrzypuśćmy, że masz taki kod
Jeśli podniesie
IndexError
, nie wiesz, czy pochodzi z arr [i], czy też z wnętrza funkcji bazy danych. zwykle nie ma tak wielkiego znaczenia, gdzie wystąpił wyjątek, raczej to, że coś poszło nie tak, a to, co chciałeś, się nie wydarzyło.Przydatną techniką jest złapanie i może przebicie wyjątku w ten sposób
except Exception as e #inspect e, decide what to do raise
źródło
Nikt do tej pory nie wyjaśnił, dlaczego nie możesz mieć pełnej, w 100% poprawnej listy wyjątków, więc pomyślałem, że warto to skomentować. Jednym z powodów jest pierwszorzędna funkcja. Powiedzmy, że masz taką funkcję:
def apl(f,arg): return f(arg)
Teraz
apl
można zgłosić każdy wyjątek, który sięf
pojawi. Chociaż w podstawowej bibliotece nie ma wielu takich funkcji, ma to wpływ na wszystko, co używa rozumienia list z niestandardowymi filtrami, mapowaniem, redukcją itp.Dokumentacja i analizatory źródeł są tutaj jedynymi „poważnymi” źródłami informacji. Pamiętaj tylko, czego nie mogą zrobić.
źródło
Natknąłem się na to podczas korzystania z gniazda, chciałem poznać wszystkie warunki błędów, na które napotkałem (więc zamiast próbować tworzyć błędy i dowiedzieć się, jakie gniazdo ma, chciałem tylko zwięzłą listę). Ostatecznie skończyłem grep'owanie „/usr/lib64/python2.4/test/test_socket.py” dla „podbicia”:
$ grep raise test_socket.py Any exceptions raised by the clients during their tests raise TypeError, "test_func must be a callable function" raise NotImplementedError, "clientSetUp must be implemented." def raise_error(*args, **kwargs): raise socket.error def raise_herror(*args, **kwargs): raise socket.herror def raise_gaierror(*args, **kwargs): raise socket.gaierror self.failUnlessRaises(socket.error, raise_error, self.failUnlessRaises(socket.error, raise_herror, self.failUnlessRaises(socket.error, raise_gaierror, raise socket.error # Check that setting it to an invalid value raises ValueError # Check that setting it to an invalid type raises TypeError def raise_timeout(*args, **kwargs): self.failUnlessRaises(socket.timeout, raise_timeout, def raise_timeout(*args, **kwargs): self.failUnlessRaises(socket.timeout, raise_timeout,
To dość zwięzła lista błędów. Teraz oczywiście działa to tylko w indywidualnych przypadkach i zależy od dokładności testów (a zazwyczaj są). W przeciwnym razie musisz prawie wyłapać wszystkie wyjątki, zarejestrować je, przeanalizować i dowiedzieć się, jak sobie z nimi poradzić (co w przypadku testów jednostkowych nie byłoby trudne).
źródło
Są dwa sposoby, które uznałem za przydatne. W pierwszym uruchom kod w iPythonie, który wyświetli typ wyjątku.
n = 2 str = 'me ' str + 2 TypeError: unsupported operand type(s) for +: 'int' and 'str'
W drugim przypadku zadowalamy się zbyt dużym wyłapywaniem i z czasem je poprawiamy. Uwzględnij
try
wyrażenie w swoim kodzie i złapexcept Exception as err
. Wydrukuj wystarczające dane, aby wiedzieć, który wyjątek został zgłoszony. Ponieważ wyjątki są generowane, ulepsz swój kod, dodając bardziej precyzyjnąexcept
klauzulę. Kiedy poczujesz, że złapałeś wszystkie istotne wyjątki, usuń ten all inclusive. Mimo wszystko to dobra rzecz, ponieważ połyka błędy programowania.try: so something except Exception as err: print "Some message" print err.__class__ print err exit(1)
źródło
normalnie musiałbyś wyłapać wyjątek tylko w kilku wierszach kodu. Nie chciałbyś umieścić całej swojej
main
funkcji wtry except
klauzuli. dla każdych kilku wierszy powinieneś teraz (lub mieć możliwość łatwego sprawdzenia), jakiego rodzaju wyjątek może zostać zgłoszony.dokumenty mają wyczerpującą listę wbudowanych wyjątków . nie próbuj wykluczać tych wyjątków, których się nie spodziewasz, mogą być obsługiwane / oczekiwane w kodzie wywołującym.
edycja : to, co może zostać wyrzucone, zależy oczywiście od tego, co robisz! dostęp do losowego elementu ciągu
IndexError
:, losowego elementu dyktatu:,KeyError
itp.Po prostu spróbuj uruchomić te kilka wierszy w IDLE i wywołaj wyjątek. Ale unittest byłoby oczywiście lepszym rozwiązaniem.
źródło