diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 840043d5271224..5566c9803d43ed 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -2136,8 +2136,10 @@ async def func(): pass coro = None 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.assertEqual(repr(cm.unraisable.object), coro_repr) def test_for_assign_raising_stop_async_iteration(self): class BadTarget: @@ -2411,10 +2413,13 @@ async def corofn(): coro_repr = repr(coro) # clear reference to the coroutine without awaiting for it + coro_repr = repr(coro) del coro 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) del warnings._warn_unawaited_coroutine diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 2d324827451b54..3838eb5b27c9e6 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1678,10 +1678,13 @@ def __del__(self): obj = BrokenDel() with support.catch_unraisable_exception() as cm: + obj_repr = repr(type(obj).__del__) del obj 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) def test_unhandled(self): diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index b6985054c33d10..bf4b88cd9c4450 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -2664,14 +2664,18 @@ def printsolution(self, x): >>> with support.catch_unraisable_exception() as cm: ... g = f() ... next(g) +... gen_repr = repr(g) ... del g ... +... cm.unraisable.err_msg == (f'Exception ignored while closing ' +... f'generator {gen_repr}') ... cm.unraisable.exc_type == RuntimeError ... "generator ignored GeneratorExit" in str(cm.unraisable.exc_value) ... cm.unraisable.exc_traceback is not None True True True +True And errors thrown during closing should propagate: @@ -2776,10 +2780,12 @@ def printsolution(self, x): ... invoke("del failed") ... >>> with support.catch_unraisable_exception() as cm: -... l = Leaker() -... del l +... leaker = Leaker() +... 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 ... str(cm.unraisable.exc_value) == "del failed" ... cm.unraisable.exc_traceback is not None diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 5fee9fbb92acf4..b99391e482ff70 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -694,7 +694,7 @@ def __del__(self): rc, stdout, stderr = assert_python_ok("-c", code) self.assertEqual(rc, 0) 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) def test__struct_reference_cycle_cleaned_up(self): diff --git a/Objects/genobject.c b/Objects/genobject.c index 73bbf86588c457..79aed8571c35e7 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -97,8 +97,10 @@ _PyGen_Finalize(PyObject *self) PyObject *res = PyObject_CallOneArg(finalizer, self); if (res == NULL) { - PyErr_WriteUnraisable(self); - } else { + PyErr_FormatUnraisable("Exception ignored while " + "finalizing generator %R", self); + } + else { Py_DECREF(res); } /* Restore the saved exception. */ @@ -122,7 +124,8 @@ _PyGen_Finalize(PyObject *self) PyObject *res = gen_close((PyObject*)gen, NULL); if (res == NULL) { if (PyErr_Occurred()) { - PyErr_WriteUnraisable(self); + PyErr_FormatUnraisable("Exception ignored while " + "closing generator %R", self); } } else { @@ -338,7 +341,8 @@ gen_close_iter(PyObject *yf) else { PyObject *meth; if (PyObject_GetOptionalAttr(yf, &_Py_ID(close), &meth) < 0) { - PyErr_WriteUnraisable(yf); + PyErr_FormatUnraisable("Exception ignored while " + "closing generator %R", yf); } if (meth) { retval = _PyObject_CallNoArgs(meth); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 93920341a179e8..f3238da8a642e4 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -10288,10 +10288,13 @@ slot_tp_finalize(PyObject *self) del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound); if (del != NULL) { res = call_unbound_noarg(unbound, del, self); - if (res == NULL) - PyErr_WriteUnraisable(del); - else + if (res == NULL) { + PyErr_FormatUnraisable("Exception ignored while " + "calling deallocator %R", del); + } + else { Py_DECREF(res); + } Py_DECREF(del); } diff --git a/Python/_warnings.c b/Python/_warnings.c index 283f203c72c9bf..bb195da9512caf 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -1445,7 +1445,8 @@ _PyErr_WarnUnawaitedAgenMethod(PyAsyncGenObject *agen, PyObject *method) "coroutine method %R of %R was never awaited", method, agen->ag_qualname) < 0) { - PyErr_WriteUnraisable((PyObject *)agen); + PyErr_FormatUnraisable("Exception ignored while " + "finalizing async generator %R", agen); } PyErr_SetRaisedException(exc); } @@ -1487,14 +1488,17 @@ _PyErr_WarnUnawaitedCoroutine(PyObject *coro) } if (PyErr_Occurred()) { - PyErr_WriteUnraisable(coro); + PyErr_FormatUnraisable("Exception ignored while " + "finalizing coroutine %R", coro); } + if (!warned) { if (_PyErr_WarnFormat(coro, PyExc_RuntimeWarning, 1, "coroutine '%S' was never awaited", ((PyCoroObject *)coro)->cr_qualname) < 0) { - PyErr_WriteUnraisable(coro); + PyErr_FormatUnraisable("Exception ignored while " + "finalizing coroutine %R", coro); } } }