redis / redis-py

Redis Python client
MIT License
12.58k stars 2.51k forks source link

Best way to receive the return value when the type hint is Union[Awaitable[int], int]? #2399

Open hyperstarkkk opened 2 years ago

hyperstarkkk commented 2 years ago

I noticed the return value type hint of redis-py functions are always like: Union[Awaitable[int], int]: (e.g. redis.commands.core.py:L4824)

def hlen(self, name: str) -> Union[Awaitable[int], int]:
    """
    Return the number of elements in hash ``name``

    For more information see https://redis.io/commands/hlen
    """
    return self.execute_command("HLEN", name)

When I receive the value as normal

dict_size = redis.hlen("dict_key")
assert dict_size < 10

Pylance will remind me

Operator "<=" not supported for types "Awaitable[int] | int" and "Literal[10]"
  Operator "<=" not supported for types "Awaitable[int]" and "Literal[10]"

How to let Pylance know I am not calling the function in asynchronous way, so I can mitigate this type checking error?

akx commented 2 years ago

That smells like a typing bug in the library. Awaitables should really only be returned when using the async client...

balazser commented 1 year ago

I'm also facing this issue. I there any update?

    def llen(self, name: str) -> Union[Awaitable[int], int]:
        """
        Return the length of the list ``name``

        For more information see https://redis.io/commands/llen
        """

Many of the methods with Union[Awaitable[int], int] require a type guard check, or what is the best way to get around it? :thinking:

Kyle-sandeman-mrdfood commented 1 year ago

Similarly for almost ALL functions in commands/core.py, they're annotated to return ResponseT (or similar) which is Union[Awaitable, Any] which is picked up by PyCharm as just Awaitable. So passing e.g. r.get(key)'s result to anything that expects bytes/str results in a warning

github-actions[bot] commented 7 months ago

This issue is marked stale. It will be closed in 30 days if it is not updated.

Kyle-sandeman-mrdfood commented 7 months ago

Still relevant, I presume

divad commented 7 months ago

I'm hitting this, but in the reverse; mypy complains when using the asyncio version that it might return int, even when I'm importing from redis.asyncio and calling await against the function. Any update?

armarik commented 3 months ago

I'm also facing the same issue.

honzaflash commented 3 months ago

Seems like this should be mentioned here. One of the linked issues above contains a reply with a solution/workaround: https://github.com/redis/redis-py/issues/3091#issuecomment-1903419256

The suggested solution is to install the type stub package: https://pypi.org/project/types-redis/

Worked for me :+1:

Afaiu, this is the old way of getting typing hints for redis but the package now has its own typing hints that are incomplete. Because of that pylance won't tell you to go install available type stubs but it will complain because the included types return types are wrong.

ruben-vb commented 2 months ago

Seems like this should be mentioned here. One of the linked issues above contains a reply with a solution/workaround: #3091 (comment)

The suggested solution is to install the type stub package: https://pypi.org/project/types-redis/

Worked for me 👍

Afaiu, this is the old way of getting typing hints for redis but the package now has its own typing hints that are incomplete. Because of that pylance won't tell you to go install available type stubs but it will complain because the included types return types are wrong.

Thanks this has fixed it :)

It's always a bit irritating when something as big as redis has typing support, but it's incomplete in a way that it fails for the most basic stuff. So I'll leave this here as a push :D

aa0308qq commented 1 month ago

For the latest version of redis-py, the approach I use is:

from typing import Any, Awaitable
from redis.commands.core import ResponseT

class RedisResult:
    @classmethod
    def extract(cls, value: ResponseT) -> Any:
        if isinstance(value, Awaitable):
            raise TypeError
        return value

    @classmethod
    async def extract_async(cls, value: ResponseT) -> Any:
        if not isinstance(value, Awaitable):
            raise TypeError
        return await value

For sync:

from typing import Union
from redis import Redis

sync_redis = Redis(host=host, port=port)
task:Union[str,None] = RedisResult.extract(sync_redis.hget("task","uuid"))

For async:

from typing import Union
from redis.asyncio import Redis as AsyncRedis

async_redis = AsyncRedis(host='localhost', port=port)
task:Union[str,None] =await RedisResult.extract_async(async_redis.hget("task","uuid"))
ykun91 commented 1 month ago

Seems like this should be mentioned here. One of the linked issues above contains a reply with a solution/workaround: #3091 (comment) The suggested solution is to install the type stub package: https://pypi.org/project/types-redis/ Worked for me 👍 Afaiu, this is the old way of getting typing hints for redis but the package now has its own typing hints that are incomplete. Because of that pylance won't tell you to go install available type stubs but it will complain because the included types return types are wrong.

Thanks this has fixed it :)

It's always a bit irritating when something as big as redis has typing support, but it's incomplete in a way that it fails for the most basic stuff. So I'll leave this here as a push :D

Exactly. I don't think Redis, Inc maintain this redis-py well, the code is bad typed in general and lots of issues seems to be neglected.

May better for this project to delete the py.typed and accept the community maded typeshed, instead of releasing the half-baked types and wasting users time.