Viewing file: test_asyncgen.py (31.44 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
import inspect import types import unittest
from test.support import import_module asyncio = import_module("asyncio")
class AwaitException(Exception): pass
@types.coroutine def awaitable(*, throw=False): if throw: yield ('throw',) else: yield ('result',)
def run_until_complete(coro): exc = False while True: try: if exc: exc = False fut = coro.throw(AwaitException) else: fut = coro.send(None) except StopIteration as ex: return ex.args[0]
if fut == ('throw',): exc = True
def to_list(gen): async def iterate(): res = [] async for i in gen: res.append(i) return res
return run_until_complete(iterate())
class AsyncGenSyntaxTest(unittest.TestCase):
def test_async_gen_syntax_01(self): code = '''async def foo(): await abc yield from 123 '''
with self.assertRaisesRegex(SyntaxError, 'yield from.*inside async'): exec(code, {}, {})
def test_async_gen_syntax_02(self): code = '''async def foo(): yield from 123 '''
with self.assertRaisesRegex(SyntaxError, 'yield from.*inside async'): exec(code, {}, {})
def test_async_gen_syntax_03(self): code = '''async def foo(): await abc yield return 123 '''
with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'): exec(code, {}, {})
def test_async_gen_syntax_04(self): code = '''async def foo(): yield return 123 '''
with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'): exec(code, {}, {})
def test_async_gen_syntax_05(self): code = '''async def foo(): if 0: yield return 12 '''
with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'): exec(code, {}, {})
class AsyncGenTest(unittest.TestCase):
def compare_generators(self, sync_gen, async_gen): def sync_iterate(g): res = [] while True: try: res.append(g.__next__()) except StopIteration: res.append('STOP') break except Exception as ex: res.append(str(type(ex))) return res
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
sync_gen_result = sync_iterate(sync_gen) async_gen_result = async_iterate(async_gen) self.assertEqual(sync_gen_result, async_gen_result) return async_gen_result
def test_async_gen_iteration_01(self): async def gen(): await awaitable() a = yield 123 self.assertIs(a, None) await awaitable() yield 456 await awaitable() yield 789
self.assertEqual(to_list(gen()), [123, 456, 789])
def test_async_gen_iteration_02(self): async def gen(): await awaitable() yield 123 await awaitable()
g = gen() ai = g.__aiter__()
an = ai.__anext__() self.assertEqual(an.__next__(), ('result',))
try: an.__next__() except StopIteration as ex: self.assertEqual(ex.args[0], 123) else: self.fail('StopIteration was not raised')
an = ai.__anext__() self.assertEqual(an.__next__(), ('result',))
try: an.__next__() except StopAsyncIteration as ex: self.assertFalse(ex.args) else: self.fail('StopAsyncIteration was not raised')
def test_async_gen_exception_03(self): async def gen(): await awaitable() yield 123 await awaitable(throw=True) yield 456
with self.assertRaises(AwaitException): to_list(gen())
def test_async_gen_exception_04(self): async def gen(): await awaitable() yield 123 1 / 0
g = gen() ai = g.__aiter__() an = ai.__anext__() self.assertEqual(an.__next__(), ('result',))
try: an.__next__() except StopIteration as ex: self.assertEqual(ex.args[0], 123) else: self.fail('StopIteration was not raised')
with self.assertRaises(ZeroDivisionError): ai.__anext__().__next__()
def test_async_gen_exception_05(self): async def gen(): yield 123 raise StopAsyncIteration
with self.assertRaisesRegex(RuntimeError, 'async generator.*StopAsyncIteration'): to_list(gen())
def test_async_gen_exception_06(self): async def gen(): yield 123 raise StopIteration
with self.assertRaisesRegex(RuntimeError, 'async generator.*StopIteration'): to_list(gen())
def test_async_gen_exception_07(self): def sync_gen(): try: yield 1 1 / 0 finally: yield 2 yield 3
yield 100
async def async_gen(): try: yield 1 1 / 0 finally: yield 2 yield 3
yield 100
self.compare_generators(sync_gen(), async_gen())
def test_async_gen_exception_08(self): def sync_gen(): try: yield 1 finally: yield 2 1 / 0 yield 3
yield 100
async def async_gen(): try: yield 1 await awaitable() finally: await awaitable() yield 2 1 / 0 yield 3
yield 100
self.compare_generators(sync_gen(), async_gen())
def test_async_gen_exception_09(self): def sync_gen(): try: yield 1 1 / 0 finally: yield 2 yield 3
yield 100
async def async_gen(): try: await awaitable() yield 1 1 / 0 finally: yield 2 await awaitable() yield 3
yield 100
self.compare_generators(sync_gen(), async_gen())
def test_async_gen_exception_10(self): async def gen(): yield 123 with self.assertRaisesRegex(TypeError, "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
g = gen()
self.assertEqual(g.__name__, 'gen') g.__name__ = '123' self.assertEqual(g.__name__, '123')
self.assertIn('.gen', g.__qualname__) g.__qualname__ = '123' self.assertEqual(g.__qualname__, '123')
self.assertIsNone(g.ag_await) self.assertIsInstance(g.ag_frame, types.FrameType) self.assertFalse(g.ag_running) self.assertIsInstance(g.ag_code, types.CodeType)
self.assertTrue(inspect.isawaitable(g.aclose()))
class AsyncGenAsyncioTest(unittest.TestCase):
def setUp(self): self.loop = asyncio.new_event_loop() asyncio.set_event_loop(None)
def tearDown(self): self.loop.close() self.loop = None asyncio.set_event_loop_policy(None)
async def to_list(self, gen): res = [] async for i in gen: res.append(i) return res
def test_async_gen_asyncio_01(self): async def gen(): yield 1 await asyncio.sleep(0.01) yield 2 await asyncio.sleep(0.01) return yield 3
res = self.loop.run_until_complete(self.to_list(gen())) self.assertEqual(res, [1, 2])
def test_async_gen_asyncio_02(self): async def gen(): yield 1 await asyncio.sleep(0.01) yield 2 1 / 0 yield 3
with self.assertRaises(ZeroDivisionError): self.loop.run_until_complete(self.to_list(gen()))
def test_async_gen_asyncio_03(self): loop = self.loop
class Gen: async def __aiter__(self): yield 1 await asyncio.sleep(0.01) yield 2
res = loop.run_until_complete(self.to_list(Gen())) self.assertEqual(res, [1, 2])
def test_async_gen_asyncio_anext_04(self): async def foo(): yield 1 await asyncio.sleep(0.01) try: yield 2 yield 3 except ZeroDivisionError: yield 1000 await asyncio.sleep(0.01) yield 4
async def run1(): it = foo().__aiter__()
self.assertEqual(await it.__anext__(), 1) self.assertEqual(await it.__anext__(), 2) self.assertEqual(await it.__anext__(), 3) self.assertEqual(await it.__anext__(), 4) with self.assertRaises(StopAsyncIteration): await it.__anext__() with self.assertRaises(StopAsyncIteration): await it.__anext__()
async def run2(): it = foo().__aiter__()
self.assertEqual(await it.__anext__(), 1) self.assertEqual(await it.__anext__(), 2) try: it.__anext__().throw(ZeroDivisionError) except StopIteration as ex: self.assertEqual(ex.args[0], 1000) else: self.fail('StopIteration was not raised') self.assertEqual(await it.__anext__(), 4) with self.assertRaises(StopAsyncIteration): await it.__anext__()
self.loop.run_until_complete(run1()) self.loop.run_until_complete(run2())
def test_async_gen_asyncio_anext_05(self): async def foo(): v = yield 1 v = yield v yield v * 100
async def run(): it = foo().__aiter__()
try: it.__anext__().send(None) except StopIteration as ex: self.assertEqual(ex.args[0], 1) else: self.fail('StopIteration was not raised')
try: it.__anext__().send(10) except StopIteration as ex: self.assertEqual(ex.args[0], 10) else: self.fail('StopIteration was not raised')
try: it.__anext__().send(12) except StopIteration as ex: self.assertEqual(ex.args[0], 1200) else: self.fail('StopIteration was not raised')
with self.assertRaises(StopAsyncIteration): await it.__anext__()
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: yield (1,) except ZeroDivisionError: yield (2,)
async def run(): it = foo().__aiter__()
self.assertEqual(await it.__anext__(), (1,)) with self.assertRaises(StopIteration) as cm: it.__anext__().throw(ZeroDivisionError) self.assertEqual(cm.exception.args[0], (2,)) with self.assertRaises(StopAsyncIteration): await it.__anext__()
self.loop.run_until_complete(run())
def test_async_gen_asyncio_anext_stopiteration(self): async def foo(): try: yield StopIteration(1) except ZeroDivisionError: yield StopIteration(3)
async def run(): it = foo().__aiter__()
v = await it.__anext__() self.assertIsInstance(v, StopIteration) self.assertEqual(v.value, 1) with self.assertRaises(StopIteration) as cm: it.__anext__().throw(ZeroDivisionError) v = cm.exception.args[0] self.assertIsInstance(v, StopIteration) self.assertEqual(v.value, 3) with self.assertRaises(StopAsyncIteration): await it.__anext__()
self.loop.run_until_complete(run())
def test_async_gen_asyncio_aclose_06(self): async def foo(): try: yield 1 1 / 0 finally: await asyncio.sleep(0.01) yield 12
async def run(): gen = foo() it = gen.__aiter__() await it.__anext__() await gen.aclose()
with self.assertRaisesRegex( RuntimeError, "async generator ignored GeneratorExit"): self.loop.run_until_complete(run())
def test_async_gen_asyncio_aclose_07(self): DONE = 0
async def foo(): nonlocal DONE try: yield 1 1 / 0 finally: await asyncio.sleep(0.01) await asyncio.sleep(0.01) DONE += 1 DONE += 1000
async def run(): gen = foo() it = gen.__aiter__() await it.__anext__() await gen.aclose()
self.loop.run_until_complete(run()) self.assertEqual(DONE, 1)
def test_async_gen_asyncio_aclose_08(self): DONE = 0
fut = asyncio.Future(loop=self.loop)
async def foo(): nonlocal DONE try: yield 1 await fut DONE += 1000 yield 2 finally: await asyncio.sleep(0.01) await asyncio.sleep(0.01) DONE += 1 DONE += 1000
async def run(): gen = foo() it = gen.__aiter__() self.assertEqual(await it.__anext__(), 1) await gen.aclose()
self.loop.run_until_complete(run()) self.assertEqual(DONE, 1)
# Silence ResourceWarnings fut.cancel() self.loop.run_until_complete(asyncio.sleep(0.01))
def test_async_gen_asyncio_gc_aclose_09(self): DONE = 0
async def gen(): nonlocal DONE try: while True: yield 1 finally: await asyncio.sleep(0.01) await asyncio.sleep(0.01) DONE = 1
async def run(): g = gen() await g.__anext__() await g.__anext__() del g
await asyncio.sleep(0.1)
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_aclose_12(self): DONE = 0
async def target(): await asyncio.sleep(0.01) 1 / 0
async def foo(): nonlocal DONE task = asyncio.create_task(target()) try: yield 1 finally: try: await task except ZeroDivisionError: DONE = 1
async def run(): gen = foo() it = gen.__aiter__() await it.__anext__() await gen.aclose()
self.loop.run_until_complete(run()) self.assertEqual(DONE, 1)
def test_async_gen_asyncio_asend_01(self): DONE = 0
# Sanity check: def sgen(): v = yield 1 yield v * 2 sg = sgen() v = sg.send(None) self.assertEqual(v, 1) v = sg.send(100) self.assertEqual(v, 200)
async def gen(): nonlocal DONE try: await asyncio.sleep(0.01) v = yield 1 await asyncio.sleep(0.01) yield v * 2 await asyncio.sleep(0.01) return finally: await asyncio.sleep(0.01) await asyncio.sleep(0.01) DONE = 1
async def run(): g = gen()
v = await g.asend(None) self.assertEqual(v, 1)
v = await g.asend(100) self.assertEqual(v, 200)
with self.assertRaises(StopAsyncIteration): await g.asend(None)
self.loop.run_until_complete(run()) self.assertEqual(DONE, 1)
def test_async_gen_asyncio_asend_02(self): DONE = 0
async def sleep_n_crash(delay): await asyncio.sleep(delay) 1 / 0
async def gen(): nonlocal DONE try: await asyncio.sleep(0.01) v = yield 1 await sleep_n_crash(0.01) DONE += 1000 yield v * 2 finally: await asyncio.sleep(0.01) await asyncio.sleep(0.01) DONE = 1
async def run(): g = gen()
v = await g.asend(None) self.assertEqual(v, 1)
await g.asend(100)
with self.assertRaises(ZeroDivisionError): self.loop.run_until_complete(run()) self.assertEqual(DONE, 1)
def test_async_gen_asyncio_asend_03(self): DONE = 0
async def sleep_n_crash(delay): fut = asyncio.ensure_future(asyncio.sleep(delay), loop=self.loop) self.loop.call_later(delay / 2, lambda: fut.cancel()) return await fut
async def gen(): nonlocal DONE try: await asyncio.sleep(0.01) v = yield 1 await sleep_n_crash(0.01) DONE += 1000 yield v * 2 finally: await asyncio.sleep(0.01) await asyncio.sleep(0.01) DONE = 1
async def run(): g = gen()
v = await g.asend(None) self.assertEqual(v, 1)
await g.asend(100)
with self.assertRaises(asyncio.CancelledError): self.loop.run_until_complete(run()) self.assertEqual(DONE, 1)
def test_async_gen_asyncio_athrow_01(self): DONE = 0
class FooEr(Exception): pass
# Sanity check: def sgen(): try: v = yield 1 except FooEr: v = 1000 yield v * 2 sg = sgen() v = sg.send(None) self.assertEqual(v, 1) v = sg.throw(FooEr) self.assertEqual(v, 2000) with self.assertRaises(StopIteration): sg.send(None)
async def gen(): nonlocal DONE try: await asyncio.sleep(0.01) try: v = yield 1 except FooEr: v = 1000 await asyncio.sleep(0.01) yield v * 2 await asyncio.sleep(0.01) # return finally: await asyncio.sleep(0.01) await asyncio.sleep(0.01) DONE = 1
async def run(): g = gen()
v = await g.asend(None) self.assertEqual(v, 1)
v = await g.athrow(FooEr) self.assertEqual(v, 2000)
with self.assertRaises(StopAsyncIteration): await g.asend(None)
self.loop.run_until_complete(run()) self.assertEqual(DONE, 1)
def test_async_gen_asyncio_athrow_02(self): DONE = 0
class FooEr(Exception): pass
async def sleep_n_crash(delay): fut = asyncio.ensure_future(asyncio.sleep(delay), loop=self.loop) self.loop.call_later(delay / 2, lambda: fut.cancel()) return await fut
async def gen(): nonlocal DONE try: await asyncio.sleep(0.01) try: v = yield 1 except FooEr: await sleep_n_crash(0.01) yield v * 2 await asyncio.sleep(0.01) # return finally: await asyncio.sleep(0.01) await asyncio.sleep(0.01) DONE = 1
async def run(): g = gen()
v = await g.asend(None) self.assertEqual(v, 1)
try: await g.athrow(FooEr) except asyncio.CancelledError: self.assertEqual(DONE, 1) raise else: self.fail('CancelledError was not raised')
with self.assertRaises(asyncio.CancelledError): 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: yield 1 except ZeroDivisionError: yield (2,)
async def run(): g = gen() v = await g.asend(None) self.assertEqual(v, 1) v = await g.athrow(ZeroDivisionError) self.assertEqual(v, (2,)) with self.assertRaises(StopAsyncIteration): await g.asend(None)
self.loop.run_until_complete(run())
def test_async_gen_asyncio_athrow_stopiteration(self): async def gen(): try: yield 1 except ZeroDivisionError: yield StopIteration(2)
async def run(): g = gen() v = await g.asend(None) self.assertEqual(v, 1) v = await g.athrow(ZeroDivisionError) self.assertIsInstance(v, StopIteration) self.assertEqual(v.value, 2) with self.assertRaises(StopAsyncIteration): await g.asend(None)
self.loop.run_until_complete(run())
def test_async_gen_asyncio_shutdown_01(self): finalized = 0
async def waiter(timeout): nonlocal finalized try: await asyncio.sleep(timeout) yield 1 finally: await asyncio.sleep(0) finalized += 1
async def wait(): async for _ in waiter(1): pass
t1 = self.loop.create_task(wait()) t2 = self.loop.create_task(wait())
self.loop.run_until_complete(asyncio.sleep(0.1))
# Silence warnings t1.cancel() t2.cancel()
with self.assertRaises(asyncio.CancelledError): self.loop.run_until_complete(t1) with self.assertRaises(asyncio.CancelledError): self.loop.run_until_complete(t2)
self.loop.run_until_complete(self.loop.shutdown_asyncgens())
self.assertEqual(finalized, 2)
def test_async_gen_expression_01(self): async def arange(n): for i in range(n): await asyncio.sleep(0.01) yield i
def make_arange(n): # This syntax is legal starting with Python 3.7 return (i * 2 async for i in arange(n))
async def run(): return [i async for i in make_arange(10)]
res = self.loop.run_until_complete(run()) self.assertEqual(res, [i * 2 for i in range(10)])
def test_async_gen_expression_02(self): async def wrap(n): await asyncio.sleep(0.01) return n
def make_arange(n): # This syntax is legal starting with Python 3.7 return (i * 2 for i in range(n) if await wrap(i))
async def run(): return [i async for i in make_arange(10)]
res = self.loop.run_until_complete(run()) self.assertEqual(res, [i * 2 for i in range(1, 10)])
def test_asyncgen_nonstarted_hooks_are_cancellable(self): # See https://bugs.python.org/issue38013 messages = []
def exception_handler(loop, context): messages.append(context)
async def async_iterate(): yield 1 yield 2
async def main(): loop = asyncio.get_running_loop() loop.set_exception_handler(exception_handler)
async for i in async_iterate(): break
asyncio.run(main())
self.assertEqual([], messages)
def test_async_gen_await_same_anext_coro_twice(self): async def async_iterate(): yield 1 yield 2
async def run(): it = async_iterate() nxt = it.__anext__() await nxt with self.assertRaisesRegex( RuntimeError, r"cannot reuse already awaited __anext__\(\)/asend\(\)" ): await nxt
await it.aclose() # prevent unfinished iterator warning
self.loop.run_until_complete(run())
def test_async_gen_await_same_aclose_coro_twice(self): async def async_iterate(): yield 1 yield 2
async def run(): it = async_iterate() nxt = it.aclose() await nxt with self.assertRaisesRegex( RuntimeError, r"cannot reuse already awaited aclose\(\)/athrow\(\)" ): await nxt
self.loop.run_until_complete(run())
def test_async_gen_aclose_twice_with_different_coros(self): # Regression test for https://bugs.python.org/issue39606 async def async_iterate(): yield 1 yield 2
async def run(): it = async_iterate() await it.aclose() await it.aclose()
self.loop.run_until_complete(run())
def test_async_gen_aclose_after_exhaustion(self): # Regression test for https://bugs.python.org/issue39606 async def async_iterate(): yield 1 yield 2
async def run(): it = async_iterate() async for _ in it: pass await it.aclose()
self.loop.run_until_complete(run())
def test_async_gen_aclose_compatible_with_get_stack(self): async def async_generator(): yield object()
async def run(): ag = async_generator() asyncio.create_task(ag.aclose()) tasks = asyncio.all_tasks() for task in tasks: # No AttributeError raised task.get_stack()
self.loop.run_until_complete(run())
if __name__ == "__main__": unittest.main()
|