From 39a2bcf949095bd603f7b73f15b5b478dbb49ba9 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. Fix test_sys.UnraisableHookTest: use the right decorator function to disable colors. Previously, tests were always skipped. --- Lib/test/test_sys.py | 3 ++- .../Library/2026-01-08-14-53-46.gh-issue-143547.wHBVlr.rst | 3 +++ Python/errors.c | 6 ++---- 3 files changed, 7 insertions(+), 5 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 04018e9603ff..1d8e908efb05 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1350,7 +1350,7 @@ class SysModuleTest(unittest.TestCase): @test.support.cpython_only -@force_not_colorized +@test.support.force_not_colorized_test_class class UnraisableHookTest(unittest.TestCase): def test_original_unraisablehook(self): _testcapi = import_helper.import_module('_testcapi') @@ -1492,6 +1492,7 @@ class UnraisableHookTest(unittest.TestCase): 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 000000000000..934570b30b97 --- /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 5c6ac48371a0..229e3a565db5 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -1656,6 +1656,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); @@ -1700,7 +1701,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; @@ -1713,7 +1713,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; @@ -1721,13 +1720,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); @@ -1757,6 +1754,7 @@ done: Py_XDECREF(exc_value); Py_XDECREF(exc_tb); Py_XDECREF(err_msg); + Py_XDECREF(hook); _PyErr_Clear(tstate); /* Just in case */ } -- 2.47.3