Commit dd246d5d authored by Yury Selivanov's avatar Yury Selivanov

Issue #28721: Fix asynchronous generators aclose() and athrow()

parent 92c4bef7
......@@ -450,6 +450,41 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.loop.run_until_complete(run())
def test_async_gen_asyncio_anext_06(self):
DONE = 0
# test synchronous generators
def foo():
try:
yield
except:
pass
g = foo()
g.send(None)
with self.assertRaises(StopIteration):
g.send(None)
# now with asynchronous generators
async def gen():
nonlocal DONE
try:
yield
except:
pass
DONE = 1
async def run():
nonlocal DONE
g = gen()
await g.asend(None)
with self.assertRaises(StopAsyncIteration):
await g.asend(None)
DONE += 10
self.loop.run_until_complete(run())
self.assertEqual(DONE, 11)
def test_async_gen_asyncio_anext_tuple(self):
async def foo():
try:
......@@ -594,6 +629,76 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.loop.run_until_complete(run())
self.assertEqual(DONE, 1)
def test_async_gen_asyncio_aclose_10(self):
DONE = 0
# test synchronous generators
def foo():
try:
yield
except:
pass
g = foo()
g.send(None)
g.close()
# now with asynchronous generators
async def gen():
nonlocal DONE
try:
yield
except:
pass
DONE = 1
async def run():
nonlocal DONE
g = gen()
await g.asend(None)
await g.aclose()
DONE += 10
self.loop.run_until_complete(run())
self.assertEqual(DONE, 11)
def test_async_gen_asyncio_aclose_11(self):
DONE = 0
# test synchronous generators
def foo():
try:
yield
except:
pass
yield
g = foo()
g.send(None)
with self.assertRaisesRegex(RuntimeError, 'ignored GeneratorExit'):
g.close()
# now with asynchronous generators
async def gen():
nonlocal DONE
try:
yield
except:
pass
yield
DONE += 1
async def run():
nonlocal DONE
g = gen()
await g.asend(None)
with self.assertRaisesRegex(RuntimeError, 'ignored GeneratorExit'):
await g.aclose()
DONE += 10
self.loop.run_until_complete(run())
self.assertEqual(DONE, 10)
def test_async_gen_asyncio_asend_01(self):
DONE = 0
......@@ -801,6 +906,41 @@ class AsyncGenAsyncioTest(unittest.TestCase):
self.loop.run_until_complete(run())
self.assertEqual(DONE, 1)
def test_async_gen_asyncio_athrow_03(self):
DONE = 0
# test synchronous generators
def foo():
try:
yield
except:
pass
g = foo()
g.send(None)
with self.assertRaises(StopIteration):
g.throw(ValueError)
# now with asynchronous generators
async def gen():
nonlocal DONE
try:
yield
except:
pass
DONE = 1
async def run():
nonlocal DONE
g = gen()
await g.asend(None)
with self.assertRaises(StopAsyncIteration):
await g.athrow(ValueError)
DONE += 10
self.loop.run_until_complete(run())
self.assertEqual(DONE, 11)
def test_async_gen_asyncio_athrow_tuple(self):
async def gen():
try:
......
......@@ -31,6 +31,9 @@ Core and Builtins
- Issue #26182: Fix a refleak in code that raises DeprecationWarning.
- Issue #28721: Fix asynchronous generators aclose() and athrow() to
handle StopAsyncIteration propagation properly.
Library
-------
......
......@@ -1931,9 +1931,17 @@ yield_close:
return NULL;
check_error:
if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)
|| PyErr_ExceptionMatches(PyExc_GeneratorExit)
) {
if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
o->agt_state = AWAITABLE_STATE_CLOSED;
if (o->agt_args == NULL) {
/* when aclose() is called we don't want to propagate
StopAsyncIteration; just raise StopIteration, signalling
that 'aclose()' is done. */
PyErr_Clear();
PyErr_SetNone(PyExc_StopIteration);
}
}
else if (PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
o->agt_state = AWAITABLE_STATE_CLOSED;
PyErr_Clear(); /* ignore these errors */
PyErr_SetNone(PyExc_StopIteration);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment