Set class attribute by awaiting a coroutine in Python

Viewed 685

I have a class that has an attribute that holds a Redis connection as seen below:

import redis

class RedisService:
    db = redis.Redis(host=RedisConfig.REDIS_HOST, port=RedisConfig.REDIS_PORT)

    @staticmethod
    def exists(key):
        return RedisService.db.exists(key)

This works well. But now I want to replace this with async alternatives:

import aioredis

class RedisService:
    db = await aioredis.create_connection((RedisConfig.REDIS_HOST, RedisConfig.REDIS_PORT))

    @staticmethod
    async def exists(key):
        value = await RedisService.db.execute('GET', key)
        return value

But await is not allowed to assign a value to a class attribute. If I remove await, when I call RedisService.exists() I get:

File "./src/service/redis.py", line 12, in exists
     value = await RedisService.db.execute('GET', key)
 AttributeError: 'coroutine' object has no attribute 'execute'

So how can I assign a value to a class attribute by awaiting a coroutine?

Note that, I directly call await RedisService.exists() without creating an object instance. It should be a static method and should've been called without creating an instance for some BL reason.

1 Answers

You can create an init function in the module that initializes the needed class attributes:

# let's assume module name redis_service.py

class RedisService:
    ...

async def init():
    RedisService.db = await aioredis.create_connection(...)

You would invoke this function from a main entry point:

import redis_service
...

async def main():
    await redis_service.init()
    ...

if __name__ == '__main__':
    asyncio.run(main())

This has the advantage of being able to re-initialize the "global" async data, in case you run asyncio.run() more than once. Also, the object returned by aioredis.create_connection() is most likely tied to the current event loop, so executing it at class definition time would preclude the use of asyncio.run to begin with.

Related