mirror of https://github.com/python/cpython
Issue #28721: Fix asynchronous generators aclose() and athrow()
This commit is contained in:
parent
a83a6a3275
commit
41782e4970
|
@ -450,6 +450,41 @@ class AsyncGenAsyncioTest(unittest.TestCase):
|
||||||
|
|
||||||
self.loop.run_until_complete(run())
|
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):
|
def test_async_gen_asyncio_anext_tuple(self):
|
||||||
async def foo():
|
async def foo():
|
||||||
try:
|
try:
|
||||||
|
@ -594,6 +629,76 @@ class AsyncGenAsyncioTest(unittest.TestCase):
|
||||||
self.loop.run_until_complete(run())
|
self.loop.run_until_complete(run())
|
||||||
self.assertEqual(DONE, 1)
|
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):
|
def test_async_gen_asyncio_asend_01(self):
|
||||||
DONE = 0
|
DONE = 0
|
||||||
|
|
||||||
|
@ -801,6 +906,41 @@ class AsyncGenAsyncioTest(unittest.TestCase):
|
||||||
self.loop.run_until_complete(run())
|
self.loop.run_until_complete(run())
|
||||||
self.assertEqual(DONE, 1)
|
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):
|
def test_async_gen_asyncio_athrow_tuple(self):
|
||||||
async def gen():
|
async def gen():
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -31,6 +31,9 @@ Core and Builtins
|
||||||
|
|
||||||
- Issue #26182: Fix a refleak in code that raises DeprecationWarning.
|
- Issue #26182: Fix a refleak in code that raises DeprecationWarning.
|
||||||
|
|
||||||
|
- Issue #28721: Fix asynchronous generators aclose() and athrow() to
|
||||||
|
handle StopAsyncIteration propagation properly.
|
||||||
|
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -1931,9 +1931,17 @@ yield_close:
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
check_error:
|
check_error:
|
||||||
if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)
|
if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)) {
|
||||||
|| PyErr_ExceptionMatches(PyExc_GeneratorExit)
|
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;
|
o->agt_state = AWAITABLE_STATE_CLOSED;
|
||||||
PyErr_Clear(); /* ignore these errors */
|
PyErr_Clear(); /* ignore these errors */
|
||||||
PyErr_SetNone(PyExc_StopIteration);
|
PyErr_SetNone(PyExc_StopIteration);
|
||||||
|
|
Loading…
Reference in New Issue