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 ignorierensubprocess_fork_exec(): von `enable_gc()` ausgelöste Ausnahme ignorierent_bootstrap()des Moduls_thread: von dem Versuch, die Bootstrap-Funktion anzuzeigen, ausgelöste Ausnahme ignorierensys.stderrPyDict_GetItem(),_PyDict_GetItem_KnownHash(): von der Suche nach einem Schlüssel im Wörterbuch ausgelöste Ausnahme ignorieren_PyErr_TrySetFromCause(): Ausnahme ignorierenPyFrame_LocalsToFast(): vondict_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(): vonremove_all_subclasses()ausgelöste Ausnahme ignorierenPyObject_ClearWeakRefs(): Ausnahme ignorieren?call_exc_trace(),call_trace_protected(): Ausnahme ignorierenremove_importlib_frames(): Ausnahme ignorierendo_mktuple(), Hilfsfunktion, die z. B. vonPy_BuildValue()verwendet wird: Ausnahme ignorieren?flush_io(): Ausnahme ignorierensys_write(),sys_format(): Ausnahme ignorieren_PyTraceback_Add(): Ausnahme ignorierenPyTraceBack_Print(): Ausnahme ignorieren
Beispiele für Funktionen, die die neue Ausnahme anzeigen sys.stderr
atexit_callfuncs(): Ausnahmen mitPyErr_Display()anzeigen und die letzte Ausnahme zurückgeben; die Funktion ruft mehrere Callbacks auf und gibt nur die letzte Ausnahme zurücksock_dealloc():ResourceWarningAusnahme mitPyErr_WriteUnraisable()protokollierenslot_tp_del(): Ausnahme mitPyErr_WriteUnraisable()anzeigen_PyGen_Finalize():gen_close()Ausnahme mitPyErr_WriteUnraisable()anzeigenslot_tp_finalize(): von der Methode__del__()ausgelöste Ausnahme mitPyErr_WriteUnraisable()anzeigenPyErr_GivenExceptionMatches(): vonPyType_IsSubtype()ausgelöste Ausnahme mitPyErr_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
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 beiexcept: passPyErr_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 vonexcept 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
- Issue #23763: Ausnahmen in C verketten
- Issue #23696: zipimport: ImportError mit OSError verketten
- Issue #21715: Ausnahmen auf C-Ebene verketten:
_PyErr_ChainExceptions()hinzugefügt - Issue #18488: sqlite: finalize()-Methode einer Benutzerfunktion kann mit gesetzter Ausnahme aufgerufen werden, wenn ein Aufruf der step()-Methode fehlschlug
- Issue #23781: Füge privates _PyErr_ReplaceException() in 2.7 hinzu
- Issue #23782: Leck in _PyTraceback_Add
Ä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.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0490.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT