From b1170056ae453e1fd24113a2bdd29554a990c195 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 9 Jan 2026 13:16:22 +0100 Subject: [PATCH] gh-143547: Fix PyErr_FormatUnraisable() fallback (#143557) Hold a strong reference to 'hook' while calling the default unraisable took to log hook failure. (cherry picked from commit 39a2bcf949095bd603f7b73f15b5b478dbb49ba9) --- Lib/test/test_sys.py | 1 + .../Library/2026-01-08-14-53-46.gh-issue-143547.wHBVlr.rst | 3 +++ Python/errors.c | 6 ++---- 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-01-08-14-53-46.gh-issue-143547.wHBVlr.rst diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index e2117b9588dba2..4aeeac56b1aae3 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1480,6 +1480,7 @@ def hook_func(args): def test_custom_unraisablehook_fail(self): _testcapi = import_helper.import_module('_testcapi') from _testcapi import err_writeunraisable + def hook_func(*args): raise Exception("hook_func failed") diff --git a/Misc/NEWS.d/next/Library/2026-01-08-14-53-46.gh-issue-143547.wHBVlr.rst b/Misc/NEWS.d/next/Library/2026-01-08-14-53-46.gh-issue-143547.wHBVlr.rst new file mode 100644 index 00000000000000..934570b30b971f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-08-14-53-46.gh-issue-143547.wHBVlr.rst @@ -0,0 +1,3 @@ +Fix :func:`sys.unraisablehook` when the hook raises an exception and changes +:func:`sys.unraisablehook`: hold a strong reference to the old hook. Patch +by Victor Stinner. diff --git a/Python/errors.c b/Python/errors.c index 89ea79f8c914fb..3fd97ea36b3368 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1632,6 +1632,7 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) _Py_EnsureTstateNotNULL(tstate); PyObject *err_msg = NULL; + PyObject *hook = NULL; PyObject *exc_type, *exc_value, *exc_tb; _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); @@ -1676,7 +1677,6 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) goto error; } - PyObject *hook; if (_PySys_GetOptionalAttr(&_Py_ID(unraisablehook), &hook) < 0) { Py_DECREF(hook_args); err_msg_str = NULL; @@ -1689,7 +1689,6 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) } if (_PySys_Audit(tstate, "sys.unraisablehook", "OO", hook, hook_args) < 0) { - Py_DECREF(hook); Py_DECREF(hook_args); err_msg_str = "Exception ignored in audit hook"; obj = NULL; @@ -1697,13 +1696,11 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) } if (hook == Py_None) { - Py_DECREF(hook); Py_DECREF(hook_args); goto default_hook; } PyObject *res = PyObject_CallOneArg(hook, hook_args); - Py_DECREF(hook); Py_DECREF(hook_args); if (res != NULL) { Py_DECREF(res); @@ -1733,6 +1730,7 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj) Py_XDECREF(exc_value); Py_XDECREF(exc_tb); Py_XDECREF(err_msg); + Py_XDECREF(hook); _PyErr_Clear(tstate); /* Just in case */ }