Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-129354: Use PyErr_FormatUnraisable() function #129523

Merged
merged 5 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Lib/test/test_coroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -2411,10 +2413,13 @@ async def corofn():
coro_repr = repr(coro)

# clear reference to the coroutine without awaiting for it
coro_repr = repr(coro)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is already evaluated three lines above.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, I enabled automerge before you posted your review. I wrote #129684 to remove the duplicated variable and remove a few other minor similar issues.

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
Expand Down
5 changes: 4 additions & 1 deletion Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
12 changes: 9 additions & 3 deletions Lib/test/test_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
12 changes: 8 additions & 4 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 6 additions & 3 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
10 changes: 7 additions & 3 deletions Python/_warnings.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
}
}
Expand Down
Loading