async def iter(self): async with self.running(): # Common kwargs to all middleware handlers. kwargs = {"events": self} async with AsyncExitStack() as stack: enter = stack.enter_async_context agen = await enter(aitercontext(self.root())) # Construct the middleware pipeline. if self.batch_args: agen = await enter( aitercontext( Take(*self.batch_args).handler(agen, **kwargs))) # User-provided middleware, which must handle and yield either single # events or a batch. for m in self.executor.processor.middleware: agen = await enter(aitercontext(m.handler(agen, **kwargs))) # Acknowledgement handling. agen = await enter(aitercontext(Ack().handler(agen, **kwargs))) # Automatic deserialisation. if self.want == "records": agen = await enter( aitercontext(Deserialize().handler(agen, **kwargs))) # It begins. async for x in agen: try: yield x except GeneratorExit: logger.warning("events-iter-exit") return
async def test_aitercontext(event_loop): async def agen(): for x in range(5): await asyncio.sleep(1) yield x with event_loop.assert_cleanup(): async with aitercontext(agen()) as safe_gen: it = iter(range(5)) async for item in safe_gen: assert item == next(it) assert event_loop.steps == [1] * 5 with pytest.raises(RuntimeError): await anext(safe_gen) with pytest.raises(RuntimeError): async with safe_gen: pass safe_gen = aitercontext(agen()) with pytest.warns(UserWarning): await anext(safe_gen) with pytest.raises(TypeError): AsyncIteratorContext(None) with pytest.raises(TypeError): AsyncIteratorContext(safe_gen)
async def test_not_an_agen_in_aitercontext(event_loop): async with aitercontext(not_an_agen([1])) as safe_gen: async for item in safe_gen: assert item == 1 with pytest.raises(ZeroDivisionError): async with aitercontext(not_an_agen([1])) as safe_gen: async for item in safe_gen: 1/0
async def test_not_an_agen_in_aitercontext(event_loop): async with aitercontext(not_an_agen([1])) as safe_gen: async for item in safe_gen: assert item == 1 with pytest.raises(ZeroDivisionError): async with aitercontext(not_an_agen([1])) as safe_gen: async for item in safe_gen: 1 / 0
async def test_silence_exception_in_aitercontext(event_loop): async with aitercontext(silence_agen()) as safe_gen: async for item in safe_gen: assert item == 1 1/0 # Silencing a generator exit is forbidden with pytest.raises(GeneratorExit): async with aitercontext(silence_agen()) as safe_gen: async for _ in safe_gen: raise GeneratorExit
async def test_silence_exception_in_aitercontext(event_loop): async with aitercontext(silence_agen()) as safe_gen: async for item in safe_gen: assert item == 1 1 / 0 # Silencing a generator exit is forbidden with pytest.raises(GeneratorExit): async with aitercontext(silence_agen()) as safe_gen: async for _ in safe_gen: raise GeneratorExit
async def test_reraise_exception_in_aitercontext(event_loop): with pytest.raises(RuntimeError) as info: async with aitercontext(reraise_agen()) as safe_gen: async for item in safe_gen: assert item == 1 1/0 assert type(info.value.__cause__) is ZeroDivisionError with pytest.raises(RuntimeError) as info: async with aitercontext(reraise_agen()) as safe_gen: async for item in safe_gen: assert item == 1 raise GeneratorExit assert type(info.value.__cause__) is GeneratorExit
async def test_reraise_exception_in_aitercontext(event_loop): with pytest.raises(RuntimeError) as info: async with aitercontext(reraise_agen()) as safe_gen: async for item in safe_gen: assert item == 1 1 / 0 assert type(info.value.__cause__) is ZeroDivisionError with pytest.raises(RuntimeError) as info: async with aitercontext(reraise_agen()) as safe_gen: async for item in safe_gen: assert item == 1 raise GeneratorExit assert type(info.value.__cause__) is GeneratorExit
async def test_athrow_in_aitercontext(event_loop): async with aitercontext(agen()) as safe_gen: assert await safe_gen.__anext__() == 0 with pytest.raises(ZeroDivisionError): await safe_gen.athrow(ZeroDivisionError()) async for _ in safe_gen: assert False # No more items
async def test_stuck_in_aitercontext(event_loop): with pytest.raises(RuntimeError) as info: async with aitercontext(stuck_agen()) as safe_gen: async for item in safe_gen: assert item == 1 1 / 0 assert "didn't stop after athrow" in str(info.value) with pytest.raises(RuntimeError) as info: async with aitercontext(stuck_agen()) as safe_gen: async for item in safe_gen: assert item == 1 raise GeneratorExit # GeneratorExit relies on aclose, not athrow # The message is a bit different assert "async generator ignored GeneratorExit" in str(info.value)
async def test_simple_aitercontext(event_loop): async with aitercontext(agen()) as safe_gen: # Cannot enter twice with pytest.raises(RuntimeError): async with safe_gen: pass it = iter(range(5)) async for item in safe_gen: assert item == next(it) assert event_loop.steps == [1] * 5 # Exiting is idempotent await safe_gen.__aexit__(None, None, None) await safe_gen.aclose() with pytest.raises(RuntimeError): await anext(safe_gen) with pytest.raises(RuntimeError): async with safe_gen: pass with pytest.raises(RuntimeError): await safe_gen.athrow(ValueError())
async def test_stuck_in_aitercontext(event_loop): with pytest.raises(RuntimeError) as info: async with aitercontext(stuck_agen()) as safe_gen: async for item in safe_gen: assert item == 1 1/0 assert "didn't stop after athrow" in str(info.value) with pytest.raises(RuntimeError) as info: async with aitercontext(stuck_agen()) as safe_gen: async for item in safe_gen: assert item == 1 raise GeneratorExit # GeneratorExit relies on aclose, not athrow # The message is a bit different assert "async generator ignored GeneratorExit" in str(info.value)
async def test_aitercontext_wrong_usage(event_loop): safe_gen = aitercontext(agen()) with pytest.warns(UserWarning): await anext(safe_gen) with pytest.raises(TypeError): AsyncIteratorContext(None) with pytest.raises(TypeError): AsyncIteratorContext(safe_gen)
async def test_aitercontext(event_loop): async def agen(): for x in range(5): await asyncio.sleep(1) yield x with event_loop.assert_cleanup(): async with aitercontext(agen()) as safe_gen: it = iter(range(5)) async for item in safe_gen: assert item == next(it) assert event_loop.steps == [1] * 5 with pytest.raises(RuntimeError): await anext(safe_gen) with pytest.raises(RuntimeError): async with safe_gen: pass with pytest.raises(RuntimeError): await safe_gen.athrow(ValueError()) with event_loop.assert_cleanup(): async with aitercontext(agen()) as safe_gen: assert await safe_gen.__anext__() == 0 with pytest.raises(ZeroDivisionError): await safe_gen.athrow(ZeroDivisionError()) async for item in safe_gen: assert False # No more items safe_gen = aitercontext(agen()) with pytest.warns(UserWarning): await anext(safe_gen) with pytest.raises(TypeError): AsyncIteratorContext(None) with pytest.raises(TypeError): AsyncIteratorContext(safe_gen)
async def test_raise_in_aitercontext(event_loop): with pytest.raises(ZeroDivisionError): async with aitercontext(agen()) as safe_gen: async for _ in safe_gen: 1 / 0 with pytest.raises(ZeroDivisionError): async with aitercontext(agen()) as safe_gen: async for _ in safe_gen: pass 1 / 0 with pytest.raises(GeneratorExit): async with aitercontext(agen()) as safe_gen: async for _ in safe_gen: raise GeneratorExit with pytest.raises(GeneratorExit): async with aitercontext(agen()) as safe_gen: async for _ in safe_gen: pass raise GeneratorExit
async def test_raise_in_aitercontext(event_loop): with pytest.raises(ZeroDivisionError): async with aitercontext(agen()) as safe_gen: async for _ in safe_gen: 1/0 with pytest.raises(ZeroDivisionError): async with aitercontext(agen()) as safe_gen: async for _ in safe_gen: pass 1/0 with pytest.raises(GeneratorExit): async with aitercontext(agen()) as safe_gen: async for _ in safe_gen: raise GeneratorExit with pytest.raises(GeneratorExit): async with aitercontext(agen()) as safe_gen: async for _ in safe_gen: pass raise GeneratorExit
async def test_simple_aitercontext(event_loop): async with aitercontext(agen()) as safe_gen: it = iter(range(5)) async for item in safe_gen: assert item == next(it) assert event_loop.steps == [1]*5 # Exiting is idempotent await safe_gen.__aexit__(None, None, None) await safe_gen.aclose() with pytest.raises(RuntimeError): await anext(safe_gen) with pytest.raises(RuntimeError): async with safe_gen: pass with pytest.raises(RuntimeError): await safe_gen.athrow(ValueError())