Skip to content

Instantly share code, notes, and snippets.

@achimnol
Created October 28, 2017 06:45
Show Gist options
  • Save achimnol/da6983838b31f6f188d539b9ce9ea5ba to your computer and use it in GitHub Desktop.
Save achimnol/da6983838b31f6f188d539b9ce9ea5ba to your computer and use it in GitHub Desktop.
async getitem with futures
import asyncio
class MyAsyncDict:
async def async_getitem(self, fut, key):
try:
await asyncio.sleep(0.5)
raise RuntimeError('oops')
except Exception as e:
fut.set_exception(e)
else:
fut.set_result(1)
def __getitem__(self, key):
loop = asyncio.get_event_loop()
fut = loop.create_future()
loop.create_task(self.async_getitem(fut, key))
return fut
async def myfunc():
a = MyAsyncDict()
print(await a['x'])
if __name__ == '__main__':
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(myfunc())
finally:
loop.close()
@mntolia
Copy link

mntolia commented Aug 21, 2024

Thanks for this.

@asjadsyed
Copy link

Simplified version:

import asyncio
import random


class MyAsyncDict:
    async def async_getitem(self, key):
        await asyncio.sleep(0.5)
        if random.random() < 0.5:
            return "value"
        else:
            raise KeyError(key)

    def __getitem__(self, key):
        return asyncio.create_task(self.async_getitem(key))


async def main():
    a = MyAsyncDict()
    print(f"Get {a['key']!r}")


asyncio.run(main())

Unfortunately __setitem__ is complicated as it doesn't return anything. The only workaround I found was with nest-asyncio, which monkey-patches asyncio to make it reentrant.

import asyncio
import random

import nest_asyncio

nest_asyncio.apply()


class MyAsyncDict:
    async def async_getitem(self, key):
        await asyncio.sleep(0.5)
        if random.random() < 0.5:
            return "value"
        else:
            raise KeyError(key)

    async def async_setitem(self, key, value):
        await asyncio.sleep(0.5)
        if random.random() < 0.5:
            raise KeyError(key)
        else:
            print(f"Set {key!r} to {value!r}")

    def __getitem__(self, key):
        return asyncio.run(self.async_getitem(key))

    def __setitem__(self, key, value):
        return asyncio.run(self.async_setitem(key, value))


async def main():
    a = MyAsyncDict()
    print(f"Get {a['key']!r} for key 'key'")
    a["key"] = "value"


asyncio.run(main())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment