Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

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

Inhaltsverzeichnis

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

  1. Python tut es (fehlerhafter Code, fehlende Ressourcen, Enden von Schleifen usw.)
  2. 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 None

    Folgt der bestehenden Syntax, die die ursprüngliche Ausnahme explizit deklariert

  • exc = NewException(); exc.__context__ = None; raise exc

    Sehr 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__ bei None).

    Abgelehnt, da dies die Fehlersuche aufgrund von schlechten Fehlermeldungen erheblich behindern kann.

  • Verwendung eines der booleschen Werte in __cause__: False wäre der Standardwert und würde ersetzt, wenn from ... mit der explizit verketteten Ausnahme oder None verwendet 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 (True wü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, True und False.

  • Verwendung von Ellipsis als 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__ Ellipsis ist, wird __context__ (falls vorhanden) gedruckt.
  • Wenn __cause__ None ist, 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.


Quelle: https://github.com/python/peps/blob/main/peps/pep-0409.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT