redis / redis-py

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

ResponseT type seems to be inconsistent with whatever types-redis provides #2933

Open meshantz opened 1 year ago

meshantz commented 1 year ago

Version:

5.0.0

Platform:

python 3.10.12 WSL/Ubuntu 20.04

Description:

Both mypy and pyright result in an error with the following code in test.py

from typing import Protocol
from redis import Redis

class RedisLikeSync(Protocol):
    def get(self, name: str | bytes) -> bytes | None:
        ...

def foo(r: RedisLikeSync):
    return r.get("foo")

r = Redis()
foo(r)

To reproduce:

pyenv shell 3.10.12
python -m venv venv
. venv/bin/activate
pip install -U pip
pip install redis==5.0.0 mypy pyright

Result of mypy test.py

test.py:15: error: Argument 1 to "foo" has incompatible type "Redis"; expected "RedisLikeSync"  [arg-type]
test.py:15: note: Following member(s) of "Redis" have conflicts:
test.py:15: note:     Expected:
test.py:15: note:         def get(self, name: str | bytes) -> bytes | None
test.py:15: note:     Got:
test.py:15: note:         def get(self, name: bytes | str | memoryview) -> Awaitable[Any] | Any

Result of pyright test.py

/home/mshantz/typing/test.py
  /home/mshantz/typing/test.py:15:5 - error: Argument of type "Redis" cannot be assigned to parameter "r" of type "RedisLikeSync" in function "foo"
    "Redis" is incompatible with protocol "RedisLikeSync"
      "get" is an incompatible type
        Type "(name: KeyT) -> ResponseT" cannot be assigned to type "(name: str | bytes) -> (bytes | None)"
          Function return type "ResponseT" is incompatible with type "bytes | None" (reportGeneralTypeIssues)
1 error, 0 warnings, 0 informations

After pip install types-redis, both checks come up clean.

❯ mypy test.py
Success: no issues found in 1 source file

❯ pyright test.py
0 errors, 0 warnings, 0 informations

Since types-redis indicates that it should be removed as of version 5.0.0 of this library, this inconsistency is backwards incompatible...

from https://pypi.org/project/types-redis/

Note: The redis package includes type annotations or type stubs since version 5.0.0. Please uninstall the types-redis package if you use this or a newer version.

Issues #2174, #2399, #2897 may be related...

lokucrazy commented 10 months ago

just wanted to chime in here that I was using redis-py 5.0.1 and had mypy type issues with Awaitable, had to drop down to 4.6.0 and use types-redis

Tjstretchalot commented 10 months ago

Adding in a related issue after enabling type checking in 5.0.1 is a ton (all?) functions are typing the parameters with str when they accept str or bytes. I always pass bytes as not every key or value I store is a valid str (e.g., compressed gzip) and I want consistency in the return types. Currently, my working around looks like this, for reference:

my_info = await typing.cast(
    Awaitable[List[Optional[bytes]]],
    redis.hmget(
        b"my_hash",  # type: ignore
        b"key1",  # type: ignore
        b"key2",  # type: ignore
        b"key3",  # type: ignore
    ),
)
THeK3nger commented 2 months ago

Yep, I had to come back to 4.60 like lokucrazy. Version 5 gives me inconsistent typing errors. The Redis functions says they return Awaitable[Unknown], so I need to await them. But they are not Awaitable at runtime, so I get an error. And casting everything to str doesn't feel great.

dmartin commented 3 weeks ago

I have also reverted back to using types-redis as the builtin types with 5.x aren't specific enough to be useful. As one example:

# types-redis (one of many kwarg-specific overloads)
def zrevrange(..., withscores: Literal[True], ...) -> list[tuple[_StrType, _ScoreCastFuncReturn]]: ...

# 5.x
def zrevrange(..., withscores: bool = False, ...) -> Union[Awaitable[Any], Any]: ...

Union[Awaitable[Any], Any] is a basically useless return type, and isn't even compatible (as there is no indication that an iterable is returned)