gh-129354: Use PyErr_FormatUnraisable() function (#129523)

Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable().

Update tests:

* test_coroutines
* test_exceptions
* test_generators
* test_struct
This commit is contained in:
Victor Stinner 2025-02-05 11:31:59 +01:00 committed by GitHub
parent dc804ffb2f
commit a25042e6d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 42 additions and 17 deletions

View file

@ -2136,8 +2136,10 @@ class CoroutineTest(unittest.TestCase):
coro = None coro = None
support.gc_collect() support.gc_collect()
self.assertEqual(cm.unraisable.err_msg,
f"Exception ignored while finalizing "
f"coroutine {coro_repr}")
self.assertIn("was never awaited", str(cm.unraisable.exc_value)) self.assertIn("was never awaited", str(cm.unraisable.exc_value))
self.assertEqual(repr(cm.unraisable.object), coro_repr)
def test_for_assign_raising_stop_async_iteration(self): def test_for_assign_raising_stop_async_iteration(self):
class BadTarget: class BadTarget:
@ -2411,10 +2413,13 @@ class OriginTrackingTest(unittest.TestCase):
coro_repr = repr(coro) coro_repr = repr(coro)
# clear reference to the coroutine without awaiting for it # clear reference to the coroutine without awaiting for it
coro_repr = repr(coro)
del coro del coro
support.gc_collect() support.gc_collect()
self.assertEqual(repr(cm.unraisable.object), coro_repr) self.assertEqual(cm.unraisable.err_msg,
f"Exception ignored while finalizing "
f"coroutine {coro_repr}")
self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError) self.assertEqual(cm.unraisable.exc_type, ZeroDivisionError)
del warnings._warn_unawaited_coroutine del warnings._warn_unawaited_coroutine

View file

@ -1678,10 +1678,13 @@ class ExceptionTests(unittest.TestCase):
obj = BrokenDel() obj = BrokenDel()
with support.catch_unraisable_exception() as cm: with support.catch_unraisable_exception() as cm:
obj_repr = repr(type(obj).__del__)
del obj del obj
gc_collect() # For PyPy or other GCs. gc_collect() # For PyPy or other GCs.
self.assertEqual(cm.unraisable.object, BrokenDel.__del__) self.assertEqual(cm.unraisable.err_msg,
f"Exception ignored while calling "
f"deallocator {obj_repr}")
self.assertIsNotNone(cm.unraisable.exc_traceback) self.assertIsNotNone(cm.unraisable.exc_traceback)
def test_unhandled(self): def test_unhandled(self):

View file

@ -2664,14 +2664,18 @@ Our ill-behaved code should be invoked during GC:
>>> with support.catch_unraisable_exception() as cm: >>> with support.catch_unraisable_exception() as cm:
... g = f() ... g = f()
... next(g) ... next(g)
... gen_repr = repr(g)
... del g ... del g
... ...
... cm.unraisable.err_msg == (f'Exception ignored while closing '
... f'generator {gen_repr}')
... cm.unraisable.exc_type == RuntimeError ... cm.unraisable.exc_type == RuntimeError
... "generator ignored GeneratorExit" in str(cm.unraisable.exc_value) ... "generator ignored GeneratorExit" in str(cm.unraisable.exc_value)
... cm.unraisable.exc_traceback is not None ... cm.unraisable.exc_traceback is not None
True True
True True
True True
True
And errors thrown during closing should propagate: And errors thrown during closing should propagate:
@ -2776,10 +2780,12 @@ to test.
... invoke("del failed") ... invoke("del failed")
... ...
>>> with support.catch_unraisable_exception() as cm: >>> with support.catch_unraisable_exception() as cm:
... l = Leaker() ... leaker = Leaker()
... del l ... del_repr = repr(type(leaker).__del__)
... del leaker
... ...
... cm.unraisable.object == Leaker.__del__ ... cm.unraisable.err_msg == (f'Exception ignored while '
... f'calling deallocator {del_repr}')
... cm.unraisable.exc_type == RuntimeError ... cm.unraisable.exc_type == RuntimeError
... str(cm.unraisable.exc_value) == "del failed" ... str(cm.unraisable.exc_value) == "del failed"
... cm.unraisable.exc_traceback is not None ... cm.unraisable.exc_traceback is not None

View file

@ -694,7 +694,7 @@ class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase):
rc, stdout, stderr = assert_python_ok("-c", code) rc, stdout, stderr = assert_python_ok("-c", code)
self.assertEqual(rc, 0) self.assertEqual(rc, 0)
self.assertEqual(stdout.rstrip(), b"") self.assertEqual(stdout.rstrip(), b"")
self.assertIn(b"Exception ignored in:", stderr) self.assertIn(b"Exception ignored while calling deallocator", stderr)
self.assertIn(b"C.__del__", stderr) self.assertIn(b"C.__del__", stderr)
def test__struct_reference_cycle_cleaned_up(self): def test__struct_reference_cycle_cleaned_up(self):

View file

@ -97,8 +97,10 @@ _PyGen_Finalize(PyObject *self)
PyObject *res = PyObject_CallOneArg(finalizer, self); PyObject *res = PyObject_CallOneArg(finalizer, self);
if (res == NULL) { if (res == NULL) {
PyErr_WriteUnraisable(self); PyErr_FormatUnraisable("Exception ignored while "
} else { "finalizing generator %R", self);
}
else {
Py_DECREF(res); Py_DECREF(res);
} }
/* Restore the saved exception. */ /* Restore the saved exception. */
@ -122,7 +124,8 @@ _PyGen_Finalize(PyObject *self)
PyObject *res = gen_close((PyObject*)gen, NULL); PyObject *res = gen_close((PyObject*)gen, NULL);
if (res == NULL) { if (res == NULL) {
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
PyErr_WriteUnraisable(self); PyErr_FormatUnraisable("Exception ignored while "
"closing generator %R", self);
} }
} }
else { else {
@ -338,7 +341,8 @@ gen_close_iter(PyObject *yf)
else { else {
PyObject *meth; PyObject *meth;
if (PyObject_GetOptionalAttr(yf, &_Py_ID(close), &meth) < 0) { if (PyObject_GetOptionalAttr(yf, &_Py_ID(close), &meth) < 0) {
PyErr_WriteUnraisable(yf); PyErr_FormatUnraisable("Exception ignored while "
"closing generator %R", yf);
} }
if (meth) { if (meth) {
retval = _PyObject_CallNoArgs(meth); retval = _PyObject_CallNoArgs(meth);

View file

@ -10288,10 +10288,13 @@ slot_tp_finalize(PyObject *self)
del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound); del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound);
if (del != NULL) { if (del != NULL) {
res = call_unbound_noarg(unbound, del, self); res = call_unbound_noarg(unbound, del, self);
if (res == NULL) if (res == NULL) {
PyErr_WriteUnraisable(del); PyErr_FormatUnraisable("Exception ignored while "
else "calling deallocator %R", del);
}
else {
Py_DECREF(res); Py_DECREF(res);
}
Py_DECREF(del); Py_DECREF(del);
} }

View file

@ -1445,7 +1445,8 @@ _PyErr_WarnUnawaitedAgenMethod(PyAsyncGenObject *agen, PyObject *method)
"coroutine method %R of %R was never awaited", "coroutine method %R of %R was never awaited",
method, agen->ag_qualname) < 0) method, agen->ag_qualname) < 0)
{ {
PyErr_WriteUnraisable((PyObject *)agen); PyErr_FormatUnraisable("Exception ignored while "
"finalizing async generator %R", agen);
} }
PyErr_SetRaisedException(exc); PyErr_SetRaisedException(exc);
} }
@ -1487,14 +1488,17 @@ _PyErr_WarnUnawaitedCoroutine(PyObject *coro)
} }
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
PyErr_WriteUnraisable(coro); PyErr_FormatUnraisable("Exception ignored while "
"finalizing coroutine %R", coro);
} }
if (!warned) { if (!warned) {
if (_PyErr_WarnFormat(coro, PyExc_RuntimeWarning, 1, if (_PyErr_WarnFormat(coro, PyExc_RuntimeWarning, 1,
"coroutine '%S' was never awaited", "coroutine '%S' was never awaited",
((PyCoroObject *)coro)->cr_qualname) < 0) ((PyCoroObject *)coro)->cr_qualname) < 0)
{ {
PyErr_WriteUnraisable(coro); PyErr_FormatUnraisable("Exception ignored while "
"finalizing coroutine %R", coro);
} }
} }
} }