-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
[redis] fix pipeline return types #4989
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Pipeline is a subclass of Redis, but instead of the commands returning results, they return Pipeline. Previously the types incorrectly said `Redis().pipeline().rpush("mylist")` returned `int`. The updated types now type the response as `Pipeline`. With this change the following code now type checks. ``` from redis import Redis r = Redis() list_length, _ = r.pipeline().rpush("mylist").ltrim("mylist", 0, 100).execute() ```
def multi(self) -> None: ... | ||
def execute_command(self, *args, **kwargs): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
execute_command
, along with parse_response
, watch
, and unwatch
were duplicated in the methods I copied from Redis
, so I deleted these and updated the copied ones below.
def script_load_for_pipeline(self, script): ... | ||
|
||
class StrictPipeline(BasePipeline, StrictRedis): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StrictPipeline
and BasePipeline
don't exist in the library, so I think it's safe to use use the name Pipeline
: https://github.com/andymccurdy/redis-py/blob/1a41cfd95a53a95b078084d8627be6b6fba3bb71/redis/client.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably was leftover from an old redis-py version.
stubs/redis/redis/client.pyi
Outdated
# methods from `Redis` client. | ||
# arguments match `Redis`, but with some exceptions return types are | ||
# `Pipeline` or `None` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically all the commands have the same arguments as Redis
, but the return types are different.
stubs/redis/redis/client.pyi
Outdated
def sort( | ||
self, | ||
name: _Key, | ||
start: Optional[int] = ..., | ||
num: Optional[int] = ..., | ||
by: Optional[_Key] = ..., | ||
get: Optional[Union[_Key, Sequence[_Key]]] = ..., | ||
desc: bool = ..., | ||
alpha: bool = ..., | ||
store: Optional[_Key] = ..., | ||
groups: bool = ..., | ||
) -> Pipeline: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redis().sort()
is overloaded to provide an accurate return type, but for Pipeline
the return type is always Pipeline
, so I removed the overloads.
typeshed/stubs/redis/redis/client.pyi
Lines 495 to 534 in e2fd852
@overload | |
def sort( | |
self, | |
name: _Key, | |
start: Optional[int] = ..., | |
num: Optional[int] = ..., | |
by: Optional[_Key] = ..., | |
get: Optional[Union[_Key, Sequence[_Key]]] = ..., | |
desc: bool = ..., | |
alpha: bool = ..., | |
store: None = ..., | |
groups: bool = ..., | |
) -> List[_StrType]: ... | |
@overload | |
def sort( | |
self, | |
name: _Key, | |
start: Optional[int] = ..., | |
num: Optional[int] = ..., | |
by: Optional[_Key] = ..., | |
get: Optional[Union[_Key, Sequence[_Key]]] = ..., | |
desc: bool = ..., | |
alpha: bool = ..., | |
*, | |
store: _Key, | |
groups: bool = ..., | |
) -> int: ... | |
@overload | |
def sort( | |
self, | |
name: _Key, | |
start: Optional[int], | |
num: Optional[int], | |
by: Optional[_Key], | |
get: Optional[Union[_Key, Sequence[_Key]]], | |
desc: bool, | |
alpha: bool, | |
store: _Key, | |
groups: bool = ..., | |
) -> int: ... |
stubs/redis/redis/client.pyi
Outdated
def smembers(self, name: _Key) -> Pipeline: ... | ||
def smove(self, src, dst, value) -> Pipeline: ... | ||
def spop(self, name, count: Optional[int] = ...) -> Pipeline: ... | ||
def srandmember(self, name: _Key, number: Optional[int] = ...) -> Pipeline: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the overload here too for the same reason.
typeshed/stubs/redis/redis/client.pyi
Lines 555 to 558 in e2fd852
@overload | |
def srandmember(self, name: _Key, number: None = ...) -> Optional[_Value]: ... | |
@overload | |
def srandmember(self, name: _Key, number: int) -> List[_Value]: ... |
groups: bool = ..., | ||
) -> Pipeline: ... | ||
def scan(self, cursor: int = ..., match: Optional[_Key] = ..., count: Optional[int] = ...) -> Pipeline: ... | ||
def scan_iter(self, match: Optional[Text] = ..., count: Optional[int] = ...) -> Iterator[Any]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scan_iter
and other _iter
methods return generators that explode when next()
is called on them.
def script_flush(self) -> Pipeline: ... | ||
def script_kill(self) -> Pipeline: ... | ||
def script_load(self, script) -> Pipeline: ... | ||
def register_script(self, script: Union[Text, _StrType]) -> Script: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unlike other methods, this one actually returns Script
def pubsub_numpat(self) -> Pipeline: ... | ||
def monitor(self) -> Monitor: ... | ||
def cluster(self, cluster_arg: str, *args: Any) -> Pipeline: ... | ||
def client(self) -> Any: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method explodes when called
def pubsub_channels(self, pattern: _Key = ...) -> Pipeline: ... | ||
def pubsub_numsub(self, *args: _Key) -> Pipeline: ... | ||
def pubsub_numpat(self) -> Pipeline: ... | ||
def monitor(self) -> Monitor: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also returns Monitor
instead of Pipeline
xx: bool = ..., | ||
keepttl: bool = ..., | ||
) -> Pipeline: ... | ||
def __setitem__(self, name, value) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should all the dunder methods be organized at the top?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I try to use the order the implementation uses. But it is your choice.
def object(self, infotype, key) -> Pipeline: ... | ||
def ping(self) -> Pipeline: ... | ||
def save(self) -> Pipeline: ... | ||
def sentinel(self, *args) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this deprecated method doesn't do or return anything: https://github.com/andymccurdy/redis-py/blob/1a41cfd95a53a95b078084d8627be6b6fba3bb71/redis/client.py#L1448-L1451
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this PR and sorry for the long delay. Only one issue below.
stubs/redis/redis/client.pyi
Outdated
@@ -690,38 +690,297 @@ class PubSub: | |||
def run_in_thread(self, sleep_time=...): ... | |||
def ping(self, message: Optional[_Value] = ...) -> None: ... | |||
|
|||
class BasePipeline: | |||
class Pipeline: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This devices from Redis
in the implementation, so it should do so here as well. This will probably need # type: ignore
s where the signature of Pipeline
does not match that of Redis
. (But since Pipeline
is a subtype of Redis
that shouldn't be much of a concern.)
xx: bool = ..., | ||
keepttl: bool = ..., | ||
) -> Pipeline: ... | ||
def __setitem__(self, name, value) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I try to use the order the implementation uses. But it is your choice.
I updated The ordering of methods should match the |
I think |
Pipeline
is a subclass ofRedis
, but instead of the commands returning results, they returnPipeline
.Previously the types said
Redis().pipeline().rpush("mylist")
returnedint
. The updated types now type the response asPipeline
.With this change the following code now type checks.