So erhalten Sie eine Ausnahmemeldung in Python richtig

101

Was ist der beste Weg, um Ausnahmemeldungen von Komponenten der Standardbibliothek in Python abzurufen?

Mir ist aufgefallen, dass Sie es in einigen Fällen über ein messageFeld wie das folgende erhalten können:

try:
  pass
except Exception as ex:
  print(ex.message)

In einigen Fällen (z. B. bei Socket-Fehlern) müssen Sie jedoch Folgendes tun:

try:
  pass
except socket.error as ex:
  print(ex)

Ich habe mich gefragt, ob es einen Standardweg gibt, um die meisten dieser Situationen abzudecken.

Gefrorenes Herz
quelle
1
Sie verbinden zwei verschiedene Dinge - except Foo as bar:ist dasselbe wie except Foo, bar:(außer dass das erstere neuer ist und in 3.x weiterhin funktioniert), ob der Fehler mit einem messageAttribut kommt oder nicht, ist getrennt.
Jonrsharpe
1
@FrozenHeart Fragen Sie, ob der Zugriff .messageauf einen Fehler Standard ist oder nicht?
Anand S Kumar
Ihr zweites Beispiel mit print(msg)ist nur eine Abkürzung fürprint(str(msg))
Salo
@ Anand S Kumar Yep. Wird es auf jeden Fall funktionieren?
FrozenHeart
4
Ich glaube, der Zugriff messageist veraltet.
Jonathon Reinhart

Antworten:

98

Wenn Sie sich die Dokumentation für die integrierten Fehler ansehen , werden Sie feststellen, dass die meisten ExceptionKlassen ihr erstes Argument als messageAttribut zuweisen . Nicht alle von ihnen tun dies jedoch.

Insbesondere EnvironmentError(mit Unterklassen IOErrorund OSError) hat ein erstes Argument von errno, das zweite von strerror. Es gibt kein message... strerrorist ungefähr analog zu dem, was normalerweise ein wäre message.

Im Allgemeinen können Unterklassen von Exceptiontun, was sie wollen. Sie können ein messageAttribut haben oder nicht . Zukünftige integrierte Exceptions haben möglicherweise kein messageAttribut. Jede ExceptionUnterklasse importiert von Bibliotheken von Drittanbietern oder Benutzercode möglicherweise nicht über einemessage Attribut.

Ich denke, der richtige Weg, dies zu handhaben, besteht darin, die spezifischen ExceptionUnterklassen zu identifizieren, die Sie abfangen möchten, und dann nur diejenigen anstelle von allem mit einem except Exceptionabzufangen und dann die Attribute zu verwenden, die diese bestimmte Unterklasse definiert, wie Sie möchten.

Wenn Sie printetwas müssen , denke ich, dass das Drucken des gefangenen Exceptionselbst höchstwahrscheinlich das tut, was Sie wollen, unabhängig davon, ob es ein messageAttribut hat oder nicht.

Sie können auch nach dem Nachrichtenattribut suchen, wenn Sie dies möchten, aber ich würde es nicht wirklich vorschlagen, da es nur chaotisch erscheint:

try:
    pass
except Exception as e:
    # Just print(e) is cleaner and more likely what you want,
    # but if you insist on printing message specifically whenever possible...
    if hasattr(e, 'message'):
        print(e.message)
    else:
        print(e)
ArtOfWarfare
quelle
Danke für die Antwort. Gibt es einen bestimmten Grund, str(ex)anstatt nur zu verwenden ex?
FrozenHeart
4
@FrozenHeart - print()ruft automatisch str()nach Ihnen. Es gibt keinen Grund, es manuell aufzurufen, obwohl es harmlos ist, da das Aufrufen str()einer Zeichenfolge nur die Zeichenfolge selbst zurückgibt.
ArtOfWarfare
1
@ArtOfWarfare Ich denke, es könnte Probleme geben, str()wenn die Nachricht vom Typ ist unicode.
Kratenko
39

Um die Antwort von @artofwarfare zu verbessern, halte ich Folgendes für eine bessere Möglichkeit, nach dem messageAttribut zu suchen und es zu drucken oder das ExceptionObjekt als Fallback zu drucken .

try:
    pass 
except Exception as e:
    print getattr(e, 'message', repr(e))

Der Aufruf von reprist optional, aber ich finde es in einigen Anwendungsfällen notwendig.


Update Nr. 1:

Nach dem Kommentar von @MadPhysicist finden Sie hier einen Beweis dafür, warum der Aufruf von reprmöglicherweise erforderlich ist. Versuchen Sie, den folgenden Code in Ihrem Interpreter auszuführen:

try:
    raise Exception 
except Exception as e:
    print(getattr(e, 'message', repr(e)))
    print(getattr(e, 'message', str(e)))

Update Nr. 2:

Hier ist eine Demo mit Besonderheiten für Python 2.7 und 3.5: https://gist.github.com/takwas/3b7a6edddef783f2abddffda1439f533

Tosin Mash
quelle
1
getattrlöst eine Ausnahme aus, wenn das übergebene Objekt nicht das angeforderte Attribut hat. Wenn Sie so etwas tun wollten, sollten Sie das optionale dritte Argument für getattrFolgendes verwenden : print getattr(e, 'message', e). Zweifellos besser als das, was ich getan habe. Eine andere Option wäre print(e.message if hasattr(e, 'message') else e).
ArtOfWarfare
2
Sie würden mit ziemlicher Sicherheit strstattdessen wollen repr.
Mad Physicist
2
Im Vergleich zu str, reprfügt nur die Klassennamen. Anstatt zu verwenden repr, würde ich vorschlagen, zu verwenden print('{e.__class__.__name__}: {e}'.format(e=e)). Die Druckausgabe ist sauberer und entspricht der Ausgabe von Raise selbst.
Kadee
1
Basierend auf dem oben '{e.__class__.__module__}.{e.__class__.__name__}: {e}'
Gesagten
1
Vielen Dank!! repr(e)war vorerst die einzige Lösung, die mir geholfen hat, mindestens eine minimale Menge an Informationen über einen Fehler zu drucken, den ich unter bestimmten Umständen festgestellt habe. repr(e)ergibt KeyError(0,)(was der Fehler ist), während str(e)oder e.messagenur 0oder gar nichts ergab .
FlorianH
0

Ich hatte das gleiche Problem. Ich denke, die beste Lösung ist die Verwendung von log.exception, die automatisch die Stapelverfolgung und die Fehlermeldung ausgibt, wie z.

try:
    pass
    log.info('Success')
except:
    log.exception('Failed')
Bill Z.
quelle
2
Was ist log? Ich habe gerade import logPython 2.7.13 ausprobiert und die Meldung erhalten, dass es kein Modul mit diesem Namen gibt. Ist es etwas, das Sie über hinzugefügt haben, pipoder wurde es in einer neueren Version von Python hinzugefügt (ich weiß, ich weiß, ich muss auf Python 3 aktualisieren ... Ich werde das tun, sobald CentOS standardmäßig nicht mehr mit Python 2 ausgeliefert wird ...)
ArtOfWarfare
7
Er / Sie verwendet wahrscheinlich das Protokollierungsmodul von Python und instanziiert dann einen Protokollierer als Protokollvariable. import logging\ nlog = logging.getLogger(__name__)
Josepas
0

Ich hatte auch das gleiche Problem. Als ich mich damit befasste, stellte ich fest, dass die Exception-Klasse ein argsAttribut hat, das die Argumente erfasst, die zum Erstellen der Ausnahme verwendet wurden. Wenn Sie die Ausnahmen eingrenzen, die mit Ausnahme einer Teilmenge abgefangen werden, sollten Sie bestimmen können, wie sie erstellt wurden und welches Argument die Nachricht enthält.

try:
   # do something that may raise an AuthException
except AuthException as ex:
   if ex.args[0] == "Authentication Timeout.":
      # handle timeout
   else:
      # generic handling
user2501097
quelle