]> granicus.if.org Git - python/commitdiff
bpo-36829: Add sys.unraisablehook() (GH-13187)
authorVictor Stinner <vstinner@redhat.com>
Wed, 22 May 2019 09:28:22 +0000 (11:28 +0200)
committerGitHub <noreply@github.com>
Wed, 22 May 2019 09:28:22 +0000 (11:28 +0200)
Add new sys.unraisablehook() function which can be overridden to
control how "unraisable exceptions" are handled. It is 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()).

Changes:

* Add an internal UnraisableHookArgs type used to pass arguments to
  sys.unraisablehook.
* Add _PyErr_WriteUnraisableDefaultHook().
* The default hook now ignores exception on writing the traceback.
* test_sys now uses unittest.main() to automatically discover tests:
  remove test_main().
* Add _PyErr_Init().
* Fix PyErr_WriteUnraisable(): hold a strong reference to sys.stderr
  while using it

12 files changed:
Doc/c-api/exceptions.rst
Doc/library/sys.rst
Doc/whatsnew/3.8.rst
Include/internal/pycore_pylifecycle.h
Lib/test/test_sys.py
Misc/NEWS.d/next/Library/2019-05-08-12-51-37.bpo-36829.8enFMA.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Python/clinic/sysmodule.c.h
Python/errors.c
Python/importlib.h
Python/pylifecycle.c
Python/sysmodule.c

index 00ef00526bc5f8ba82aea2e1867acb92b3b473d7..18ff697a2325eb42d9d60589880164233c7250be 100644 (file)
@@ -72,6 +72,9 @@ Printing and clearing
 
 .. c:function:: void PyErr_WriteUnraisable(PyObject *obj)
 
+   Call :func:`sys.unraisablehook` using the current exception and *obj*
+   argument.
+
    This utility function prints a warning message to ``sys.stderr`` when an
    exception has been set but it is impossible for the interpreter to actually
    raise the exception.  It is used, for example, when an exception occurs in an
@@ -81,6 +84,8 @@ Printing and clearing
    in which the unraisable exception occurred. If possible,
    the repr of *obj* will be printed in the warning message.
 
+   An exception must be set when calling this function.
+
 
 Raising exceptions
 ==================
index 7d27c89fac9572ac4e58fe3998442ff05d2c692f..3b754bd4e27677e0b17d1bd9920a289dba8be389 100644 (file)
@@ -248,16 +248,19 @@ always available.
    before the program exits.  The handling of such top-level exceptions can be
    customized by assigning another three-argument function to ``sys.excepthook``.
 
+   See also :func:`unraisablehook` which handles unraisable exceptions.
+
 
 .. data:: __breakpointhook__
           __displayhook__
           __excepthook__
+          __unraisablehook__
 
    These objects contain the original values of ``breakpointhook``,
-   ``displayhook``, and ``excepthook`` at the start of the program.  They are
-   saved so that ``breakpointhook``, ``displayhook`` and ``excepthook`` can be
-   restored in case they happen to get replaced with broken or alternative
-   objects.
+   ``displayhook``, ``excepthook``, and ``unraisablehook`` at the start of the
+   program.  They are saved so that ``breakpointhook``, ``displayhook`` and
+   ``excepthook``, ``unraisablehook`` can be restored in case they happen to
+   get replaced with broken or alternative objects.
 
    .. versionadded:: 3.7
       __breakpointhook__
@@ -1487,6 +1490,28 @@ always available.
    is suppressed and only the exception type and value are printed.
 
 
+.. function:: unraisablehook(unraisable, /)
+
+   Handle an unraisable exception.
+
+   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 (:func:`gc.collect`).
+
+   The *unraisable* argument has the following attributes:
+
+   * *exc_type*: Exception type.
+   * *exc_value*: Exception value, can be ``None``.
+   * *exc_traceback*: Exception traceback, can be ``None``.
+   * *object*: Object causing the exception, can be ``None``.
+
+   :func:`sys.unraisablehook` can be overridden to control how unraisable
+   exceptions are handled.
+
+   See also :func:`excepthook` which handles uncaught exceptions.
+
+   .. versionadded:: 3.8
+
 .. data:: version
 
    A string containing the version number of the Python interpreter plus additional
index 5f8208d5bf4a5ea6b72e51113696b0731cf5ff7e..63b8200a1528f02000eaec5ecdcc3f915ac986f5 100644 (file)
@@ -481,6 +481,16 @@ and manipulating normal distributions of a random variable.
     [7.672102882379219, 12.000027119750287, 4.647488369766392]
 
 
+sys
+---
+
+Add new :func:`sys.unraisablehook` function which can be overridden to control
+how "unraisable exceptions" are handled. It is 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
+(:func:`gc.collect`).
+
+
 tarfile
 -------
 
index 4684ebea44a683f0424d0257a5a5af405e44e063..07cf815363b779d079d1e6059a81aaf87d3b897a 100644 (file)
@@ -48,6 +48,7 @@ extern int _PySys_InitMain(
     PyInterpreterState *interp);
 extern _PyInitError _PyImport_Init(PyInterpreterState *interp);
 extern _PyInitError _PyExc_Init(void);
+extern _PyInitError _PyErr_Init(void);
 extern _PyInitError _PyBuiltins_AddExceptions(PyObject * bltinmod);
 extern _PyInitError _PyImportHooks_Init(void);
 extern int _PyFloat_Init(void);
@@ -100,8 +101,11 @@ PyAPI_FUNC(_PyInitError) _Py_PreInitializeFromCoreConfig(
     const _PyCoreConfig *coreconfig,
     const _PyArgv *args);
 
+
 PyAPI_FUNC(int) _Py_HandleSystemExit(int *exitcode_p);
 
+PyAPI_FUNC(PyObject*) _PyErr_WriteUnraisableDefaultHook(PyObject *unraisable);
+
 #ifdef __cplusplus
 }
 #endif
index d1c7daad7bba48301457bd139686c909ecdb966a..2b358ca0466e6e70f7e7f5d9bc1efd9e4031aa46 100644 (file)
@@ -876,6 +876,81 @@ class SysModuleTest(unittest.TestCase):
         self.assertEqual(out, 'mbcs replace')
 
 
+@test.support.cpython_only
+class UnraisableHookTest(unittest.TestCase):
+    def write_unraisable_exc(self, exc, obj):
+        import _testcapi
+        import types
+        try:
+            # raise the exception to get a traceback in the except block
+            try:
+                raise exc
+            except Exception as exc2:
+                _testcapi.write_unraisable_exc(exc2, obj)
+                return types.SimpleNamespace(exc_type=type(exc2),
+                                             exc_value=exc2,
+                                             exc_traceback=exc2.__traceback__,
+                                             object=obj)
+        finally:
+            # Explicitly break any reference cycle
+            exc = None
+            exc2 = None
+
+    def test_original_unraisablehook(self):
+        obj = "an object"
+
+        with test.support.captured_output("stderr") as stderr:
+            with test.support.swap_attr(sys, 'unraisablehook',
+                                        sys.__unraisablehook__):
+                self.write_unraisable_exc(ValueError(42), obj)
+
+        err = stderr.getvalue()
+        self.assertIn(f'Exception ignored in: {obj!r}\n', err)
+        self.assertIn('Traceback (most recent call last):\n', err)
+        self.assertIn('ValueError: 42\n', err)
+
+    def test_original_unraisablehook_wrong_type(self):
+        exc = ValueError(42)
+        with test.support.swap_attr(sys, 'unraisablehook',
+                                    sys.__unraisablehook__):
+            with self.assertRaises(TypeError):
+                sys.unraisablehook(exc)
+
+    def test_custom_unraisablehook(self):
+        hook_args = None
+
+        def hook_func(args):
+            nonlocal hook_args
+            hook_args = args
+
+        obj = object()
+        try:
+            with test.support.swap_attr(sys, 'unraisablehook', hook_func):
+                expected = self.write_unraisable_exc(ValueError(42), obj)
+                for attr in "exc_type exc_value exc_traceback object".split():
+                    self.assertEqual(getattr(hook_args, attr),
+                                     getattr(expected, attr),
+                                     (hook_args, expected))
+        finally:
+            # expected and hook_args contain an exception: break reference cycle
+            expected = None
+            hook_args = None
+
+    def test_custom_unraisablehook_fail(self):
+        def hook_func(*args):
+            raise Exception("hook_func failed")
+
+        with test.support.captured_output("stderr") as stderr:
+            with test.support.swap_attr(sys, 'unraisablehook', hook_func):
+                self.write_unraisable_exc(ValueError(42), None)
+
+        err = stderr.getvalue()
+        self.assertIn(f'Exception ignored in: {hook_func!r}\n',
+                      err)
+        self.assertIn('Traceback (most recent call last):\n', err)
+        self.assertIn('Exception: hook_func failed\n', err)
+
+
 @test.support.cpython_only
 class SizeofTest(unittest.TestCase):
 
@@ -1277,8 +1352,5 @@ class SizeofTest(unittest.TestCase):
         self.assertIsNone(cur.finalizer)
 
 
-def test_main():
-    test.support.run_unittest(SysModuleTest, SizeofTest)
-
 if __name__ == "__main__":
-    test_main()
+    unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2019-05-08-12-51-37.bpo-36829.8enFMA.rst b/Misc/NEWS.d/next/Library/2019-05-08-12-51-37.bpo-36829.8enFMA.rst
new file mode 100644 (file)
index 0000000..0b04efc
--- /dev/null
@@ -0,0 +1,5 @@
+Add new :func:`sys.unraisablehook` function which can be overridden to
+control how "unraisable exceptions" are handled. It is 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
+(:func:`gc.collect`).
index 2af553960cc7da325b3acbec37bc1b1950484bc8..7945f498f9e2e730ff9144cf393c500d85fe2e60 100644 (file)
@@ -4982,6 +4982,20 @@ negative_refcount(PyObject *self, PyObject *Py_UNUSED(args))
 #endif
 
 
+static PyObject*
+test_write_unraisable_exc(PyObject *self, PyObject *args)
+{
+    PyObject *exc, *obj;
+    if (!PyArg_ParseTuple(args, "OO", &exc, &obj)) {
+        return NULL;
+    }
+
+    PyErr_SetObject((PyObject *)Py_TYPE(exc), exc);
+    PyErr_WriteUnraisable(obj);
+    Py_RETURN_NONE;
+}
+
+
 static PyMethodDef TestMethods[] = {
     {"raise_exception",         raise_exception,                 METH_VARARGS},
     {"raise_memoryerror",       raise_memoryerror,               METH_NOARGS},
@@ -5221,6 +5235,7 @@ static PyMethodDef TestMethods[] = {
 #ifdef Py_REF_DEBUG
     {"negative_refcount", negative_refcount, METH_NOARGS},
 #endif
+    {"write_unraisable_exc", test_write_unraisable_exc, METH_VARARGS},
     {NULL, NULL} /* sentinel */
 };
 
index c70b721983c1c415820e5440f698c38b8d98f12c..aede60ac85b519ed17d9472e5958af0b7d5cef98 100644 (file)
@@ -65,6 +65,22 @@ sys_exc_info(PyObject *module, PyObject *Py_UNUSED(ignored))
     return sys_exc_info_impl(module);
 }
 
+PyDoc_STRVAR(sys_unraisablehook__doc__,
+"unraisablehook($module, unraisable, /)\n"
+"--\n"
+"\n"
+"Handle an unraisable exception.\n"
+"\n"
+"The unraisable argument has the following attributes:\n"
+"\n"
+"* exc_type: Exception type.\n"
+"* exc_value: Exception value.\n"
+"* exc_tb: Exception traceback, can be None.\n"
+"* obj: Object causing the exception, can be None.");
+
+#define SYS_UNRAISABLEHOOK_METHODDEF    \
+    {"unraisablehook", (PyCFunction)sys_unraisablehook, METH_O, sys_unraisablehook__doc__},
+
 PyDoc_STRVAR(sys_exit__doc__,
 "exit($module, status=None, /)\n"
 "--\n"
@@ -1060,4 +1076,4 @@ sys_getandroidapilevel(PyObject *module, PyObject *Py_UNUSED(ignored))
 #ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
     #define SYS_GETANDROIDAPILEVEL_METHODDEF
 #endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
-/*[clinic end generated code: output=3ba4c194d00f1866 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=603e4d5a453dc769 input=a9049054013a1b77]*/
index b8af1df4161d57f6864df788276fd58fe99014b7..9622b5a1067df921c5945fc87661aaf117b464d9 100644 (file)
@@ -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 */
 }
 
index 694984af8060f88971631e9b206d31279ea05bec..5bd1d179e2f0c81f6b682b06ae723a6f6eeeca17 100644 (file)
@@ -1326,8 +1326,8 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     97,114,100,108,101,115,115,32,111,102,32,97,110,121,32,114,
     97,105,115,101,100,32,101,120,99,101,112,116,105,111,110,115,
     46,78,41,2,114,57,0,0,0,114,60,0,0,0,41,4,
-    114,30,0,0,0,90,8,101,120,99,95,116,121,112,101,90,
-    9,101,120,99,95,118,97,108,117,101,90,13,101,120,99,95,
+    114,30,0,0,0,218,8,101,120,99,95,116,121,112,101,218,
+    9,101,120,99,95,118,97,108,117,101,218,13,101,120,99,95,
     116,114,97,99,101,98,97,99,107,114,10,0,0,0,114,10,
     0,0,0,114,11,0,0,0,114,56,0,0,0,99,3,0,
     0,115,2,0,0,0,0,2,122,27,95,73,109,112,111,114,
@@ -1358,7 +1358,7 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     98,97,115,101,114,10,0,0,0,114,10,0,0,0,114,11,
     0,0,0,218,13,95,114,101,115,111,108,118,101,95,110,97,
     109,101,104,3,0,0,115,10,0,0,0,0,2,16,1,12,
-    1,8,1,8,1,114,185,0,0,0,99,3,0,0,0,0,
+    1,8,1,8,1,114,188,0,0,0,99,3,0,0,0,0,
     0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,67,
     0,0,0,115,34,0,0,0,124,0,160,0,124,1,124,2,
     161,2,125,3,124,3,100,0,107,8,114,24,100,0,83,0,
@@ -1368,7 +1368,7 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     109,0,0,0,114,10,0,0,0,114,10,0,0,0,114,11,
     0,0,0,218,17,95,102,105,110,100,95,115,112,101,99,95,
     108,101,103,97,99,121,113,3,0,0,115,8,0,0,0,0,
-    3,12,1,8,1,4,1,114,187,0,0,0,99,3,0,0,
+    3,12,1,8,1,4,1,114,190,0,0,0,99,3,0,0,
     0,0,0,0,0,0,0,0,0,10,0,0,0,10,0,0,
     0,67,0,0,0,115,12,1,0,0,116,0,106,1,125,3,
     124,3,100,1,107,8,114,22,116,2,100,2,131,1,130,1,
@@ -1398,17 +1398,17 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     104,114,79,0,0,0,218,9,95,119,97,114,110,105,110,103,
     115,218,4,119,97,114,110,218,13,73,109,112,111,114,116,87,
     97,114,110,105,110,103,114,92,0,0,0,114,178,0,0,0,
-    114,166,0,0,0,114,106,0,0,0,114,187,0,0,0,114,
+    114,166,0,0,0,114,106,0,0,0,114,190,0,0,0,114,
     105,0,0,0,41,10,114,17,0,0,0,114,164,0,0,0,
-    114,165,0,0,0,114,188,0,0,0,90,9,105,115,95,114,
-    101,108,111,97,100,114,186,0,0,0,114,166,0,0,0,114,
+    114,165,0,0,0,114,191,0,0,0,90,9,105,115,95,114,
+    101,108,111,97,100,114,189,0,0,0,114,166,0,0,0,114,
     95,0,0,0,114,96,0,0,0,114,105,0,0,0,114,10,
     0,0,0,114,10,0,0,0,114,11,0,0,0,218,10,95,
     102,105,110,100,95,115,112,101,99,122,3,0,0,115,54,0,
     0,0,0,2,6,1,8,2,8,3,4,1,12,5,10,1,
     8,1,8,1,2,1,10,1,14,1,12,1,8,1,20,2,
     22,1,8,2,18,1,10,1,2,1,10,1,14,4,14,2,
-    8,1,8,2,10,2,10,2,114,192,0,0,0,99,3,0,
+    8,1,8,2,10,2,10,2,114,195,0,0,0,99,3,0,
     0,0,0,0,0,0,0,0,0,0,3,0,0,0,5,0,
     0,0,67,0,0,0,115,108,0,0,0,116,0,124,0,116,
     1,131,2,115,28,116,2,100,1,160,3,116,4,124,0,131,
@@ -1432,12 +1432,12 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     121,32,109,111,100,117,108,101,32,110,97,109,101,78,41,7,
     218,10,105,115,105,110,115,116,97,110,99,101,218,3,115,116,
     114,218,9,84,121,112,101,69,114,114,111,114,114,45,0,0,
-    0,114,14,0,0,0,114,182,0,0,0,114,79,0,0,0,
-    169,3,114,17,0,0,0,114,183,0,0,0,114,184,0,0,
+    0,114,14,0,0,0,114,185,0,0,0,114,79,0,0,0,
+    169,3,114,17,0,0,0,114,186,0,0,0,114,187,0,0,
     0,114,10,0,0,0,114,10,0,0,0,114,11,0,0,0,
     218,13,95,115,97,110,105,116,121,95,99,104,101,99,107,169,
     3,0,0,115,22,0,0,0,0,2,10,1,18,1,8,1,
-    8,1,8,1,10,1,10,1,4,1,8,2,12,1,114,197,
+    8,1,8,1,10,1,10,1,4,1,8,2,12,1,114,200,
     0,0,0,122,16,78,111,32,109,111,100,117,108,101,32,110,
     97,109,101,100,32,122,4,123,33,114,125,99,2,0,0,0,
     0,0,0,0,0,0,0,0,8,0,0,0,8,0,0,0,
@@ -1462,7 +1462,7 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     0,114,92,0,0,0,114,67,0,0,0,114,141,0,0,0,
     114,106,0,0,0,218,8,95,69,82,82,95,77,83,71,114,
     45,0,0,0,218,19,77,111,100,117,108,101,78,111,116,70,
-    111,117,110,100,69,114,114,111,114,114,192,0,0,0,114,159,
+    111,117,110,100,69,114,114,111,114,114,195,0,0,0,114,159,
     0,0,0,114,5,0,0,0,41,8,114,17,0,0,0,218,
     7,105,109,112,111,114,116,95,114,164,0,0,0,114,130,0,
     0,0,90,13,112,97,114,101,110,116,95,109,111,100,117,108,
@@ -1472,7 +1472,7 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     117,110,108,111,99,107,101,100,188,3,0,0,115,42,0,0,
     0,0,1,4,1,14,1,4,1,10,1,10,2,10,1,10,
     1,10,1,2,1,10,1,14,1,16,1,20,1,10,1,8,
-    1,20,2,8,1,4,2,10,1,22,1,114,202,0,0,0,
+    1,20,2,8,1,4,2,10,1,22,1,114,205,0,0,0,
     99,2,0,0,0,0,0,0,0,0,0,0,0,4,0,0,
     0,10,0,0,0,67,0,0,0,115,106,0,0,0,116,0,
     124,0,131,1,143,50,1,0,116,1,106,2,160,3,124,0,
@@ -1488,14 +1488,14 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     101,32,105,110,32,115,121,115,46,109,111,100,117,108,101,115,
     114,16,0,0,0,41,9,114,50,0,0,0,114,15,0,0,
     0,114,92,0,0,0,114,34,0,0,0,218,14,95,78,69,
-    69,68,83,95,76,79,65,68,73,78,71,114,202,0,0,0,
-    114,45,0,0,0,114,200,0,0,0,114,65,0,0,0,41,
-    4,114,17,0,0,0,114,201,0,0,0,114,96,0,0,0,
+    69,68,83,95,76,79,65,68,73,78,71,114,205,0,0,0,
+    114,45,0,0,0,114,203,0,0,0,114,65,0,0,0,41,
+    4,114,17,0,0,0,114,204,0,0,0,114,96,0,0,0,
     114,75,0,0,0,114,10,0,0,0,114,10,0,0,0,114,
     11,0,0,0,218,14,95,102,105,110,100,95,97,110,100,95,
     108,111,97,100,218,3,0,0,115,22,0,0,0,0,2,10,
     1,14,1,8,1,32,2,8,1,4,1,2,255,4,2,12,
-    2,8,1,114,204,0,0,0,114,22,0,0,0,99,3,0,
+    2,8,1,114,207,0,0,0,114,22,0,0,0,99,3,0,
     0,0,0,0,0,0,0,0,0,0,3,0,0,0,4,0,
     0,0,67,0,0,0,115,42,0,0,0,116,0,124,0,124,
     1,124,2,131,3,1,0,124,2,100,1,107,4,114,32,116,
@@ -1520,11 +1520,11 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     103,32,95,95,112,97,99,107,97,103,101,95,95,32,105,102,
     10,32,32,32,32,116,104,101,32,108,111,97,100,101,114,32,
     100,105,100,32,110,111,116,46,10,10,32,32,32,32,114,22,
-    0,0,0,41,4,114,197,0,0,0,114,185,0,0,0,114,
-    204,0,0,0,218,11,95,103,99,100,95,105,109,112,111,114,
-    116,114,196,0,0,0,114,10,0,0,0,114,10,0,0,0,
-    114,11,0,0,0,114,205,0,0,0,234,3,0,0,115,8,
-    0,0,0,0,9,12,1,8,1,12,1,114,205,0,0,0,
+    0,0,0,41,4,114,200,0,0,0,114,188,0,0,0,114,
+    207,0,0,0,218,11,95,103,99,100,95,105,109,112,111,114,
+    116,114,199,0,0,0,114,10,0,0,0,114,10,0,0,0,
+    114,11,0,0,0,114,208,0,0,0,234,3,0,0,115,8,
+    0,0,0,0,9,12,1,8,1,12,1,114,208,0,0,0,
     169,1,218,9,114,101,99,117,114,115,105,118,101,99,3,0,
     0,0,0,0,0,0,1,0,0,0,8,0,0,0,11,0,
     0,0,67,0,0,0,115,226,0,0,0,124,1,68,0,93,
@@ -1561,21 +1561,21 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     109,32,108,105,115,116,39,39,122,8,73,116,101,109,32,105,
     110,32,122,18,32,109,117,115,116,32,98,101,32,115,116,114,
     44,32,110,111,116,32,250,1,42,218,7,95,95,97,108,108,
-    95,95,84,114,206,0,0,0,114,179,0,0,0,78,41,16,
-    114,193,0,0,0,114,194,0,0,0,114,1,0,0,0,114,
-    195,0,0,0,114,14,0,0,0,114,4,0,0,0,218,16,
+    95,95,84,114,209,0,0,0,114,182,0,0,0,78,41,16,
+    114,196,0,0,0,114,197,0,0,0,114,1,0,0,0,114,
+    198,0,0,0,114,14,0,0,0,114,4,0,0,0,218,16,
     95,104,97,110,100,108,101,95,102,114,111,109,108,105,115,116,
-    114,209,0,0,0,114,45,0,0,0,114,67,0,0,0,114,
-    200,0,0,0,114,17,0,0,0,114,15,0,0,0,114,92,
-    0,0,0,114,34,0,0,0,114,203,0,0,0,41,8,114,
-    96,0,0,0,218,8,102,114,111,109,108,105,115,116,114,201,
-    0,0,0,114,207,0,0,0,218,1,120,90,5,119,104,101,
+    114,212,0,0,0,114,45,0,0,0,114,67,0,0,0,114,
+    203,0,0,0,114,17,0,0,0,114,15,0,0,0,114,92,
+    0,0,0,114,34,0,0,0,114,206,0,0,0,41,8,114,
+    96,0,0,0,218,8,102,114,111,109,108,105,115,116,114,204,
+    0,0,0,114,210,0,0,0,218,1,120,90,5,119,104,101,
     114,101,90,9,102,114,111,109,95,110,97,109,101,90,3,101,
     120,99,114,10,0,0,0,114,10,0,0,0,114,11,0,0,
-    0,114,210,0,0,0,249,3,0,0,115,44,0,0,0,0,
+    0,114,213,0,0,0,249,3,0,0,115,44,0,0,0,0,
     10,8,1,10,1,4,1,12,2,4,1,28,2,8,1,14,
     1,10,1,2,255,8,2,10,1,14,1,2,1,14,1,16,
-    4,10,1,16,255,2,2,8,1,22,1,114,210,0,0,0,
+    4,10,1,16,255,2,2,8,1,22,1,114,213,0,0,0,
     99,1,0,0,0,0,0,0,0,0,0,0,0,3,0,0,
     0,6,0,0,0,67,0,0,0,115,146,0,0,0,124,0,
     160,0,100,1,161,1,125,1,124,0,160,0,100,2,161,1,
@@ -1610,14 +1610,14 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     95,95,32,97,110,100,32,95,95,112,97,116,104,95,95,114,
     1,0,0,0,114,141,0,0,0,114,128,0,0,0,114,22,
     0,0,0,41,6,114,34,0,0,0,114,130,0,0,0,114,
-    189,0,0,0,114,190,0,0,0,114,191,0,0,0,114,129,
-    0,0,0,41,3,218,7,103,108,111,98,97,108,115,114,183,
+    192,0,0,0,114,193,0,0,0,114,194,0,0,0,114,129,
+    0,0,0,41,3,218,7,103,108,111,98,97,108,115,114,186,
     0,0,0,114,95,0,0,0,114,10,0,0,0,114,10,0,
     0,0,114,11,0,0,0,218,17,95,99,97,108,99,95,95,
     95,112,97,99,107,97,103,101,95,95,30,4,0,0,115,38,
     0,0,0,0,7,10,1,10,1,8,1,18,1,22,2,2,
     0,2,254,6,3,4,1,8,1,6,2,6,2,2,0,2,
-    254,6,3,8,1,8,1,14,1,114,216,0,0,0,114,10,
+    254,6,3,8,1,8,1,14,1,114,219,0,0,0,114,10,
     0,0,0,99,5,0,0,0,0,0,0,0,0,0,0,0,
     9,0,0,0,5,0,0,0,67,0,0,0,115,180,0,0,
     0,124,4,100,1,107,2,114,18,116,0,124,0,131,1,125,
@@ -1662,18 +1662,18 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     96,32,119,111,117,108,100,32,104,97,118,101,32,97,32,39,
     108,101,118,101,108,39,32,111,102,32,50,41,46,10,10,32,
     32,32,32,114,22,0,0,0,78,114,128,0,0,0,114,141,
-    0,0,0,41,9,114,205,0,0,0,114,216,0,0,0,218,
-    9,112,97,114,116,105,116,105,111,110,114,181,0,0,0,114,
+    0,0,0,41,9,114,208,0,0,0,114,219,0,0,0,218,
+    9,112,97,114,116,105,116,105,111,110,114,184,0,0,0,114,
     15,0,0,0,114,92,0,0,0,114,1,0,0,0,114,4,
-    0,0,0,114,210,0,0,0,41,9,114,17,0,0,0,114,
-    215,0,0,0,218,6,108,111,99,97,108,115,114,211,0,0,
-    0,114,184,0,0,0,114,96,0,0,0,90,8,103,108,111,
-    98,97,108,115,95,114,183,0,0,0,90,7,99,117,116,95,
+    0,0,0,114,213,0,0,0,41,9,114,17,0,0,0,114,
+    218,0,0,0,218,6,108,111,99,97,108,115,114,214,0,0,
+    0,114,187,0,0,0,114,96,0,0,0,90,8,103,108,111,
+    98,97,108,115,95,114,186,0,0,0,90,7,99,117,116,95,
     111,102,102,114,10,0,0,0,114,10,0,0,0,114,11,0,
     0,0,218,10,95,95,105,109,112,111,114,116,95,95,57,4,
     0,0,115,30,0,0,0,0,11,8,1,10,2,16,1,8,
     1,12,1,4,3,8,1,18,1,4,1,4,4,26,3,32,
-    1,10,1,12,2,114,219,0,0,0,99,1,0,0,0,0,
+    1,10,1,12,2,114,222,0,0,0,99,1,0,0,0,0,
     0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,67,
     0,0,0,115,38,0,0,0,116,0,160,1,124,0,161,1,
     125,1,124,1,100,0,107,8,114,30,116,2,100,1,124,0,
@@ -1685,7 +1685,7 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     0,0,0,114,10,0,0,0,114,11,0,0,0,218,18,95,
     98,117,105,108,116,105,110,95,102,114,111,109,95,110,97,109,
     101,94,4,0,0,115,8,0,0,0,0,1,10,1,8,1,
-    12,1,114,220,0,0,0,99,2,0,0,0,0,0,0,0,
+    12,1,114,223,0,0,0,99,2,0,0,0,0,0,0,0,
     0,0,0,0,10,0,0,0,5,0,0,0,67,0,0,0,
     115,166,0,0,0,124,1,97,0,124,0,97,1,116,2,116,
     1,131,1,125,2,116,1,106,3,160,4,161,0,68,0,93,
@@ -1714,12 +1714,12 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     100,117,108,101,115,32,109,117,115,116,32,98,101,32,101,120,
     112,108,105,99,105,116,108,121,32,112,97,115,115,101,100,32,
     105,110,46,10,10,32,32,32,32,41,3,114,23,0,0,0,
-    114,189,0,0,0,114,64,0,0,0,78,41,15,114,57,0,
+    114,192,0,0,0,114,64,0,0,0,78,41,15,114,57,0,
     0,0,114,15,0,0,0,114,14,0,0,0,114,92,0,0,
-    0,218,5,105,116,101,109,115,114,193,0,0,0,114,78,0,
+    0,218,5,105,116,101,109,115,114,196,0,0,0,114,78,0,
     0,0,114,160,0,0,0,114,88,0,0,0,114,173,0,0,
     0,114,142,0,0,0,114,148,0,0,0,114,1,0,0,0,
-    114,220,0,0,0,114,5,0,0,0,41,10,218,10,115,121,
+    114,223,0,0,0,114,5,0,0,0,41,10,218,10,115,121,
     115,95,109,111,100,117,108,101,218,11,95,105,109,112,95,109,
     111,100,117,108,101,90,11,109,111,100,117,108,101,95,116,121,
     112,101,114,17,0,0,0,114,96,0,0,0,114,109,0,0,
@@ -1730,7 +1730,7 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     218,6,95,115,101,116,117,112,101,4,0,0,115,36,0,0,
     0,0,9,4,1,4,3,8,1,18,1,10,1,10,1,6,
     1,10,1,6,2,2,1,10,1,12,3,10,1,8,1,10,
-    1,10,2,10,1,114,224,0,0,0,99,2,0,0,0,0,
+    1,10,2,10,1,114,227,0,0,0,99,2,0,0,0,0,
     0,0,0,0,0,0,0,2,0,0,0,3,0,0,0,67,
     0,0,0,115,38,0,0,0,116,0,124,0,124,1,131,2,
     1,0,116,1,106,2,160,3,116,4,161,1,1,0,116,1,
@@ -1738,12 +1738,12 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     122,48,73,110,115,116,97,108,108,32,105,109,112,111,114,116,
     101,114,115,32,102,111,114,32,98,117,105,108,116,105,110,32,
     97,110,100,32,102,114,111,122,101,110,32,109,111,100,117,108,
-    101,115,78,41,6,114,224,0,0,0,114,15,0,0,0,114,
-    188,0,0,0,114,120,0,0,0,114,160,0,0,0,114,173,
-    0,0,0,41,2,114,222,0,0,0,114,223,0,0,0,114,
+    101,115,78,41,6,114,227,0,0,0,114,15,0,0,0,114,
+    191,0,0,0,114,120,0,0,0,114,160,0,0,0,114,173,
+    0,0,0,41,2,114,225,0,0,0,114,226,0,0,0,114,
     10,0,0,0,114,10,0,0,0,114,11,0,0,0,218,8,
     95,105,110,115,116,97,108,108,136,4,0,0,115,6,0,0,
-    0,0,2,10,2,12,1,114,225,0,0,0,99,0,0,0,
+    0,0,2,10,2,12,1,114,228,0,0,0,99,0,0,0,
     0,0,0,0,0,0,0,0,0,1,0,0,0,4,0,0,
     0,67,0,0,0,115,32,0,0,0,100,1,100,2,108,0,
     125,0,124,0,97,1,124,0,160,2,116,3,106,4,116,5,
@@ -1754,12 +1754,12 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     32,97,99,99,101,115,115,114,22,0,0,0,78,41,6,218,
     26,95,102,114,111,122,101,110,95,105,109,112,111,114,116,108,
     105,98,95,101,120,116,101,114,110,97,108,114,126,0,0,0,
-    114,225,0,0,0,114,15,0,0,0,114,92,0,0,0,114,
-    1,0,0,0,41,1,114,226,0,0,0,114,10,0,0,0,
+    114,228,0,0,0,114,15,0,0,0,114,92,0,0,0,114,
+    1,0,0,0,41,1,114,229,0,0,0,114,10,0,0,0,
     114,10,0,0,0,114,11,0,0,0,218,27,95,105,110,115,
     116,97,108,108,95,101,120,116,101,114,110,97,108,95,105,109,
     112,111,114,116,101,114,115,144,4,0,0,115,6,0,0,0,
-    0,3,8,1,4,1,114,227,0,0,0,41,2,78,78,41,
+    0,3,8,1,4,1,114,230,0,0,0,41,2,78,78,41,
     1,78,41,2,78,114,22,0,0,0,41,4,78,78,114,10,
     0,0,0,114,22,0,0,0,41,50,114,3,0,0,0,114,
     126,0,0,0,114,12,0,0,0,114,18,0,0,0,114,59,
@@ -1771,13 +1771,13 @@ const unsigned char _Py_M__importlib_bootstrap[] = {
     0,0,0,114,142,0,0,0,114,148,0,0,0,114,152,0,
     0,0,114,107,0,0,0,114,93,0,0,0,114,158,0,0,
     0,114,159,0,0,0,114,94,0,0,0,114,160,0,0,0,
-    114,173,0,0,0,114,178,0,0,0,114,185,0,0,0,114,
-    187,0,0,0,114,192,0,0,0,114,197,0,0,0,90,15,
+    114,173,0,0,0,114,178,0,0,0,114,188,0,0,0,114,
+    190,0,0,0,114,195,0,0,0,114,200,0,0,0,90,15,
     95,69,82,82,95,77,83,71,95,80,82,69,70,73,88,114,
-    199,0,0,0,114,202,0,0,0,218,6,111,98,106,101,99,
-    116,114,203,0,0,0,114,204,0,0,0,114,205,0,0,0,
-    114,210,0,0,0,114,216,0,0,0,114,219,0,0,0,114,
-    220,0,0,0,114,224,0,0,0,114,225,0,0,0,114,227,
+    202,0,0,0,114,205,0,0,0,218,6,111,98,106,101,99,
+    116,114,206,0,0,0,114,207,0,0,0,114,208,0,0,0,
+    114,213,0,0,0,114,219,0,0,0,114,222,0,0,0,114,
+    223,0,0,0,114,227,0,0,0,114,228,0,0,0,114,230,
     0,0,0,114,10,0,0,0,114,10,0,0,0,114,10,0,
     0,0,114,11,0,0,0,218,8,60,109,111,100,117,108,101,
     62,1,0,0,0,115,94,0,0,0,4,24,4,2,8,8,
index 01344db410a0f1caf54673c8dbc90b65a4171c84..6dc684bfcebb84ce67516504ebe110f2a7ffd1be 100644 (file)
@@ -587,6 +587,12 @@ pycore_init_types(void)
     if (!_PyContext_Init()) {
         return _Py_INIT_ERR("can't init context");
     }
+
+    err = _PyErr_Init();
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
     return _Py_INIT_OK();
 }
 
@@ -1462,6 +1468,12 @@ new_interpreter(PyThreadState **tstate_p)
         return err;
     }
 
+    err = _PyErr_Init();
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
+
     /* XXX The following is lax in error checking */
     PyObject *modules = PyDict_New();
     if (modules == NULL) {
index 1290164edbf68d513cd87fa926830f16e4437fb1..1735b90b33b95583ec7b5ae1c928590fa0a4b57e 100644 (file)
@@ -374,6 +374,30 @@ sys_exc_info_impl(PyObject *module)
 }
 
 
+/*[clinic input]
+sys.unraisablehook
+
+    unraisable: object
+    /
+
+Handle an unraisable exception.
+
+The unraisable argument has the following attributes:
+
+* exc_type: Exception type.
+* exc_value: Exception value.
+* exc_tb: Exception traceback, can be None.
+* obj: Object causing the exception, can be None.
+[clinic start generated code]*/
+
+static PyObject *
+sys_unraisablehook(PyObject *module, PyObject *unraisable)
+/*[clinic end generated code: output=bb92838b32abaa14 input=fdbdb47fdd0bee06]*/
+{
+    return _PyErr_WriteUnraisableDefaultHook(unraisable);
+}
+
+
 /*[clinic input]
 sys.exit
 
@@ -1672,6 +1696,7 @@ static PyMethodDef sys_methods[] = {
      METH_VARARGS | METH_KEYWORDS, set_asyncgen_hooks_doc},
     SYS_GET_ASYNCGEN_HOOKS_METHODDEF
     SYS_GETANDROIDAPILEVEL_METHODDEF
+    SYS_UNRAISABLEHOOK_METHODDEF
     {NULL,              NULL}           /* sentinel */
 };
 
@@ -2369,6 +2394,9 @@ _PySys_InitCore(_PyRuntimeState *runtime, PyInterpreterState *interp,
     SET_SYS_FROM_STRING_BORROW(
         "__breakpointhook__",
         PyDict_GetItemString(sysdict, "breakpointhook"));
+    SET_SYS_FROM_STRING_BORROW("__unraisablehook__",
+                               PyDict_GetItemString(sysdict, "unraisablehook"));
+
     SET_SYS_FROM_STRING("version",
                          PyUnicode_FromString(Py_GetVersion()));
     SET_SYS_FROM_STRING("hexversion",