diff options
Diffstat (limited to 'Python/errors.c')
-rw-r--r-- | Python/errors.c | 278 |
1 files changed, 230 insertions, 48 deletions
diff --git a/Python/errors.c b/Python/errors.c index b8af1df..9622b5a 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -2,6 +2,7 @@ /* Error handling */ #include "Python.h" +#include "pycore_coreconfig.h" #include "pycore_pystate.h" #ifndef __STDC__ @@ -944,90 +945,271 @@ 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) +PyDoc_STRVAR(UnraisableHookArgs__doc__, +"UnraisableHookArgs\n\ +\n\ +Type used to pass arguments to sys.unraisablehook."); + +static PyTypeObject UnraisableHookArgsType; + +static PyStructSequence_Field UnraisableHookArgs_fields[] = { + {"exc_type", "Exception type"}, + {"exc_value", "Exception value"}, + {"exc_traceback", "Exception traceback"}, + {"object", "Object causing the exception"}, + {0} +}; + +static PyStructSequence_Desc UnraisableHookArgs_desc = { + .name = "UnraisableHookArgs", + .doc = UnraisableHookArgs__doc__, + .fields = UnraisableHookArgs_fields, + .n_in_sequence = 4 +}; + + +_PyInitError +_PyErr_Init(void) { - _Py_IDENTIFIER(__module__); - PyObject *f, *t, *v, *tb; - PyObject *moduleName = NULL; - const char *className; + if (UnraisableHookArgsType.tp_name == NULL) { + if (PyStructSequence_InitType2(&UnraisableHookArgsType, + &UnraisableHookArgs_desc) < 0) { + return _Py_INIT_ERR("failed to initialize UnraisableHookArgs type"); + } + } + return _Py_INIT_OK(); +} - PyErr_Fetch(&t, &v, &tb); - f = _PySys_GetObjectId(&PyId_stderr); - if (f == NULL || f == Py_None) - goto done; +static PyObject * +make_unraisable_hook_args(PyObject *exc_type, PyObject *exc_value, + PyObject *exc_tb, PyObject *obj) +{ + PyObject *args = PyStructSequence_New(&UnraisableHookArgsType); + if (args == NULL) { + return NULL; + } + + Py_ssize_t pos = 0; +#define ADD_ITEM(exc_type) \ + do { \ + if (exc_type == NULL) { \ + exc_type = Py_None; \ + } \ + Py_INCREF(exc_type); \ + PyStructSequence_SET_ITEM(args, pos++, exc_type); \ + } while (0) + + + ADD_ITEM(exc_type); + ADD_ITEM(exc_value); + ADD_ITEM(exc_tb); + ADD_ITEM(obj); +#undef ADD_ITEM + + if (PyErr_Occurred()) { + Py_DECREF(args); + return NULL; + } + return args; +} + + + +/* Default implementation of sys.unraisablehook. + + It can be called to log the exception of a custom sys.unraisablehook. - if (obj) { - if (PyFile_WriteString("Exception ignored in: ", f) < 0) - goto done; - if (PyFile_WriteObject(obj, f, 0) < 0) { + Do nothing if sys.stderr attribute doesn't exist or is set to None. */ +static int +write_unraisable_exc_file(PyObject *exc_type, PyObject *exc_value, + PyObject *exc_tb, PyObject *obj, PyObject *file) +{ + if (obj != NULL && obj != Py_None) { + if (PyFile_WriteString("Exception ignored in: ", file) < 0) { + return -1; + } + + 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 -1; } } - if (PyFile_WriteString("\n", f) < 0) - goto done; + if (PyFile_WriteString("\n", file) < 0) { + return -1; + } } - if (PyTraceBack_Print(tb, f) < 0) - goto done; + if (exc_tb != NULL && exc_tb != Py_None) { + if (PyTraceBack_Print(exc_tb, file) < 0) { + /* continue even if writing the traceback failed */ + PyErr_Clear(); + } + } - if (!t) - goto done; + if (!exc_type) { + return -1; + } - assert(PyExceptionClass_Check(t)); - className = PyExceptionClass_Name(t); + assert(PyExceptionClass_Check(exc_type)); + const char *className = PyExceptionClass_Name(exc_type); if (className != NULL) { const char *dot = strrchr(className, '.'); 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 -1; + } } 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 -1; + } + Py_DECREF(moduleName); + if (PyFile_WriteString(".", file) < 0) { + return -1; + } + } + else { + Py_DECREF(moduleName); } } if (className == NULL) { - if (PyFile_WriteString("<unknown>", f) < 0) - goto done; + if (PyFile_WriteString("<unknown>", file) < 0) { + return -1; + } } else { - if (PyFile_WriteString(className, f) < 0) - goto done; + if (PyFile_WriteString(className, file) < 0) { + return -1; + } } - 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 -1; + } + if (PyFile_WriteObject(exc_value, file, Py_PRINT_RAW) < 0) { PyErr_Clear(); - if (PyFile_WriteString("<exception str() failed>", f) < 0) { + if (PyFile_WriteString("<exception str() failed>", file) < 0) { + return -1; + } + } + } + if (PyFile_WriteString("\n", file) < 0) { + return -1; + } + return 0; +} + + +static int +write_unraisable_exc(PyObject *exc_type, PyObject *exc_value, + PyObject *exc_tb, PyObject *obj) +{ + PyObject *file = _PySys_GetObjectId(&PyId_stderr); + if (file == NULL || file == Py_None) { + return 0; + } + + /* Hold a strong reference to ensure that sys.stderr doesn't go away + while we use it */ + Py_INCREF(file); + int res = write_unraisable_exc_file(exc_type, exc_value, exc_tb, + obj, file); + Py_DECREF(file); + + return res; +} + + +PyObject* +_PyErr_WriteUnraisableDefaultHook(PyObject *args) +{ + if (Py_TYPE(args) != &UnraisableHookArgsType) { + PyErr_SetString(PyExc_TypeError, + "sys.unraisablehook argument type " + "must be UnraisableHookArgs"); + return NULL; + } + + /* Borrowed references */ + PyObject *exc_type = PyStructSequence_GET_ITEM(args, 0); + PyObject *exc_value = PyStructSequence_GET_ITEM(args, 1); + PyObject *exc_tb = PyStructSequence_GET_ITEM(args, 2); + PyObject *obj = PyStructSequence_GET_ITEM(args, 3); + + if (write_unraisable_exc(exc_type, exc_value, exc_tb, obj) < 0) { + return NULL; + } + Py_RETURN_NONE; +} + + +/* Call sys.unraisablehook(). + + This function can be used 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 *exc_type, *exc_value, *exc_tb; + + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + + assert(exc_type != NULL); + + if (exc_type == NULL) { + /* sys.unraisablehook requires that at least exc_type is set */ + goto default_hook; + } + + _Py_IDENTIFIER(unraisablehook); + PyObject *hook = _PySys_GetObjectId(&PyId_unraisablehook); + if (hook != NULL && hook != Py_None) { + PyObject *hook_args; + + hook_args = make_unraisable_hook_args(exc_type, exc_value, exc_tb, obj); + if (hook_args != NULL) { + PyObject *args[1] = {hook_args}; + PyObject *res = _PyObject_FastCall(hook, args, 1); + Py_DECREF(hook_args); + if (res != NULL) { + Py_DECREF(res); goto done; } } + + /* sys.unraisablehook failed: log its error using default hook */ + Py_XDECREF(exc_type); + Py_XDECREF(exc_value); + Py_XDECREF(exc_tb); + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + + obj = hook; } - if (PyFile_WriteString("\n", f) < 0) - goto done; + +default_hook: + /* Call the default unraisable hook (ignore failure) */ + (void)write_unraisable_exc(exc_type, exc_value, exc_tb, obj); done: - Py_XDECREF(moduleName); - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(tb); + Py_XDECREF(exc_type); + Py_XDECREF(exc_value); + Py_XDECREF(exc_tb); PyErr_Clear(); /* Just in case */ } |