Search code examples
pythonruntime-errorpython-asynciodictionary-comprehension

RuntimeError: await wasn't used with future - async dict comprehension in python3.8


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]})

Solution

  • 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.