diff options
author | Yury Selivanov <yury@magic.io> | 2018-06-07 20:31:26 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-07 20:31:26 -0400 |
commit | 52698c7ad9eae9feb35839fde17a7d1da8036a9b (patch) | |
tree | 1db0d329f430f5eda34575454d35b0b7e5570249 /Lib | |
parent | 378c53cc3187dba57c7560ccc2557f516c8a7bc8 (diff) | |
download | cpython-git-52698c7ad9eae9feb35839fde17a7d1da8036a9b.tar.gz |
bpo-33786: Fix asynchronous generators to handle GeneratorExit in athrow() (GH-7467)
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/contextlib.py | 2 | ||||
-rw-r--r-- | Lib/test/test_asyncgen.py | 56 | ||||
-rw-r--r-- | Lib/test/test_contextlib_async.py | 22 |
3 files changed, 79 insertions, 1 deletions
diff --git a/Lib/contextlib.py b/Lib/contextlib.py index 1a58b509f6..c06ec73f48 100644 --- a/Lib/contextlib.py +++ b/Lib/contextlib.py @@ -187,7 +187,7 @@ class _AsyncGeneratorContextManager(_GeneratorContextManagerBase, # in this implementation try: await self.gen.athrow(typ, value, traceback) - raise RuntimeError("generator didn't stop after throw()") + raise RuntimeError("generator didn't stop after athrow()") except StopAsyncIteration as exc: return exc is not value except RuntimeError as exc: diff --git a/Lib/test/test_asyncgen.py b/Lib/test/test_asyncgen.py index 8dc76ce5c9..9d60cb0e7e 100644 --- a/Lib/test/test_asyncgen.py +++ b/Lib/test/test_asyncgen.py @@ -111,6 +111,31 @@ class AsyncGenTest(unittest.TestCase): def async_iterate(g): res = [] while True: + an = g.__anext__() + try: + while True: + try: + an.__next__() + except StopIteration as ex: + if ex.args: + res.append(ex.args[0]) + break + else: + res.append('EMPTY StopIteration') + break + except StopAsyncIteration: + raise + except Exception as ex: + res.append(str(type(ex))) + break + except StopAsyncIteration: + res.append('STOP') + break + return res + + def async_iterate(g): + res = [] + while True: try: g.__anext__().__next__() except StopAsyncIteration: @@ -297,6 +322,37 @@ class AsyncGenTest(unittest.TestCase): "non-None value .* async generator"): gen().__anext__().send(100) + def test_async_gen_exception_11(self): + def sync_gen(): + yield 10 + yield 20 + + def sync_gen_wrapper(): + yield 1 + sg = sync_gen() + sg.send(None) + try: + sg.throw(GeneratorExit()) + except GeneratorExit: + yield 2 + yield 3 + + async def async_gen(): + yield 10 + yield 20 + + async def async_gen_wrapper(): + yield 1 + asg = async_gen() + await asg.asend(None) + try: + await asg.athrow(GeneratorExit()) + except GeneratorExit: + yield 2 + yield 3 + + self.compare_generators(sync_gen_wrapper(), async_gen_wrapper()) + def test_async_gen_api_01(self): async def gen(): yield 123 diff --git a/Lib/test/test_contextlib_async.py b/Lib/test/test_contextlib_async.py index 355955f9ab..39dcd9b364 100644 --- a/Lib/test/test_contextlib_async.py +++ b/Lib/test/test_contextlib_async.py @@ -36,6 +36,28 @@ class TestAbstractAsyncContextManager(unittest.TestCase): async with manager as context: self.assertIs(manager, context) + @_async_test + async def test_async_gen_propagates_generator_exit(self): + # A regression test for https://bugs.python.org/issue33786. + + @asynccontextmanager + async def ctx(): + yield + + async def gen(): + async with ctx(): + yield 11 + + ret = [] + exc = ValueError(22) + with self.assertRaises(ValueError): + async with ctx(): + async for val in gen(): + ret.append(val) + raise exc + + self.assertEqual(ret, [11]) + def test_exit_is_abstract(self): class MissingAexit(AbstractAsyncContextManager): pass |