GH-117536: GH-117894: fix athrow().throw(...) unawaited warning (GH-117851)

This commit is contained in:
Thomas Grainger 2024-04-24 13:20:19 +01:00 committed by GitHub
parent 975081b11e
commit 7d369d471c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 81 additions and 13 deletions

View File

@ -399,9 +399,8 @@ class AsyncGenTest(unittest.TestCase):
with self.assertWarns(DeprecationWarning): with self.assertWarns(DeprecationWarning):
x = gen().athrow(GeneratorExit, GeneratorExit(), None) x = gen().athrow(GeneratorExit, GeneratorExit(), None)
with self.assertWarnsRegex(RuntimeWarning, with self.assertRaises(GeneratorExit):
f"coroutine method 'athrow' of '{gen.__qualname__}' " x.send(None)
f"was never awaited"):
del x del x
gc_collect() gc_collect()
@ -1572,11 +1571,6 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.assertIsInstance(message['exception'], ZeroDivisionError) self.assertIsInstance(message['exception'], ZeroDivisionError)
self.assertIn('unhandled exception during asyncio.run() shutdown', self.assertIn('unhandled exception during asyncio.run() shutdown',
message['message']) message['message'])
with self.assertWarnsRegex(RuntimeWarning,
f"coroutine method 'aclose' of '{async_iterate.__qualname__}' "
f"was never awaited"):
del message, messages
gc_collect()
def test_async_gen_expression_01(self): def test_async_gen_expression_01(self):
async def arange(n): async def arange(n):
@ -1630,10 +1624,6 @@ class AsyncGenAsyncioTest(unittest.TestCase):
asyncio.run(main()) asyncio.run(main())
self.assertEqual([], messages) self.assertEqual([], messages)
with self.assertWarnsRegex(RuntimeWarning,
f"coroutine method 'aclose' of '{async_iterate.__qualname__}' "
f"was never awaited"):
gc_collect()
def test_async_gen_await_same_anext_coro_twice(self): def test_async_gen_await_same_anext_coro_twice(self):
async def async_iterate(): async def async_iterate():
@ -1671,6 +1661,62 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.loop.run_until_complete(run()) self.loop.run_until_complete(run())
def test_async_gen_throw_same_aclose_coro_twice(self):
async def async_iterate():
yield 1
yield 2
it = async_iterate()
nxt = it.aclose()
with self.assertRaises(StopIteration):
nxt.throw(GeneratorExit)
with self.assertRaisesRegex(
RuntimeError,
r"cannot reuse already awaited aclose\(\)/athrow\(\)"
):
nxt.throw(GeneratorExit)
def test_async_gen_throw_custom_same_aclose_coro_twice(self):
async def async_iterate():
yield 1
yield 2
it = async_iterate()
class MyException(Exception):
pass
nxt = it.aclose()
with self.assertRaises(MyException):
nxt.throw(MyException)
with self.assertRaisesRegex(
RuntimeError,
r"cannot reuse already awaited aclose\(\)/athrow\(\)"
):
nxt.throw(MyException)
def test_async_gen_throw_custom_same_athrow_coro_twice(self):
async def async_iterate():
yield 1
yield 2
it = async_iterate()
class MyException(Exception):
pass
nxt = it.athrow(MyException)
with self.assertRaises(MyException):
nxt.throw(MyException)
with self.assertRaisesRegex(
RuntimeError,
r"cannot reuse already awaited aclose\(\)/athrow\(\)"
):
nxt.throw(MyException)
def test_async_gen_aclose_twice_with_different_coros(self): def test_async_gen_aclose_twice_with_different_coros(self):
# Regression test for https://bugs.python.org/issue39606 # Regression test for https://bugs.python.org/issue39606
async def async_iterate(): async def async_iterate():
@ -1752,6 +1798,19 @@ class TestUnawaitedWarnings(unittest.TestCase):
g.aclose() g.aclose()
gc_collect() gc_collect()
def test_aclose_throw(self):
async def gen():
return
yield
class MyException(Exception):
pass
g = gen()
with self.assertRaises(MyException):
g.aclose().throw(MyException)
del g
gc_collect()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -0,0 +1 @@
Fix a :exc:`RuntimeWarning` when calling ``agen.aclose().throw(Exception)``.

View File

@ -0,0 +1 @@
Prevent ``agen.aclose()`` objects being re-used after ``.throw()``.

View File

@ -2208,7 +2208,11 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na
retval = gen_throw((PyGenObject*)o->agt_gen, args, nargs); retval = gen_throw((PyGenObject*)o->agt_gen, args, nargs);
if (o->agt_args) { if (o->agt_args) {
return async_gen_unwrap_value(o->agt_gen, retval); retval = async_gen_unwrap_value(o->agt_gen, retval);
if (retval == NULL) {
o->agt_state = AWAITABLE_STATE_CLOSED;
}
return retval;
} else { } else {
/* aclose() mode */ /* aclose() mode */
if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) { if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) {
@ -2218,6 +2222,9 @@ async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *const *args, Py_ssize_t na
PyErr_SetString(PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG); PyErr_SetString(PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG);
return NULL; return NULL;
} }
if (retval == NULL) {
o->agt_state = AWAITABLE_STATE_CLOSED;
}
if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) || if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
PyErr_ExceptionMatches(PyExc_GeneratorExit)) PyErr_ExceptionMatches(PyExc_GeneratorExit))
{ {