]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-108082: Remove _PyErr_WriteUnraisableMsg() (GH-111643)
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 3 Nov 2023 07:45:53 +0000 (09:45 +0200)
committerGitHub <noreply@github.com>
Fri, 3 Nov 2023 07:45:53 +0000 (09:45 +0200)
Replace the remaining calls with PyErr_FormatUnraisable().

13 files changed:
Include/internal/pycore_pyerrors.h
Lib/test/_test_atexit.py
Lib/test/audit-tests.py
Lib/test/test_ctypes/test_callbacks.py
Lib/test/test_ctypes/test_random_things.py
Lib/test/test_sys.py
Lib/test/test_thread.py
Modules/_ctypes/callbacks.c
Modules/_testinternalcapi.c
Modules/_threadmodule.c
Modules/atexitmodule.c
Modules/clinic/_testinternalcapi.c.h
Python/errors.c

index 67ef71c261654135e1b093d852764ffd6ad76b96..a953d2bb18d4ad6625f4a0107094969d9e08058b 100644 (file)
@@ -194,11 +194,6 @@ Py_DEPRECATED(3.12) extern void _PyErr_ChainExceptions(PyObject *, PyObject *, P
 // Export for '_zoneinfo' shared extension
 PyAPI_FUNC(void) _PyErr_ChainExceptions1(PyObject *);
 
-// Export for '_lsprof' shared extension
-PyAPI_FUNC(void) _PyErr_WriteUnraisableMsg(
-    const char *err_msg,
-    PyObject *obj);
-
 #ifdef __cplusplus
 }
 #endif
index 55d28083349175bc7e8a0b718bfa08813fb7f5e2..f618c1fcbca52b1edb30d8884123f355c013ce93 100644 (file)
@@ -19,7 +19,9 @@ class GeneralTest(unittest.TestCase):
             atexit.register(func, *args)
             atexit._run_exitfuncs()
 
-            self.assertEqual(cm.unraisable.object, func)
+            self.assertIsNone(cm.unraisable.object)
+            self.assertEqual(cm.unraisable.err_msg,
+                    f'Exception ignored in atexit callback {func!r}')
             self.assertEqual(cm.unraisable.exc_type, exc_type)
             self.assertEqual(type(cm.unraisable.exc_value), exc_type)
 
@@ -125,7 +127,9 @@ class GeneralTest(unittest.TestCase):
         try:
             with support.catch_unraisable_exception() as cm:
                 atexit._run_exitfuncs()
-                self.assertEqual(cm.unraisable.object, func)
+                self.assertIsNone(cm.unraisable.object)
+                self.assertEqual(cm.unraisable.err_msg,
+                        f'Exception ignored in atexit callback {func!r}')
                 self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError)
                 self.assertEqual(type(cm.unraisable.exc_value), ZeroDivisionError)
         finally:
index f0cedde308d53b83b5577bb6dd56dc33c17a5be9..89f407de4b0d9c52692fbc355c1816539a35559e 100644 (file)
@@ -289,7 +289,7 @@ def test_excepthook():
 
 
 def test_unraisablehook():
-    from _testinternalcapi import write_unraisable_exc
+    from _testcapi import err_formatunraisable
 
     def unraisablehook(hookargs):
         pass
@@ -302,7 +302,8 @@ def test_unraisablehook():
 
     sys.addaudithook(hook)
     sys.unraisablehook = unraisablehook
-    write_unraisable_exc(RuntimeError("nonfatal-error"), "for audit hook test", None)
+    err_formatunraisable(RuntimeError("nonfatal-error"),
+                         "Exception ignored for audit hook test")
 
 
 def test_winreg():
index 6fe3e11967240937caec15bc857c13bf756c04e7..19f4158c0ac8461514a9269643cfb4f987b43ff8 100644 (file)
@@ -322,9 +322,9 @@ class SampleCallbacksTestCase(unittest.TestCase):
 
             self.assertIsInstance(cm.unraisable.exc_value, TypeError)
             self.assertEqual(cm.unraisable.err_msg,
-                             "Exception ignored on converting result "
-                             "of ctypes callback function")
-            self.assertIs(cm.unraisable.object, func)
+                             f"Exception ignored on converting result "
+                             f"of ctypes callback function {func!r}")
+            self.assertIsNone(cm.unraisable.object)
 
 
 if __name__ == '__main__':
index 65eb53f86475d32b87732cf5753ac92aff0298c2..630f6ed9489ebabad094ede736c19c3a4843a6e1 100644 (file)
@@ -51,9 +51,9 @@ class CallbackTracbackTestCase(unittest.TestCase):
             if exc_msg is not None:
                 self.assertEqual(str(cm.unraisable.exc_value), exc_msg)
             self.assertEqual(cm.unraisable.err_msg,
-                             "Exception ignored on calling ctypes "
-                             "callback function")
-            self.assertIs(cm.unraisable.object, callback_func)
+                             f"Exception ignored on calling ctypes "
+                             f"callback function {callback_func!r}")
+            self.assertIsNone(cm.unraisable.object)
 
     def test_ValueError(self):
         cb = CFUNCTYPE(c_int, c_int)(callback_func)
index b16e1e7f70a24ac6dafdcbb10740bcff513546dc..b111962abdcdc32c76cbb20c4d78dadfc20eb2bf 100644 (file)
@@ -1215,38 +1215,38 @@ class SysModuleTest(unittest.TestCase):
 
 @test.support.cpython_only
 class UnraisableHookTest(unittest.TestCase):
-    def write_unraisable_exc(self, exc, err_msg, obj):
-        import _testinternalcapi
-        import types
-        err_msg2 = f"Exception ignored {err_msg}"
-        try:
-            _testinternalcapi.write_unraisable_exc(exc, err_msg, obj)
-            return types.SimpleNamespace(exc_type=type(exc),
-                                         exc_value=exc,
-                                         exc_traceback=exc.__traceback__,
-                                         err_msg=err_msg2,
-                                         object=obj)
-        finally:
-            # Explicitly break any reference cycle
-            exc = None
-
     def test_original_unraisablehook(self):
-        for err_msg in (None, "original hook"):
-            with self.subTest(err_msg=err_msg):
-                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), err_msg, obj)
-
-                err = stderr.getvalue()
-                if err_msg is not None:
-                    self.assertIn(f'Exception ignored {err_msg}: {obj!r}\n', err)
-                else:
-                    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)
+        _testcapi = import_helper.import_module('_testcapi')
+        from _testcapi import err_writeunraisable, err_formatunraisable
+        obj = hex
+
+        with support.swap_attr(sys, 'unraisablehook',
+                                    sys.__unraisablehook__):
+            with support.captured_stderr() as stderr:
+                err_writeunraisable(ValueError(42), obj)
+            lines = stderr.getvalue().splitlines()
+            self.assertEqual(lines[0], f'Exception ignored in: {obj!r}')
+            self.assertEqual(lines[1], 'Traceback (most recent call last):')
+            self.assertEqual(lines[-1], 'ValueError: 42')
+
+            with support.captured_stderr() as stderr:
+                err_writeunraisable(ValueError(42), None)
+            lines = stderr.getvalue().splitlines()
+            self.assertEqual(lines[0], 'Traceback (most recent call last):')
+            self.assertEqual(lines[-1], 'ValueError: 42')
+
+            with support.captured_stderr() as stderr:
+                err_formatunraisable(ValueError(42), 'Error in %R', obj)
+            lines = stderr.getvalue().splitlines()
+            self.assertEqual(lines[0], f'Error in {obj!r}:')
+            self.assertEqual(lines[1], 'Traceback (most recent call last):')
+            self.assertEqual(lines[-1], 'ValueError: 42')
+
+            with support.captured_stderr() as stderr:
+                err_formatunraisable(ValueError(42), None)
+            lines = stderr.getvalue().splitlines()
+            self.assertEqual(lines[0], 'Traceback (most recent call last):')
+            self.assertEqual(lines[-1], 'ValueError: 42')
 
     def test_original_unraisablehook_err(self):
         # bpo-22836: PyErr_WriteUnraisable() should give sensible reports
@@ -1293,6 +1293,8 @@ class UnraisableHookTest(unittest.TestCase):
         # Check that the exception is printed with its qualified name
         # rather than just classname, and the module names appears
         # unless it is one of the hard-coded exclusions.
+        _testcapi = import_helper.import_module('_testcapi')
+        from _testcapi import err_writeunraisable
         class A:
             class B:
                 class X(Exception):
@@ -1304,9 +1306,7 @@ class UnraisableHookTest(unittest.TestCase):
                 with test.support.captured_stderr() as stderr, test.support.swap_attr(
                     sys, 'unraisablehook', sys.__unraisablehook__
                 ):
-                    expected = self.write_unraisable_exc(
-                        A.B.X(), "msg", "obj"
-                    )
+                    err_writeunraisable(A.B.X(), "obj")
                 report = stderr.getvalue()
                 self.assertIn(A.B.X.__qualname__, report)
                 if moduleName in ['builtins', '__main__']:
@@ -1322,34 +1322,45 @@ class UnraisableHookTest(unittest.TestCase):
                 sys.unraisablehook(exc)
 
     def test_custom_unraisablehook(self):
+        _testcapi = import_helper.import_module('_testcapi')
+        from _testcapi import err_writeunraisable, err_formatunraisable
         hook_args = None
 
         def hook_func(args):
             nonlocal hook_args
             hook_args = args
 
-        obj = object()
+        obj = hex
         try:
             with test.support.swap_attr(sys, 'unraisablehook', hook_func):
-                expected = self.write_unraisable_exc(ValueError(42),
-                                                     "custom hook", obj)
-                for attr in "exc_type exc_value exc_traceback err_msg object".split():
-                    self.assertEqual(getattr(hook_args, attr),
-                                     getattr(expected, attr),
-                                     (hook_args, expected))
+                exc = ValueError(42)
+                err_writeunraisable(exc, obj)
+                self.assertIs(hook_args.exc_type, type(exc))
+                self.assertIs(hook_args.exc_value, exc)
+                self.assertIs(hook_args.exc_traceback, exc.__traceback__)
+                self.assertIsNone(hook_args.err_msg)
+                self.assertEqual(hook_args.object, obj)
+
+                err_formatunraisable(exc, "custom hook %R", obj)
+                self.assertIs(hook_args.exc_type, type(exc))
+                self.assertIs(hook_args.exc_value, exc)
+                self.assertIs(hook_args.exc_traceback, exc.__traceback__)
+                self.assertEqual(hook_args.err_msg, f'custom hook {obj!r}')
+                self.assertIsNone(hook_args.object)
         finally:
             # expected and hook_args contain an exception: break reference cycle
             expected = None
             hook_args = None
 
     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")
 
         with test.support.captured_output("stderr") as stderr:
             with test.support.swap_attr(sys, 'unraisablehook', hook_func):
-                self.write_unraisable_exc(ValueError(42),
-                                          "custom hook fail", None)
+                err_writeunraisable(ValueError(42), "custom hook fail")
 
         err = stderr.getvalue()
         self.assertIn(f'Exception ignored in sys.unraisablehook: '
index 8656fbdd83e9c706b12f81a6cc810bdf4a761c4b..831aaf5b6a794f75f88307687e720963f00a57fe 100644 (file)
@@ -155,9 +155,9 @@ class ThreadRunningTests(BasicThreadTest):
                 started.acquire()
 
             self.assertEqual(str(cm.unraisable.exc_value), "task failed")
-            self.assertIs(cm.unraisable.object, task)
+            self.assertIsNone(cm.unraisable.object)
             self.assertEqual(cm.unraisable.err_msg,
-                             "Exception ignored in thread started by")
+                             f"Exception ignored in thread started by {task!r}")
             self.assertIsNotNone(cm.unraisable.exc_traceback)
 
 
index 1bd8fec97179e99a9aebb5d6aef52a32d5132e0c..154e9f43983cdb0084569484b54d14453103aba0 100644 (file)
@@ -9,7 +9,6 @@
 #endif
 
 #include "pycore_call.h"          // _PyObject_CallNoArgs()
-#include "pycore_pyerrors.h"      // _PyErr_WriteUnraisableMsg()
 #include "pycore_runtime.h"       // _Py_ID()
 
 #include <stdbool.h>
@@ -216,8 +215,9 @@ static void _CallPythonObject(void *mem,
 
     result = PyObject_Vectorcall(callable, args, nargs, NULL);
     if (result == NULL) {
-        _PyErr_WriteUnraisableMsg("on calling ctypes callback function",
-                                  callable);
+        PyErr_FormatUnraisable(
+                "Exception ignored on calling ctypes callback function %R",
+                callable);
     }
 
 #ifdef MS_WIN32
@@ -258,9 +258,10 @@ static void _CallPythonObject(void *mem,
 
         if (keep == NULL) {
             /* Could not convert callback result. */
-            _PyErr_WriteUnraisableMsg("on converting result "
-                                      "of ctypes callback function",
-                                      callable);
+            PyErr_FormatUnraisable(
+                    "Exception ignored on converting result "
+                    "of ctypes callback function %R",
+                    callable);
         }
         else if (setfunc != _ctypes_get_fielddesc("O")->setfunc) {
             if (keep == Py_None) {
@@ -270,9 +271,10 @@ static void _CallPythonObject(void *mem,
             else if (PyErr_WarnEx(PyExc_RuntimeWarning,
                                   "memory leak in callback function.",
                                   1) == -1) {
-                _PyErr_WriteUnraisableMsg("on converting result "
-                                          "of ctypes callback function",
-                                          callable);
+                PyErr_FormatUnraisable(
+                        "Exception ignored on converting result "
+                        "of ctypes callback function %R",
+                        callable);
             }
         }
     }
index a71e7e1dcc1256525cde53cf56a69374784642f5..7dba29a54d79b7b35d8ae903ca78e5112d681703 100644 (file)
@@ -1518,37 +1518,6 @@ restore_crossinterp_data(PyObject *self, PyObject *args)
 }
 
 
-/*[clinic input]
-_testinternalcapi.write_unraisable_exc
-    exception as exc: object
-    err_msg: object
-    obj: object
-    /
-[clinic start generated code]*/
-
-static PyObject *
-_testinternalcapi_write_unraisable_exc_impl(PyObject *module, PyObject *exc,
-                                            PyObject *err_msg, PyObject *obj)
-/*[clinic end generated code: output=a0f063cdd04aad83 input=274381b1a3fa5cd6]*/
-{
-
-    const char *err_msg_utf8;
-    if (err_msg != Py_None) {
-        err_msg_utf8 = PyUnicode_AsUTF8(err_msg);
-        if (err_msg_utf8 == NULL) {
-            return NULL;
-        }
-    }
-    else {
-        err_msg_utf8 = NULL;
-    }
-
-    PyErr_SetObject((PyObject *)Py_TYPE(exc), exc);
-    _PyErr_WriteUnraisableMsg(err_msg_utf8, obj);
-    Py_RETURN_NONE;
-}
-
-
 static PyObject *
 raiseTestError(const char* test_name, const char* msg)
 {
@@ -1699,7 +1668,6 @@ static PyMethodDef module_functions[] = {
     {"perf_trampoline_set_persist_after_fork", perf_trampoline_set_persist_after_fork, METH_VARARGS},
     {"get_crossinterp_data",    get_crossinterp_data,            METH_VARARGS},
     {"restore_crossinterp_data", restore_crossinterp_data,       METH_VARARGS},
-    _TESTINTERNALCAPI_WRITE_UNRAISABLE_EXC_METHODDEF
     _TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF
     {NULL, NULL} /* sentinel */
 };
index a849a200df625c43af95b32e865cad0209fb9707..9eecebddb723a42447d9e72e88b84ae7d0f358a4 100644 (file)
@@ -5,7 +5,6 @@
 #include "Python.h"
 #include "pycore_interp.h"        // _PyInterpreterState.threads.count
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
-#include "pycore_pyerrors.h"      // _PyErr_WriteUnraisableMsg()
 #include "pycore_pylifecycle.h"
 #include "pycore_pystate.h"       // _PyThreadState_SetCurrent()
 #include "pycore_sysmodule.h"     // _PySys_GetAttr()
@@ -1071,7 +1070,8 @@ thread_run(void *boot_raw)
             /* SystemExit is ignored silently */
             PyErr_Clear();
         else {
-            _PyErr_WriteUnraisableMsg("in thread started by", boot->func);
+            PyErr_FormatUnraisable(
+                "Exception ignored in thread started by %R", boot->func);
         }
     }
     else {
index 57e2ea67450453c0c8500a6d8378a3fd8d1bc584..b6f1bcbca6791655e135c0034894dd6c66c8861a 100644 (file)
@@ -10,7 +10,6 @@
 #include "pycore_atexit.h"        // export _Py_AtExit()
 #include "pycore_initconfig.h"    // _PyStatus_NO_MEMORY
 #include "pycore_interp.h"        // PyInterpreterState.atexit
-#include "pycore_pyerrors.h"      // _PyErr_WriteUnraisableMsg()
 #include "pycore_pystate.h"       // _PyInterpreterState_GET
 
 /* ===================================================================== */
@@ -137,7 +136,8 @@ atexit_callfuncs(struct atexit_state *state)
         PyObject* the_func = Py_NewRef(cb->func);
         PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs);
         if (res == NULL) {
-            _PyErr_WriteUnraisableMsg("in atexit callback", the_func);
+            PyErr_FormatUnraisable(
+                "Exception ignored in atexit callback %R", the_func);
         }
         else {
             Py_DECREF(res);
index 10374e0211ff03692def4d705fd9e6ac8a446d4e..cba2a943d03456a268f8f43088188a38d6797f2b 100644 (file)
@@ -266,38 +266,6 @@ exit:
     return return_value;
 }
 
-PyDoc_STRVAR(_testinternalcapi_write_unraisable_exc__doc__,
-"write_unraisable_exc($module, exception, err_msg, obj, /)\n"
-"--\n"
-"\n");
-
-#define _TESTINTERNALCAPI_WRITE_UNRAISABLE_EXC_METHODDEF    \
-    {"write_unraisable_exc", _PyCFunction_CAST(_testinternalcapi_write_unraisable_exc), METH_FASTCALL, _testinternalcapi_write_unraisable_exc__doc__},
-
-static PyObject *
-_testinternalcapi_write_unraisable_exc_impl(PyObject *module, PyObject *exc,
-                                            PyObject *err_msg, PyObject *obj);
-
-static PyObject *
-_testinternalcapi_write_unraisable_exc(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
-{
-    PyObject *return_value = NULL;
-    PyObject *exc;
-    PyObject *err_msg;
-    PyObject *obj;
-
-    if (!_PyArg_CheckPositional("write_unraisable_exc", nargs, 3, 3)) {
-        goto exit;
-    }
-    exc = args[0];
-    err_msg = args[1];
-    obj = args[2];
-    return_value = _testinternalcapi_write_unraisable_exc_impl(module, exc, err_msg, obj);
-
-exit:
-    return return_value;
-}
-
 PyDoc_STRVAR(_testinternalcapi_test_long_numbits__doc__,
 "test_long_numbits($module, /)\n"
 "--\n"
@@ -314,4 +282,4 @@ _testinternalcapi_test_long_numbits(PyObject *module, PyObject *Py_UNUSED(ignore
 {
     return _testinternalcapi_test_long_numbits_impl(module);
 }
-/*[clinic end generated code: output=3425f97821fc7462 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=679bf53bbae20085 input=a9049054013a1b77]*/
index 30be7faea55a6ecfae9088079f5eab546ac3be01..c55ebfdb502d61ce67158ccd6429c5592b8fa6b8 100644 (file)
@@ -1698,17 +1698,6 @@ format_unraisable(PyObject *obj, const char *format, ...)
     va_end(va);
 }
 
-void
-_PyErr_WriteUnraisableMsg(const char *err_msg_str, PyObject *obj)
-{
-    if (err_msg_str) {
-        format_unraisable(obj, "Exception ignored %s", err_msg_str);
-    }
-    else {
-        format_unraisable(obj, NULL);
-    }
-}
-
 void
 PyErr_WriteUnraisable(PyObject *obj)
 {