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

Python Enhancement Proposals

PEP 490 – Ausnahmen auf C-Ebene verketten

Autor:
Victor Stinner <vstinner at python.org>
Status:
Abgelehnt
Typ:
Standards Track
Erstellt:
25-Mrz-2015
Python-Version:
3.6

Inhaltsverzeichnis

Zusammenfassung

Ausnahmen auf C-Ebene verketten, wie es auf Python-Ebene bereits geschieht.

Begründung

Python 3 führte ein neues Killer-Feature ein: Ausnahmen werden standardmäßig verkettet, PEP 3134.

Beispiel

try:
    raise TypeError("err1")
except TypeError:
    raise ValueError("err2")

Ausgabe

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    raise TypeError("err1")
TypeError: err1

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise ValueError("err2")
ValueError: err2

Ausnahmen werden im Python-Code standardmäßig verkettet, jedoch nicht in Erweiterungen, die in C geschrieben sind.

Eine neue private Funktion _PyErr_ChainExceptions() wurde in Python 3.4.3 und 3.5 eingeführt, um Ausnahmen zu verketten. Derzeit muss sie explizit aufgerufen werden, um Ausnahmen zu verketten, und ihre Verwendung ist nicht trivial.

Beispiel für die Verwendung von _PyErr_ChainExceptions() aus dem Modul zipimport, um die vorherige OSError mit einer neuen ZipImportError Ausnahme zu verketten

PyObject *exc, *val, *tb;
PyErr_Fetch(&exc, &val, &tb);
PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);
_PyErr_ChainExceptions(exc, val, tb);

Dieser PEP schlägt vor, Ausnahmen auch auf C-Ebene automatisch zu verketten, um konsistent zu bleiben und mehr Informationen über Fehler zu liefern, was bei der Fehlersuche hilft. Das vorherige Beispiel wird einfach zu

PyErr_Format(ZipImportError, "can't open Zip file: %R", archive);

Vorschlag

PyErr_*()-Funktionen ändern, um Ausnahmen zu verketten

C-Funktionen der Python C API, die Ausnahmen auslösen, so ändern, dass Ausnahmen automatisch verkettet werden: PyErr_SetString(), PyErr_Format(), PyErr_SetNone() usw. ändern.

Funktionen ändern, um keine Ausnahmen zu verketten

Das Beibehalten der vorherigen Ausnahme ist nicht immer interessant, wenn die neue Ausnahme Informationen über die vorherige Ausnahme oder sogar mehr Informationen enthält, insbesondere wenn die beiden Ausnahmen den gleichen Typ haben.

Beispiel einer nutzlosen Ausnahmekette mit int(str)

TypeError: a bytes-like object is required, not 'type'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string, a bytes-like object or a number, not 'type'

Die neue TypeError Ausnahme enthält mehr Informationen als die vorherige Ausnahme. Die vorherige Ausnahme sollte verborgen werden.

Die Funktion PyErr_Clear() kann aufgerufen werden, um die aktuelle Ausnahme zu löschen, bevor eine neue Ausnahme ausgelöst wird, um die aktuelle Ausnahme nicht mit einer neuen Ausnahme zu verketten.

Funktionen ändern, um Ausnahmen zu verketten

Einige Funktionen speichern und stellen die aktuelle Ausnahme wieder her. Wenn eine neue Ausnahme ausgelöst wird, wird die Ausnahme derzeit in `sys.stderr` angezeigt oder je nach Funktion ignoriert. Einige dieser Funktionen sollten geändert werden, um stattdessen Ausnahmen zu verketten.

Beispiele für Funktionen, die neue Ausnahme(n) ignorieren

  • ptrace_enter_call(): Ausnahme ignorieren
  • subprocess_fork_exec(): von `enable_gc()` ausgelöste Ausnahme ignorieren
  • t_bootstrap() des Moduls _thread: von dem Versuch, die Bootstrap-Funktion anzuzeigen, ausgelöste Ausnahme ignorieren sys.stderr
  • PyDict_GetItem(), _PyDict_GetItem_KnownHash(): von der Suche nach einem Schlüssel im Wörterbuch ausgelöste Ausnahme ignorieren
  • _PyErr_TrySetFromCause(): Ausnahme ignorieren
  • PyFrame_LocalsToFast(): von dict_to_map() ausgelöste Ausnahme ignorieren
  • _PyObject_Dump(): Ausnahme ignorieren. _PyObject_Dump() wird zur Fehlersuche, zur Inspektion eines laufenden Prozesses verwendet und sollte den Python-Zustand nicht ändern.
  • Py_ReprLeave(): Ausnahme ignorieren, "da es keine Möglichkeit gibt, sie zu melden"
  • type_dealloc(): von remove_all_subclasses() ausgelöste Ausnahme ignorieren
  • PyObject_ClearWeakRefs(): Ausnahme ignorieren?
  • call_exc_trace(), call_trace_protected(): Ausnahme ignorieren
  • remove_importlib_frames(): Ausnahme ignorieren
  • do_mktuple(), Hilfsfunktion, die z. B. von Py_BuildValue() verwendet wird: Ausnahme ignorieren?
  • flush_io(): Ausnahme ignorieren
  • sys_write(), sys_format(): Ausnahme ignorieren
  • _PyTraceback_Add(): Ausnahme ignorieren
  • PyTraceBack_Print(): Ausnahme ignorieren

Beispiele für Funktionen, die die neue Ausnahme anzeigen sys.stderr

  • atexit_callfuncs(): Ausnahmen mit PyErr_Display() anzeigen und die letzte Ausnahme zurückgeben; die Funktion ruft mehrere Callbacks auf und gibt nur die letzte Ausnahme zurück
  • sock_dealloc(): ResourceWarning Ausnahme mit PyErr_WriteUnraisable() protokollieren
  • slot_tp_del(): Ausnahme mit PyErr_WriteUnraisable() anzeigen
  • _PyGen_Finalize(): gen_close() Ausnahme mit PyErr_WriteUnraisable() anzeigen
  • slot_tp_finalize(): von der Methode __del__() ausgelöste Ausnahme mit PyErr_WriteUnraisable() anzeigen
  • PyErr_GivenExceptionMatches(): von PyType_IsSubtype() ausgelöste Ausnahme mit PyErr_WriteUnraisable() anzeigen

Abwärtskompatibilität

Eine Nebenwirkung des Verkettens von Ausnahmen ist, dass Ausnahmen Traceback-Objekte speichern, die Frame-Objekte speichern, die lokale Variablen speichern. Lokale Variablen werden durch Ausnahmen am Leben gehalten. Ein häufiges Problem ist ein Referenzzyklus zwischen lokalen Variablen und Ausnahmen: Eine Ausnahme wird in einer lokalen Variablen gespeichert und der Frame, der indirekt in der Ausnahme gespeichert ist. Der Zyklus beeinträchtigt nur Anwendungen, die Ausnahmen speichern.

Der Referenzzyklus kann nun mit dem neuen traceback.TracebackException Objekt, das in Python 3.5 eingeführt wurde, behoben werden. Es speichert Informationen, die zum Formatieren eines vollständigen textlichen Tracebacks erforderlich sind, ohne lokale Variablen zu speichern.

Das Modul asyncio ist vom Problem des Referenzzyklus betroffen. Dieses Modul wird auch außerhalb der Python-Standardbibliothek gepflegt, um eine Version für Python 3.3 zu veröffentlichen. traceback.TracebackException wird möglicherweise in einem privaten asyncio Modul zurückportiert, um Probleme mit Referenzzyklen zu beheben.

Alternativen

Keine Änderung

Eine neue private Funktion _PyErr_ChainExceptions() reicht aus, um Ausnahmen manuell zu verketten.

Ausnahmen werden nur explizit verkettet, wo es sinnvoll ist.

Neue Hilfsfunktionen zum Verketten von Ausnahmen

Funktionen wie PyErr_SetString() verketten Ausnahmen nicht automatisch. Um die Verwendung von _PyErr_ChainExceptions() zu erleichtern, werden neue private Funktionen hinzugefügt

  • _PyErr_SetStringChain(exc_type, message)
  • _PyErr_FormatChain(exc_type, format, ...)
  • _PyErr_SetNoneChain(exc_type)
  • _PyErr_SetObjectChain(exc_type, exc_value)

Hilfsfunktionen zum Auslösen spezifischer Ausnahmen wie _PyErr_SetKeyError(key) oder PyErr_SetImportError(message, name, path) verketten keine Ausnahmen. Die generische _PyErr_ChainExceptions(exc_type, exc_value, exc_tb) sollte verwendet werden, um Ausnahmen mit diesen Hilfsfunktionen zu verketten.

Anhang

PEPs

  • PEP 3134 – Exception Chaining and Embedded Tracebacks (Python 3.0): neue Attribute __context__ und __cause__ für Ausnahmen
  • PEP 415 – Implement context suppression with exception attributes (Python 3.3): raise exc from None
  • PEP 409 – Suppressing exception context (ersetzt durch PEP 415)

Python C API

Die Header-Datei Include/pyerror.h deklariert Funktionen, die sich auf Ausnahmen beziehen.

Funktionen, die Ausnahmen auslösen

  • PyErr_SetNone(exc_type)
  • PyErr_SetObject(exc_type, exc_value)
  • PyErr_SetString(exc_type, message)
  • PyErr_Format(exc, format, ...)

Helfer zum Auslösen spezifischer Ausnahmen

  • PyErr_BadArgument()
  • PyErr_BadInternalCall()
  • PyErr_NoMemory()
  • PyErr_SetFromErrno(exc)
  • PyErr_SetFromWindowsErr(err)
  • PyErr_SetImportError(message, name, path)
  • _PyErr_SetKeyError(key)
  • _PyErr_TrySetFromCause(prefix_format, ...)

Verwaltung der aktuellen Ausnahme

  • PyErr_Clear(): löscht die aktuelle Ausnahme, wie bei except: pass
  • PyErr_Fetch(exc_type, exc_value, exc_tb)
  • PyErr_Restore(exc_type, exc_value, exc_tb)
  • PyErr_GetExcInfo(exc_type, exc_value, exc_tb)
  • PyErr_SetExcInfo(exc_type, exc_value, exc_tb)

Weitere Funktionen zur Behandlung von Ausnahmen

  • PyErr_ExceptionMatches(exc): prüft die Implementierung von except exc:  ...
  • PyErr_GivenExceptionMatches(exc1, exc2)
  • PyErr_NormalizeException(exc_type, exc_value, exc_tb)
  • _PyErr_ChainExceptions(exc_type, exc_value, exc_tb)

Python-Probleme

Ausnahmen verketten

Änderungen, die verhindern, dass Ausnahmen verloren gehen

Ablehnung

Der PEP wurde am 12.09.2017 von Victor Stinner abgelehnt. In der Diskussion im python-dev wurde beschlossen, C-Ausnahmen nicht standardmäßig zu verketten, sondern sie explizit nur dort zu verketten, wo es sinnvoll ist.


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

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