Search code examples
pythonfunctional-programming

how to use returns.context.RequiresContext with async functions in python?


I am very fond of the returns library in python, and I would like to use it more. I have a little issue right now. Currently, I have a function that uses a redis client and gets the value corresponding to a key, like so:

from redis import Redis
from returns.context import RequiresContext


def get_value(key: str) -> RequiresContext[str, Redis]:
    def inner(client: Redis) -> str:
        value = client.get(key)
        return value.decode("utf-8")

    return RequiresContext(inner)

Obviously, that function works like a charm:

with Redis(
        host=redis_host,
        port=redis_port,
        password=redis_password,
    ) as redis_client:
        value = get_value(key="my-key")(redis_client)
        print("value = ", value)

Now, I would like to use the asyncio pendant of that code, i.e. use the redis.asyncio.Redis. Unfortunately, it looks like things become a bit more complicated in that case. I should probably switch from RequiresContext to RequiresContextFutureResultE, but I was not able to find a working solution. Here's the best code I was able to come up with:

async def get_value(key: str) -> RequiresContextFutureResultE[str, Redis]:
    async def inner(client: Redis) -> FutureResultE[str]:
        value = await client.get(key)
        return FutureResult.from_value(value.decode("utf-8"))

    return RequiresContextFutureResultE(inner)

When I run it like this:

async def main():
    async with Redis(
        host="localhost",
        port=6379,
        password="902nks291",
        db=15,
    ) as redis_client:
        rcfr = get_value(key="user-id")
        value = await rcfr(redis_client)
        print("value: ", value)

asyncio.run(main())

I get the error that rcfr is not a callable. Can someone help me figure out how I should fix my code to make it work the way I want?


Solution

  • If you call a function (get_value) defined with async def you get an awaitable which you must use with await to get its return value. That's why you get the error.

    But get_value shouldn't be async def. It just defines and returns a function (wrapped by RequiresContextFutureResultE), it doesn't perform any IO itself.