I've finished this tutorial about async list comprehension. Now I want to try an async dict comprehension. This is the code to replicate:
import asyncio
async def div7_tuple(x):
return x, x/7
async def main():
lost = [4, 8, 15, 16, 23, 42]
awaitables = asyncio.gather(*[div7_tuple(x) for x in lost])
print({k: v for k, v in awaitables})
if __name__ == '__main__':
asyncio.run(main())
However, this results in an exception:
> RuntimeError: await wasn't used with future
> sys:1: RuntimeWarning:coroutine 'div7_tuple' was never awaited
How to do this with asyncio.gather()
?
It's weird this does not work in unsorted way for contruct an unsorted object, because it works if I try in a sorted way:
async def div7(x):
return x/7
async def main2():
lost = [4, 8, 15, 16, 23, 42]
print({k: await v for k, v in [(x, div7(x)) for x in lost]})
gather()
gives you a Future object back(this is that Future object that the error message says - await wasn't used with future
).
If you need the result of the object(in order to iterate over it) you need to await
it first:
async def main():
lost = [4, 8, 15, 16, 23, 42]
awaitables = asyncio.gather(*[div7_tuple(x) for x in lost])
print({k: v for k, v in await awaitables})
or:
async def main():
lost = [4, 8, 15, 16, 23, 42]
awaitables = await asyncio.gather(*[div7_tuple(x) for x in lost])
print(dict(awaitables))
The relevant code in the source code is here:
def __await__(self):
if not self.done():
self._asyncio_future_blocking = True
yield self
if not self.done():
raise RuntimeError("await wasn't used with future")
return self.result() # May raise too.
__iter__ = __await__ # make compatible with 'yield from'.
That __iter__ = __await__
is what makes it possible to iterate over a Future(and not to get a TypeError instead which says "TypeError: 'Future' object is not iterable") but since you didn't use await
to make that Future done(It's result get set) it shows you that error message.