Gibt es eine Möglichkeit zu wissen (zum Zeitpunkt der Codierung), welche Ausnahmen bei der Ausführung von Python-Code zu erwarten sind? In 90% der Fälle erhalte ich die Basis-Ausnahmeklasse, da ich nicht weiß, welcher Ausnahmetyp möglicherweise ausgelöst wird (und ich nicht aufgefordert werde, die Dokumentation zu lesen. Oft kann eine Ausnahme aus der Tiefe weitergegeben werden Mal ist die Dokumentation nicht aktualisiert oder korrekt). Gibt es ein Tool, um dies zu überprüfen? (wie durch Lesen des Python-Codes und der Bibliotheken)?
86
raise
Zeichenfolgen und nicht nurBaseException
Unterklassen verwenden können. Wenn Sie also Bibliothekscode aufrufen, der außerhalb Ihrer Kontrolleexcept Exception
liegt, reicht dies nicht aus, da keine Zeichenfolgenausnahmen abgefangen werden. Wie andere bereits betont haben, bellen Sie hier den falschen Baum an.except Exception
funktioniert gut zum Abfangen von String-Ausnahmen in Python 2.6 und höher.Antworten:
Ich denke, eine Lösung könnte nur ungenau sein, weil statische Typisierungsregeln fehlen.
Mir ist kein Tool bekannt, das Ausnahmen überprüft, aber Sie könnten ein eigenes Tool entwickeln, das Ihren Anforderungen entspricht (eine gute Chance, ein wenig mit statischer Analyse zu spielen).
Als ersten Versuch könnten Sie eine Funktion schreiben, die einen AST erstellt, alle
Raise
Knoten findet und dann versucht, allgemeine Muster für das Auslösen von Ausnahmen herauszufinden (z. B. direktes Aufrufen eines Konstruktors).Sei
x
folgendes Programm:x = '''\ if f(x): raise IOError(errno.ENOENT, 'not found') else: e = g(x) raise e '''
Erstellen Sie den AST mit dem
compiler
Paket:Definieren Sie dann eine
Raise
Besucherklasse:class RaiseVisitor(object): def __init__(self): self.nodes = [] def visitRaise(self, n): self.nodes.append(n)
Und gehen Sie die AST-Sammelknoten
Raise
entlang: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), ]
Sie können fortfahren, indem Sie Symbole mithilfe von Compilersymboltabellen auflösen, Datenabhängigkeiten analysieren usw. Oder Sie können einfach ableiten, dass
CallFunc(Name('IOError'), ...)
"definitiv eine Erhöhung bedeuten sollteIOError
", was für schnelle praktische Ergebnisse völlig in Ordnung ist :)quelle
v.nodes
obigen Wertes können Sie nicht wirklich sagen, was das Ding istName('IOError')
oderName('e')
. Sie wissen nicht , was Wert (e) diejenigen ,IOError
unde
darauf hinweisen können, wie sie sogenannte freie Variablen sind. Selbst wenn ihr Bindungskontext bekannt wäre (hier kommen Symboltabellen ins Spiel), sollten Sie eine Art Datenabhängigkeitsanalyse durchführen, um ihre genauen Werte abzuleiten (dies sollte in Python schwierig sein).['IOError(errno.ENOENT, "not found")', 'e']
, die dem Benutzer angezeigt wird, in Ordnung. Sie können jedoch keine tatsächlichen Werteklassen von Variablen ableiten, die durch Zeichenfolgen dargestellt werden :) (Entschuldigung für das erneute Posten)exc_class = raw_input(); exec "raise " + exc_class
. Der Punkt ist, dass diese Art der statischen Analyse in einer dynamischen Sprache wie Python nicht wirklich möglich ist.find /path/to/library -name '*.py' | grep 'raise '
ähnliche ErgebnisseSie sollten nur Ausnahmen abfangen, die Sie behandeln werden.
Es ist Unsinn, alle Ausnahmen anhand ihrer konkreten Typen zu erfassen. Sie sollten bestimmte Ausnahmen abfangen, die Sie behandeln können und werden . Für andere Ausnahmen können Sie einen generischen Fang schreiben, der "
str()
Basisausnahme" abfängt , protokolliert ( Funktion verwenden) und Ihr Programm beendet (oder etwas anderes tut, das in einer Absturzsituation angemessen ist).Wenn Sie wirklich alle Ausnahmen behandeln und sicher sind, dass keine davon schwerwiegend ist (z. B. wenn Sie den Code in einer Sandbox-Umgebung ausführen), entspricht Ihr Ansatz, generische BaseException abzufangen, Ihren Zielen.
Möglicherweise interessieren Sie sich auch für eine Referenz für Sprachausnahmen , nicht für eine Referenz für die von Ihnen verwendete Bibliothek.
Wenn die Bibliotheksreferenz wirklich schlecht ist und beim Abfangen von Systemausnahmen keine eigenen Ausnahmen erneut ausgelöst wird, besteht der einzig nützliche Ansatz darin, Tests auszuführen (möglicherweise zur Testsuite hinzufügen, da sich dies ändern kann, wenn etwas nicht dokumentiert ist!). . Löschen Sie eine für Ihren Code wichtige Datei und überprüfen Sie, welche Ausnahme ausgelöst wird. Geben Sie zu viele Daten an und überprüfen Sie, welchen Fehler sie verursacht.
Sie müssen ohnehin Tests ausführen, da Sie selbst dann keine Ahnung haben, wie Sie mit diesen umgehen sollen , wenn die Methode zum Abrufen der Ausnahmen per Quellcode vorhanden wäre . Möglicherweise sollten Sie die Fehlermeldung "Datei needful.txt wurde nicht gefunden!" Anzeigen. wann fängst
IndexError
du Nur Test kann sagen.quelle
Das richtige Werkzeug, um dieses Problem zu lösen, sind Unittests. Wenn Sie Ausnahmen durch echten Code haben, die von den Unittests nicht ausgelöst werden, benötigen Sie weitere Unittests.
Bedenken Sie
def f(duck): try: duck.quack() except ??? could be anything
Ente kann jedes Objekt sein
Natürlich können Sie eine
AttributeError
If-Ente haben, die keinen Quacksalber hat, eineTypeError
If-Ente, die einen Quacksalber hat, aber nicht aufrufbar ist. Sie haben jedoch keine Ahnung, was sichduck.quack()
möglicherweise ergibt, vielleicht sogar einDuckError
oder soAngenommen, Sie haben einen solchen Code
Wenn es eine
IndexError
auslöst, wissen Sie nicht, ob es von arr [i] oder tief in der Datenbankfunktion stammt. Normalerweise spielt es keine Rolle, wo die Ausnahme aufgetreten ist, sondern dass etwas schief gelaufen ist und das, was Sie wollten, nicht passiert ist.Eine praktische Technik besteht darin, die Ausnahme wie diese zu fangen und möglicherweise erneut zu erhöhen
except Exception as e #inspect e, decide what to do raise
quelle
Bisher hat niemand erklärt, warum es keine vollständige, 100% korrekte Liste von Ausnahmen gibt. Ich dachte, es lohnt sich, einen Kommentar abzugeben. Einer der Gründe ist eine erstklassige Funktion. Angenommen, Sie haben eine Funktion wie diese:
def apl(f,arg): return f(arg)
Jetzt
apl
kann jede Ausnahmef
ausgelöst werden , die ausgelöst wird. Zwar gibt es in der Kernbibliothek nicht viele solcher Funktionen, doch alles, was das Listenverständnis mit benutzerdefinierten Filtern, Zuordnen, Reduzieren usw. verwendet, ist betroffen.Die Dokumentation und die Quellenanalysatoren sind hier die einzigen "ernsthaften" Informationsquellen. Denken Sie daran, was sie nicht können.
quelle
Bei der Verwendung von Socket bin ich darauf gestoßen. Ich wollte alle Fehlerbedingungen herausfinden, unter denen ich laufen würde (anstatt zu versuchen, Fehler zu erstellen und herauszufinden, welchen Socket ich habe, wollte ich nur eine kurze Liste). Letztendlich habe ich "/usr/lib64/python2.4/test/test_socket.py" für "Raise" gesucht:
$ 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,
Welches ist eine ziemlich kurze Liste von Fehlern. Dies funktioniert natürlich nur von Fall zu Fall und hängt davon ab, ob die Tests genau sind (was normalerweise der Fall ist). Andernfalls müssen Sie so ziemlich alle Ausnahmen abfangen, protokollieren und sezieren und herausfinden, wie Sie damit umgehen sollen (was mit Unit-Tests nicht zu schwierig wäre).
quelle
Es gibt zwei Möglichkeiten, die ich informativ fand. Führen Sie als erstes den Code in iPython aus, der den Ausnahmetyp anzeigt.
n = 2 str = 'me ' str + 2 TypeError: unsupported operand type(s) for +: 'int' and 'str'
Auf die zweite Weise geben wir uns damit zufrieden, zu viel zu fangen und verbessern es im Laufe der Zeit. Fügen Sie einen
try
Ausdruck in Ihren Code ein und fangen Sieexcept Exception as err
. Drucken Sie genügend Daten, um zu wissen, welche Ausnahme ausgelöst wurde. Wenn Ausnahmen ausgelöst werden, verbessern Sie Ihren Code, indem Sie eine genauereexcept
Klausel hinzufügen . Wenn Sie das Gefühl haben, alle relevanten Ausnahmen erfasst zu haben, entfernen Sie die All-Inclusive-Ausnahme. Eine gute Sache, weil es Programmierfehler verschluckt.try: so something except Exception as err: print "Some message" print err.__class__ print err exit(1)
quelle
Normalerweise müssen Sie Ausnahmen nur um einige Codezeilen herum abfangen. Sie möchten nicht Ihre gesamte
main
Funktion in dietry except
Klausel aufnehmen. Für jede einzelne Zeile sollten Sie jetzt immer (oder in der Lage sein, leicht zu überprüfen), welche Art von Ausnahme ausgelöst werden könnte.Dokumente enthalten eine vollständige Liste der integrierten Ausnahmen . Versuchen Sie nicht, die Ausnahme auszunehmen, die Sie nicht erwarten. Sie werden möglicherweise im aufrufenden Code behandelt / erwartet.
edit : was möglicherweise geworfen wird, hängt natürlich davon ab, was du tust! Zugriff auf ein zufälliges Element einer Sequenz :
IndexError
, zufälliges Element eines Diktats:KeyError
usw.Versuchen Sie einfach, diese wenigen Zeilen in IDLE auszuführen und eine Ausnahme zu verursachen. Aber unittest wäre natürlich eine bessere Lösung.
quelle