2010-05-16 11 views
11

Sorry für den langen Titel, aber es scheint am beschreibendsten für meine Frage.Wie kann ich eine Liste aller Ausnahmen finden, die eine gegebene Bibliotheksfunktion in Python auslöst?

Grundsätzlich habe ich eine schwierige Zeit finden Ausnahmeinformationen in der offiziellen Python-Dokumentation. Zum Beispiel in einem Programm bin ich zur Zeit schreibe, bin ich mit dem Move-Funktion des shutil libary:

from shutil import move 
move('somefile.txt', '/tmp/somefile.txt') 

Das funktioniert gut, solange ich den Schreibzugriff auf/tmp haben /, gibt es genug Speicherplatz, und wenn alle anderen Anforderungen erfüllt sind.

Wenn jedoch generische Code zu schreiben, ist es oft schwierig, diese Faktoren zu gewährleisten, so dass man in der Regel verwendet Ausnahmen:

from shutil import move 
try: 
    move('somefile.txt', '/tmp/somefile.txt') 
except: 
    print 'Move failed for some reason.' 

Ich möchte eigentlich die entsprechenden Ausnahmen, anstatt nur zu kontrollieren alles geworfen fangen, aber ich kann einfach keine Liste von Ausnahmen für die meisten Python-Module gefunden finden. Gibt es eine Möglichkeit für mich zu sehen, welche Ausnahmen eine bestimmte Funktion auslösen kann und warum? Auf diese Weise kann ich für jede Ausnahme geeignete Fälle machen, zum Beispiel:

from shutil import move 
try: 
    move('somefile.txt', '/tmp/somefile.txt') 
except PermissionDenied: 
    print 'No permission.' 
except DestinationDoesNotExist: 
    print "/tmp/ doesn't exist" 
except NoDiskSpace: 
    print 'No diskspace available.' 

Antwort Punkte gehen an jeden mich entweder zu einem gewissen relevanten Unterlagen verknüpfen kann, dass ich irgendwie in den offiziellen Dokumenten übersehen habe, oder bieten eine todsichere Möglichkeit, genau herauszufinden, welche Ausnahmen von welchen Funktionen ausgelöst werden und warum.

Danke!

UPDATE: Es scheint aus den gegebenen Antworten, dass es wirklich keine 100% geradlinige Möglichkeit gibt herauszufinden, welche Fehler von bestimmten Funktionen ausgelöst werden. Mit Meta-Programmierung scheint es, dass ich einfache Fälle herausfinden und einige Ausnahmen auflisten kann, aber das ist keine besonders nützliche oder bequeme Methode.

Ich würde gerne denken, dass es irgendwann einen Standard geben wird, um zu definieren, welche Ausnahmen von jeder Python-Funktion ausgelöst werden, und dass diese Information in der offiziellen Dokumentation enthalten sein wird. Bis dahin denke ich, dass ich nur zulassen werde, dass diese Ausnahmen passieren und dass es für meine Nutzer zu Fehlern kommt, da es das vernünftigste zu sein scheint.

+0

Als Python tyro, ich don Ich habe nicht die Erfahrung, mit Sicherheit zu sagen, aber ein Äquivalent zu Javas "Würfen" fühlt sich unpythonisch in der Art und Weise an, wie Funktionsparameter für ihre Typen getestet werden (wenn Ihre Funktion mit jeder iterierbaren Ente umgehen kann). – msw

Antwort

11

Um Messa zu verstärken, fangen Sie, was Sie erwarten, sind Fehlermodi, von denen Sie wissen, wie Sie sich erholen können. Ian Bicking schrieb an article, die einige der übergreifenden Prinzipien anspricht, wie Eli Benderskys note.

Das Problem mit dem Beispielcode ist, dass es nicht Umgang mit Fehlern ist, nur verschönern sie und verwerfen sie. Dein Code "weiß" nicht, was er mit einem NameError tun soll, und es gibt nicht viel anderes, als ihn zu verwerfen. Schau dir Bickings Re-Raise an, wenn du das Gefühl hast, dass du Details hinzufügen musst.

IOError und OSError sind vernünftig "zu erwarten" für eine shutil.move, aber nicht unbedingt handhabbar. Und der Aufrufer Ihrer Funktion wollte, dass es eine Datei verschiebt und möglicherweise selbst bricht, wenn der "Vertrag", über den Eli schreibt, gebrochen ist.

Fangen Sie, was Sie reparieren, schmücken und wieder erhöhen können, was Sie erwarten, aber nicht beheben können, und lassen Sie den Anrufer damit umgehen, was Sie nicht erwartet haben, auch wenn der Code, der "behandelt" sieben Stufen höher ist Stapel in main.

+1

Um das OP-Problem ein wenig mehr zu adressieren, werden 'IOError' und' OSError' hier beschrieben: http://docs.python.org/library/exceptions.html. Aber wie Sie sehen können, sind beide sehr abhängig von dem Host-System und os. Sie sollten wahrscheinlich etwas wie 'außer EnvironmentError als e: print e.strerror' tun, wenn Sie * wirklich * alle Arten von verwandten Fehlern verschlucken wollen. Sonst musst du das "errorno" überprüfen, bestimmte Fälle behandeln und nicht vergessen, den Rest zu "heben". –

+0

@ THC4k: es ist 'errno', nicht' errorno'. – tzot

+0

Ich stimme zu! Es ist wichtiger zu wissen, was mit dem Code gehandhabt werden kann und was nicht sein kann. – yaobin

2

Da diese Operationen normalerweise libc-Funktionen und Betriebssystemaufrufe verwenden, erhalten Sie meistens IOError oder OSError mit einer errno Nummer; Diese Fehler sind in man-Seiten dieser libc/OS-Aufrufe aufgeführt.

Ich weiß, dies ist möglicherweise keine vollständige Antwort, wäre es gut, alle Ausnahmen in der Dokumentation aufgeführt zu haben ...

+1

Sie haben geschrieben: "Es wäre gut, alle Ausnahmen aufgelistet zu haben" und ich fange an, diese Idee in Frage zu stellen. Wenn die Sprache keine "throws" -Klauseln erzwingt, schlägt die Dokumentationslösung die Maxime "Explizit ist besser als implizit" fehl. Zwischen fehlendem Express-Vertrag und dynamischer Bindung (einschließlich "Import", die gerade passiert ist), wissen Sie wirklich nicht, was ausgelöst werden könnte. Sie können nicht damit umgehen, was Sie nicht erwarten, und da Sie feststellen, dass die EnvironmentErrors erwartbar und handhabbar sind, möchten Sie keinen Assertion-Fehler abfangen, und Sie möchten nicht, dass Ihr Code mit MemoryError-Fixes übersät ist. – msw

4

Python keinen Mechanismus jetzt hat für die Erklärung, welche Ausnahmen geworfen werden, im Gegensatz zu (zum Beispiel) Java. (In Java müssen Sie genau definieren, welche Ausnahmen von welchem ​​ausgelöst werden, und wenn eine Ihrer Hilfsmethoden eine weitere Ausnahme auslösen muss, müssen Sie sie allen Methoden hinzufügen, die sie aufrufen, was schnell langweilig wird!)

Wenn Sie also genau herausfinden wollen, welche Ausnahmen von einem bestimmten Python-Bit ausgelöst werden, müssen Sie die Dokumentation und die Quelle überprüfen.

Allerdings hat Python eine sehr gute Ausnahmehierarchie.

Wenn Sie die unten stehende Ausnahmehierarchie untersuchen, sehen Sie, dass die Fehleroberklasse, die Sie abfangen wollen, StandardError heißt - dies sollte alle Fehler erfassen, die im normalen Betrieb generiert werden. Drehen Sie den Fehler in in einen String eine vernünftige Idee für den Benutzer geben, was schief gelaufen ist, also würde ich den Code oben sollte wie

from shutil import move 
try: 
    move('somefile.txt', '/tmp/somefile.txt') 
except StandardError, e: 
    print 'Move failed: %s' % e 

Ausnahmehierarchie

BaseException 
|---Exception 
|---|---StandardError 
|---|---|---ArithmeticError 
|---|---|---|---FloatingPointError 
|---|---|---|---OverflowError 
|---|---|---|---ZeroDivisionError 
|---|---|---AssertionError 
|---|---|---AttributeError 
|---|---|---BufferError 
|---|---|---EOFError 
|---|---|---EnvironmentError 
|---|---|---|---IOError 
|---|---|---|---OSError 
|---|---|---ImportError 
|---|---|---LookupError 
|---|---|---|---IndexError 
|---|---|---|---KeyError 
|---|---|---MemoryError 
|---|---|---NameError 
|---|---|---|---UnboundLocalError 
|---|---|---ReferenceError 
|---|---|---RuntimeError 
|---|---|---|---NotImplementedError 
|---|---|---SyntaxError 
|---|---|---|---IndentationError 
|---|---|---|---|---TabError 
|---|---|---SystemError 
|---|---|---TypeError 
|---|---|---ValueError 
|---|---|---|---UnicodeError 
|---|---|---|---|---UnicodeDecodeError 
|---|---|---|---|---UnicodeEncodeError 
|---|---|---|---|---UnicodeTranslateError 
|---|---StopIteration 
|---|---Warning 
|---|---|---BytesWarning 
|---|---|---DeprecationWarning 
|---|---|---FutureWarning 
|---|---|---ImportWarning 
|---|---|---PendingDeprecationWarning 
|---|---|---RuntimeWarning 
|---|---|---SyntaxWarning 
|---|---|---UnicodeWarning 
|---|---|---UserWarning 
|---GeneratorExit 
|---KeyboardInterrupt 
|---SystemExit 

Dies sehen auch vorschlagen bedeutet, dass Sie beim Definieren Ihrer eigenen Ausnahmen diese von StandardError not Exception ableiten sollten.

+0

Dies ist eine schlechte Idee, da es SyntaxError, AssertionError, NameError usw. schlucken wird, die wirklich Bugs anstelle von "erwarteten" Laufzeitfehlern darstellen. – msw

+0

Immer noch besser als 'außer:'. – fossilet

3

Ja, Sie können (für einfache Fälle), aber Sie brauchen ein bisschen Meta-Programmierung. Wie die anderen Antworten gesagt haben, deklariert eine Funktion nicht, dass sie einen bestimmten Fehlertyp auslöst. Sie müssen sich also das Modul anschauen und sehen, welche Exception-Typen definiert sind oder welche Exception-Typen es auslöst. Sie können entweder versuchen, die Dokumentation zu durchsuchen oder die Python-API dafür zu nutzen.

Zum ersten die Ausnahmetypen in einem Modul definiert zu finden, nur ein einfaches Skript durch jedes Objekt im Modul Wörterbuch gehen schreiben module.__dict__ und sehen, ob es in dem Wort „Fehler“ endet oder wenn es sich um eine Unterklasse von Exception:

def listexns(mod): 
    """Saved as: http://gist.github.com/402861 
    """ 
    module = __import__(mod) 
    exns = [] 
    for name in module.__dict__: 
     if (issubclass(module.__dict__[name], Exception) or 
      name.endswith('Error')): 
      exns.append(name) 
    for name in exns: 
     print '%s.%s is an exception type' % (str(mod), name) 
    return 

Wenn ich dies auf Ihrem Beispiel shutils betreibe ich diese:

$ python listexn.py shutil 
Looking for exception types in module: shutil 
shutil.Error is an exception type 
shutil.WindowsError is an exception type 
$ 

, der Ihnen sagt, welche Fehlertypen definiert sind, aber nicht, welche sind geworfen. Um Letzteres zu erreichen, müssen wir über den abstrakten Syntaxbaum gehen, der erzeugt wird, wenn der Python-Interpreter das Modul analysiert, und nach jeder raise-Anweisung suchen und dann eine Liste von Namen speichern, die ausgelöst werden. Der Code dafür ist ein wenig lang, so werde ich zuerst den Ausgangszustand:

$ python listexn-raised.py /usr/lib/python2.6/shutil.py 
Looking for exception types in: /usr/lib/python2.6/shutil.py 
/usr/lib/python2.6/shutil.py:OSError is an exception type 
/usr/lib/python2.6/shutil.py:Error is an exception type 
$ 

Also, wir wissen jetzt, dass shutil.py die Fehlertypen Error und WindowsError definiert und hebt die Ausnahmetypen OSError und Error.Wenn wir ein bisschen vollständiger sein wollen, könnten wir eine andere Methode schreiben, um alle except-Klausel zu überprüfen, um auch zu sehen, welche Ausnahmen shutil behandelt.

Hier ist der Code ist über den AST zu gehen, verwendet er nur den compiler.visitor Schnittstelle einen Wanderer zu schaffen, die die „Besuchermuster“ aus dem Gang of Four Buch implementiert:

class ExceptionFinder(visitor.ASTVisitor): 
    """List all exceptions raised by a module. 
    Saved as: http://gist.github.com/402869 
    """ 

    def __init__(self, filename): 
     visitor.ASTVisitor.__init__(self) 
     self.filename = filename 
     self.exns = set() 
     return 

    def __visitName(self, node): 
     """Should not be called by generic visit, otherwise every name 
     will be reported as an exception type. 
     """ 
     self.exns.add(node.name) 
     return 

    def __visitCallFunc(self, node): 
     """Should not be called by generic visit, otherwise every name 
     will be reported as an exception type. 
     """ 
     self.__visitName(node.node) 
     return 

    def visitRaise(self, node): 
     """Visit a raise statement. 
     Cheat the default dispatcher. 
     """ 
     if issubclass(node.expr1, compiler.ast.Name): 
      self.__visitName(node.expr1) 
     elif isinstance(node.expr1, compiler.ast.CallFunc): 
      self.__visitCallFunc(node.expr1) 
     return 
+3

Nein, Sie können nicht alle Ausnahmen auflisten, weder in der Theorie noch in der Praxis: In der Praxis kann alles viele Ausnahmen vom C-Teil von Python auslösen, von AssertionError bis ZeroDivisionError. Theoretisch kann man nicht herausfinden, was das bringt: 'raise type (raw_input (" Raise What? "), (Exception,), {})' (es wird * eine neue Art von Ausnahme * für jede Eingabe ausgelöst) –

+1

OK, das stimmt, ich listet hier nur Python-Ausnahmen auf und die statische Analyse wird Ihr spezielles Beispiel nicht verstehen. Ihr Beispiel zeigt, dass die Frage nicht zu beantworten ist, aber in der Praxis reicht wahrscheinlich nur ein vernünftiger Stich in einer Antwort aus, um einen Code von Drittanbietern zu verwenden, was das OP gefordert hat. – snim2

+0

Ich schätze diese Antwort wirklich. Ich fand es immens nützlich und aufschlussreich. Ich habe dir jedoch nicht die Antwort gegeben, denn nachdem ich die Antworten durchgelesen habe, scheint es, dass es keinen wirklich sicheren Weg gibt, das zu finden, wonach ich suche. Ihre Lösung scheint in einfachen Fällen zu funktionieren, ist aber für den normalen Gebrauch nicht bequem oder elegant (es wäre ein Schmerz, das für jede Funktion auszuführen). Aber danke dafür trotzdem. Es war sehr interessant. – rdegges

Verwandte Themen