summaryrefslogtreecommitdiff
path: root/Python
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@redhat.com>2019-05-22 18:23:28 +0200
committerGitHub <noreply@github.com>2019-05-22 18:23:28 +0200
commita58db9628d0c96cc5b863137fed4e432238f8027 (patch)
treef47a49eb5b1d3f894bb118814857fe54fb5d975b /Python
parentd092caf096fa48baadfc0900792206bb5aa0192d (diff)
downloadcpython-git-a58db9628d0c96cc5b863137fed4e432238f8027.tar.gz
bpo-36829: Enhance PyErr_WriteUnraisable() (GH-13487)
PyErr_WriteUnraisable() now displays the exception even if displaying the traceback failed. Moreover, hold a strong reference to sys.stderr while using it. Document that an exception must be set when calling PyErr_WriteUnraisable(), but don't add an assertion to check it at runtime. Cleanup: use longer names for variables and create write_unraisable_exc_file() subfunction.
Diffstat (limited to 'Python')
-rw-r--r--Python/errors.c137
1 files changed, 84 insertions, 53 deletions
diff --git a/Python/errors.c b/Python/errors.c
index 8c8ea1cd57..6b8bac2fe1 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -940,90 +940,121 @@ PyErr_NewExceptionWithDoc(const char *name, const char *doc,
}
-/* Call when an exception has occurred but there is no way for Python
- to handle it. Examples: exception in __del__ or during GC. */
-void
-PyErr_WriteUnraisable(PyObject *obj)
+static void
+write_unraisable_exc_file(PyObject *exc_type, PyObject *exc_value,
+ PyObject *exc_tb, PyObject *obj, PyObject *file)
{
- _Py_IDENTIFIER(__module__);
- PyObject *f, *t, *v, *tb;
- PyObject *moduleName = NULL;
- char* className;
-
- PyErr_Fetch(&t, &v, &tb);
-
- f = _PySys_GetObjectId(&PyId_stderr);
- if (f == NULL || f == Py_None)
- goto done;
-
if (obj) {
- if (PyFile_WriteString("Exception ignored in: ", f) < 0)
- goto done;
- if (PyFile_WriteObject(obj, f, 0) < 0) {
+ if (PyFile_WriteString("Exception ignored in: ", file) < 0) {
+ return;
+ }
+ if (PyFile_WriteObject(obj, file, 0) < 0) {
PyErr_Clear();
- if (PyFile_WriteString("<object repr() failed>", f) < 0) {
- goto done;
+ if (PyFile_WriteString("<object repr() failed>", file) < 0) {
+ return;
}
}
- if (PyFile_WriteString("\n", f) < 0)
- goto done;
+ if (PyFile_WriteString("\n", file) < 0) {
+ return;
+ }
}
- if (PyTraceBack_Print(tb, f) < 0)
- goto done;
+ if (exc_tb != NULL) {
+ if (PyTraceBack_Print(exc_tb, file) < 0) {
+ /* continue even if writing the traceback failed */
+ PyErr_Clear();
+ }
+ }
- if (!t)
- goto done;
+ if (!exc_type) {
+ return;
+ }
- assert(PyExceptionClass_Check(t));
- className = PyExceptionClass_Name(t);
+ assert(PyExceptionClass_Check(exc_type));
+ char* className = PyExceptionClass_Name(exc_type);
if (className != NULL) {
char *dot = strrchr(className, '.');
- if (dot != NULL)
+ if (dot != NULL) {
className = dot+1;
+ }
}
- moduleName = _PyObject_GetAttrId(t, &PyId___module__);
+ _Py_IDENTIFIER(__module__);
+ PyObject *moduleName = _PyObject_GetAttrId(exc_type, &PyId___module__);
if (moduleName == NULL || !PyUnicode_Check(moduleName)) {
+ Py_XDECREF(moduleName);
PyErr_Clear();
- if (PyFile_WriteString("<unknown>", f) < 0)
- goto done;
+ if (PyFile_WriteString("<unknown>", file) < 0) {
+ return;
+ }
}
else {
if (!_PyUnicode_EqualToASCIIId(moduleName, &PyId_builtins)) {
- if (PyFile_WriteObject(moduleName, f, Py_PRINT_RAW) < 0)
- goto done;
- if (PyFile_WriteString(".", f) < 0)
- goto done;
+ if (PyFile_WriteObject(moduleName, file, Py_PRINT_RAW) < 0) {
+ Py_DECREF(moduleName);
+ return;
+ }
+ Py_DECREF(moduleName);
+ if (PyFile_WriteString(".", file) < 0) {
+ return;
+ }
+ }
+ else {
+ Py_DECREF(moduleName);
}
}
+
if (className == NULL) {
- if (PyFile_WriteString("<unknown>", f) < 0)
- goto done;
+ if (PyFile_WriteString("<unknown>", file) < 0) {
+ return;
+ }
}
else {
- if (PyFile_WriteString(className, f) < 0)
- goto done;
+ if (PyFile_WriteString(className, file) < 0) {
+ return;
+ }
}
- if (v && v != Py_None) {
- if (PyFile_WriteString(": ", f) < 0)
- goto done;
- if (PyFile_WriteObject(v, f, Py_PRINT_RAW) < 0) {
+ if (exc_value && exc_value != Py_None) {
+ if (PyFile_WriteString(": ", file) < 0) {
+ return;
+ }
+ if (PyFile_WriteObject(exc_value, file, Py_PRINT_RAW) < 0) {
PyErr_Clear();
- if (PyFile_WriteString("<exception str() failed>", f) < 0) {
- goto done;
+ if (PyFile_WriteString("<exception str() failed>", file) < 0) {
+ return;
}
}
}
- if (PyFile_WriteString("\n", f) < 0)
- goto done;
+ if (PyFile_WriteString("\n", file) < 0) {
+ return;
+ }
+}
-done:
- Py_XDECREF(moduleName);
- Py_XDECREF(t);
- Py_XDECREF(v);
- Py_XDECREF(tb);
+
+/* Display an unraisable exception into sys.stderr.
+
+ Called when an exception has occurred but there is no way for Python to
+ handle it. For example, when a destructor raises an exception or during
+ garbage collection (gc.collect()).
+
+ An exception must be set when calling this function. */
+void
+PyErr_WriteUnraisable(PyObject *obj)
+{
+ PyObject *f, *exc_type, *exc_value, *exc_tb;
+
+ PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
+
+ f = _PySys_GetObjectId(&PyId_stderr);
+ /* Do nothing if sys.stderr is not available or set to None */
+ if (f != NULL && f != Py_None) {
+ write_unraisable_exc_file(exc_type, exc_value, exc_tb, obj, f);
+ }
+
+ Py_XDECREF(exc_type);
+ Py_XDECREF(exc_value);
+ Py_XDECREF(exc_tb);
PyErr_Clear(); /* Just in case */
}