diff --git a/src/asynkit/coroutine.py b/src/asynkit/coroutine.py index 783f1ae..a245a92 100644 --- a/src/asynkit/coroutine.py +++ b/src/asynkit/coroutine.py @@ -7,6 +7,7 @@ from typing import ( Any, AsyncGenerator, + AsyncIterable, Awaitable, Callable, Coroutine, @@ -37,6 +38,7 @@ "coro_is_suspended", "coro_is_finished", "coro_iter", + "aiter_sync", "await_sync", "SynchronousError", "SynchronousAbort", @@ -541,3 +543,18 @@ async def helper(*args: P.args, **kwargs: P.kwargs) -> T: return func(*args, **kwargs) return helper + + +def aiter_sync(async_iterable: AsyncIterable[T]) -> Generator[T, None, None]: + """Iterate synchronously over an async itarable""" + ai = async_iterable.__aiter__() + + # a helper ensures that we have a coroutine, not just an Awaitable + async def helper() -> T: + return await ai.__anext__() + + try: + while True: + yield await_sync(helper()) + except StopAsyncIteration: + pass diff --git a/tests/test_coro.py b/tests/test_coro.py index b1bf752..8be087f 100644 --- a/tests/test_coro.py +++ b/tests/test_coro.py @@ -585,6 +585,15 @@ def test_noexit(self): assert err.match("failed to complete synchronously") +def test_aiter_sync(): + async def agen(): + for i in range(5): + yield i + + gen = asynkit.aiter_sync(agen()) + assert list(gen) == list(range(5)) + + class TestCoroAwait: """ These tests test the behaviour of a coroutine wrapped in `coro_await`