Ich schreibe ein Modul und möchte eine einheitliche Ausnahmehierarchie für die Ausnahmen haben, die es auslösen kann (z. B. Erben von einer FooError
abstrakten Klasse für alle foo
spezifischen Ausnahmen des Moduls). Auf diese Weise können Benutzer des Moduls diese bestimmten Ausnahmen abfangen und bei Bedarf eindeutig behandeln. Viele der vom Modul ausgelösten Ausnahmen werden jedoch aufgrund einer anderen Ausnahme ausgelöst. z. B. Fehler bei einer Aufgabe aufgrund eines OSError in einer Datei.
Was ich brauche, ist, die gefangene Ausnahme so zu "verpacken", dass sie einen anderen Typ und eine andere Nachricht hat , so dass Informationen weiter oben in der Propagierungshierarchie verfügbar sind, je nachdem, was die Ausnahme abfängt. Aber ich möchte den vorhandenen Typ, die Nachricht und den Stack-Trace nicht verlieren. Das sind alles nützliche Informationen für jemanden, der versucht, das Problem zu beheben. Ein Ausnahmebehandler der obersten Ebene ist nicht gut, da ich versuche, die Ausnahme zu dekorieren, bevor sie den Propagierungsstapel weiter nach oben führt, und der Handler der obersten Ebene zu spät ist.
Dies wird teilweise dadurch gelöst, dass die foo
spezifischen Ausnahmetypen meines Moduls vom vorhandenen Typ abgeleitet werden (z. B. class FooPermissionError(OSError, FooError)
). Dies macht es jedoch nicht einfacher, die vorhandene Ausnahmeinstanz in einen neuen Typ zu packen oder die Nachricht zu ändern.
In Pythons PEP 3134 „Ausnahmekettung und eingebettete Tracebacks“ wird eine in Python 3.0 akzeptierte Änderung für die Verkettung von Ausnahmeobjekten erläutert, um anzuzeigen, dass während der Behandlung einer vorhandenen Ausnahme eine neue Ausnahme ausgelöst wurde.
Was ich versuche zu tun, ist verwandt: Ich brauche es auch in früheren Python-Versionen, und ich brauche es nicht zum Verketten, sondern nur für Polymorphismus. Was ist der richtige Weg, um dies zu tun?
quelle
except Exception as e
->raise type(e), type(e)(e.message + custom_message), sys.exc_info()[2]
-> Diese Lösung stammt aus einer anderen SO-Frage . Das ist nicht schön, aber funktional.Antworten:
Python 3 führte eine Ausnahmeverkettung ein (wie in PEP 3134 beschrieben ). Auf diese Weise können Sie beim Auslösen einer Ausnahme eine vorhandene Ausnahme als „Ursache“ angeben:
Die abgefangene Ausnahme wird dadurch Teil der neuen Ausnahme (ist die „Ursache“ dafür) und steht für jeden Code zur Verfügung, der die neue Ausnahme abfängt.
Mit dieser Funktion wird das
__cause__
Attribut festgelegt. Der integrierte Ausnahmebehandler weiß auch , wie die "Ursache" und der "Kontext" der Ausnahme zusammen mit dem Traceback gemeldet werden.In Python 2 scheint dieser Anwendungsfall keine gute Antwort zu haben (wie von Ian Bicking und Ned Batchelder beschrieben ). Schade.
quelle
try: return 2 / 0 except ZeroDivisionError as e: raise ValueError(e)
Sie können sys.exc_info () verwenden, um den Traceback abzurufen, und Ihre neue Ausnahme mit diesem Traceback auslösen (wie im PEP erwähnt). Wenn Sie den alten Typ und die alte Nachricht beibehalten möchten, können Sie dies für die Ausnahme tun. Dies ist jedoch nur dann nützlich, wenn das, was Ihre Ausnahme abfängt, danach sucht.
Beispielsweise
Das ist natürlich nicht so nützlich. Wenn es so wäre, würden wir diesen PEP nicht brauchen. Ich würde es nicht empfehlen.
quelle
sys.exc_info()
@Devin. Es heißt: "Das Zuweisen des Traceback-Rückgabewerts zu einer lokalen Variablen in einer Funktion, die eine Ausnahme behandelt, führt zu einem Zirkelverweis." In der folgenden Anmerkung heißt es jedoch, dass der Zyklus seit Python 2.2 bereinigt werden kann, es jedoch effizienter ist, ihn nur zu vermeiden.Sie können Ihren eigenen Ausnahmetyp erstellen, der die von Ihnen abgefangene Ausnahme erweitert .
Aber die meiste Zeit denke ich, dass es einfacher wäre, die Ausnahme abzufangen, damit
raise
umzugehen und entweder die ursprüngliche Ausnahme (und den Traceback beizubehalten) oderraise NewException()
. Wenn ich Ihren Code aufrufen würde und eine Ihrer benutzerdefinierten Ausnahmen erhalten würde, würde ich erwarten, dass Ihr Code bereits alle Ausnahmen behandelt hat, die Sie abfangen mussten. Daher muss ich nicht selbst darauf zugreifen.Bearbeiten: Ich habe diese Analyse der Möglichkeiten gefunden, Ihre eigene Ausnahme auszulösen und die ursprüngliche Ausnahme beizubehalten. Keine schönen Lösungen.
quelle
Ich fand auch, dass ich oft etwas "Wrapping" für Fehler brauche.
Dies umfasste sowohl einen Funktionsumfang als auch manchmal nur einige Zeilen innerhalb einer Funktion.
Erstellt einen Wrapper zur Verwendung von a
decorator
undcontext manager
:Implementierung
Anwendungsbeispiele
Dekorateur
beim Aufruf
do
Methode, keine Sorge überIndexError
, nurMyError
Kontextmanager
innen
do2
, in dercontext manager
, wennIndexError
es angehoben wird, wird es eingewickelt und angehobenMyError
quelle
Die einfachste Lösung für Ihre Anforderungen sollte folgende sein:
Auf diese Weise können Sie später Ihre Nachricht und den spezifischen Fehler drucken, der von der Upload-Funktion ausgelöst wird
quelle