PEP 409 – Unterdrücken des Exception-Kontexts
- Autor:
- Ethan Furman <ethan at stoneleaf.us>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 26-Jan-2012
- Python-Version:
- 3.3
- Post-History:
- 30-Aug-2002, 01-Feb-2012, 03-Feb-2012
- Ersetzt-Durch:
- 415
- Resolution:
- Python-Dev Nachricht
Zusammenfassung
Eines der offenen Probleme aus PEP 3134 ist das Unterdrücken des Kontexts: derzeit gibt es keine Möglichkeit, dies zu tun. Dieser PEP schlägt eine vor.
Begründung
Es gibt zwei grundlegende Arten, Ausnahmen zu erzeugen
- Python tut es (fehlerhafter Code, fehlende Ressourcen, Enden von Schleifen usw.)
- manuell (mit einer `raise`-Anweisung)
Beim Schreiben von Bibliotheken oder sogar nur von benutzerdefinierten Klassen kann es notwendig werden, Ausnahmen auszulösen. Darüber hinaus kann es nützlich, sogar notwendig sein, von einer Ausnahme zu einer anderen zu wechseln. Um ein Beispiel aus meinem `dbf`-Modul zu nehmen
try:
value = int(value)
except Exception:
raise DbfError(...)
Was auch immer die ursprüngliche Ausnahme war (ValueError, TypeError oder etwas anderes), ist irrelevant. Die Ausnahme von diesem Zeitpunkt an ist eine DbfError, und die ursprüngliche Ausnahme ist von keinem Wert. Wenn diese Ausnahme jedoch gedruckt wird, würden wir derzeit beide sehen.
Alternativen
Mehrere Möglichkeiten wurden vorgeschlagen
raise as NewException()Verwendet das Schlüsselwort `as` wieder; kann verwirrend sein, da wir die ursprüngliche Ausnahme nicht wirklich erneut auslösen
raise NewException() from NoneFolgt der bestehenden Syntax, die die ursprüngliche Ausnahme explizit deklariert
exc = NewException(); exc.__context__ = None; raise excSehr ausführliche Variante der vorherigen Methode
raise NewException.no_context(...)Kontextunterdrückung als Klassenmethode.
Alle oben genannten Optionen erfordern Änderungen am Kern.
Vorschlag
Ich schlage vor, die zweite Option zu wählen
raise NewException from None
Sie hat den Vorteil, dass das bestehende Muster der expliziten Festlegung der Ursache verwendet wird
raise KeyError() from NameError()
aber da die Ursache None ist, wird der vorherige Kontext von den Standard-Ausdruckroutinen nicht angezeigt.
Diskussion der Implementierung
Hinweis: Nach der Annahme dieses PEPs wurde ein sauberer Implementierungsmechanismus vorgeschlagen und in PEP 415 angenommen. Verweisen Sie auf diesen PEP für weitere Details zur tatsächlichen Implementierung, die in Python 3.3 verwendet wird.
Derzeit ist None der Standard für sowohl __context__ als auch __cause__. Um raise ... from None (was __cause__ auf None setzen würde) zu unterstützen, benötigen wir einen anderen Standardwert für __cause__. Mehrere Ideen wurden zur Implementierung auf Sprachniveau vorgestellt
- Überschreiben der vorherigen Ausnahminformationen (umgehen des Problems und belassen von
__cause__beiNone).Abgelehnt, da dies die Fehlersuche aufgrund von schlechten Fehlermeldungen erheblich behindern kann.
- Verwendung eines der booleschen Werte in
__cause__:Falsewäre der Standardwert und würde ersetzt, wennfrom ...mit der explizit verketteten Ausnahme oderNoneverwendet wurde.Abgelehnt, da dies die Verwendung von zwei verschiedenen Objekttypen für
__cause__fördert, wobei einer von ihnen (boolesch) nicht den vollen Bereich möglicher Werte haben darf (Truewürde nie verwendet werden). - Erstellung einer speziellen Ausnahmeklasse,
__NoException__.Abgelehnt, da möglicherweise verwirrend, möglicherweise versehentlich von Benutzern ausgelöst und kein wirklich eindeutiger Wert wie
None,TrueundFalse. - Verwendung von
Ellipsisals Standardwert (das...Singleton).Angenommen.
Ellipsen werden im Englischen häufig als Platzhalter verwendet, wenn Wörter weggelassen werden. Dies funktioniert hier zu unseren Gunsten als Signal, dass
__cause__weggelassen wird, sodass__context__für weitere Details herangezogen werden muss.Ellipsis ist keine Ausnahme, kann also nicht ausgelöst werden.
Es gibt nur ein Ellipsis, also keine ungenutzten Werte.
Fehlerinformationen werden nicht verworfen, so dass benutzerdefinierter Code die gesamte Ausnahmekette verfolgen kann, auch wenn der Standardcode dies nicht tut.
Sprachdetails
Um raise Exception from None zu unterstützen, bleibt __context__ unverändert, aber __cause__ beginnt als Ellipsis und wird zu None geändert, wenn die Methode raise Exception from None verwendet wird.
| form | __context__ | __cause__ |
|---|---|---|
| raise | Keine |
Ellipsis |
| reraise | vorherige Ausnahme | Ellipsis |
reraise from None | ChainedException |
vorherige Ausnahme | None | explizit verkettete Ausnahme |
Die Standard-Ausdruckroutine wird dann
- Wenn
__cause__Ellipsisist, wird__context__(falls vorhanden) gedruckt. - Wenn
__cause__Noneist, wird__context__nicht gedruckt. - Wenn
__cause__etwas anderes ist, wird__cause__gedruckt.
In beiden letzten Fällen wird die Ausnahmekette nicht mehr verfolgt.
Da der Standardwert für __cause__ nun Ellipsis ist und raise Exception from Cause lediglich syntaktischer Zucker für
_exc = NewException()
_exc.__cause__ = Cause()
raise _exc
Ellipsis, sowie None, ist nun als Ursache erlaubt
raise Exception from Ellipsis
Patches
Ein Patch für CPython, der dies implementiert, ist an Issue 6210 angehängt.
Referenzen
Diskussion und Verfeinerungen in diesem Thread auf python-dev.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0409.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT