from collections.abc import AsyncIterable, AsyncIterator def aiter(*args): # aiter(object, [sentinel]) if len(args) == 2: obj, sentinel = args if callable(obj): # Is this the best we can do? We cannot test whether # obj() returns an awaitable, so the iterator might fail # later when you first try to consume it. raise TypeError('aiter(v, w): v must be a callable') async def async_call_iterator(): while True: value = await obj() if value == sentinel: break else: yield value return async_call_iterator() elif len(args) == 1: obj, = args if isinstance(obj, AsyncIterable): return obj.__aiter__() else: raise TypeError('%r object is not an asynchronous iterable' % (type(obj).__name__,)) else: raise TypeError('expected arguments blabla') def anext(iterator): if not isinstance(iterator, AsyncIterator): raise TypeError('%r object is not an asynchronous iterator' % (type(iterator).__name__,)) return iterator.__anext__() async def anext_with_default(iterator, default): # This function is async because if we want to return # a default value we need to wrap the iteration. awaitable = anext(iterator) try: return await awaitable except StopAsyncIteration: # Maybe default needs to be an awaitable, too? While # convoluted for most use cases, that might help in case # you want to guarantee there is a yield point every time # you await anext(), instead of catching StopAsyncIteration # and special-casing in calling code. return default