Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/redis/commands/core.py: 22%
1867 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-23 06:16 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-23 06:16 +0000
1# from __future__ import annotations
3import datetime
4import hashlib
5import warnings
6from typing import (
7 TYPE_CHECKING,
8 AsyncIterator,
9 Awaitable,
10 Callable,
11 Dict,
12 Iterable,
13 Iterator,
14 List,
15 Literal,
16 Mapping,
17 Optional,
18 Sequence,
19 Set,
20 Tuple,
21 Union,
22)
24from redis.exceptions import ConnectionError, DataError, NoScriptError, RedisError
25from redis.typing import (
26 AbsExpiryT,
27 AnyKeyT,
28 BitfieldOffsetT,
29 ChannelT,
30 CommandsProtocol,
31 ConsumerT,
32 EncodableT,
33 ExpiryT,
34 FieldT,
35 GroupT,
36 KeysT,
37 KeyT,
38 PatternT,
39 ResponseT,
40 ScriptTextT,
41 StreamIdT,
42 TimeoutSecT,
43 ZScoreBoundT,
44)
46from .helpers import list_or_args
48if TYPE_CHECKING:
49 from redis.asyncio.client import Redis as AsyncRedis
50 from redis.client import Redis
53class ACLCommands(CommandsProtocol):
54 """
55 Redis Access Control List (ACL) commands.
56 see: https://redis.io/topics/acl
57 """
59 def acl_cat(self, category: Union[str, None] = None, **kwargs) -> ResponseT:
60 """
61 Returns a list of categories or commands within a category.
63 If ``category`` is not supplied, returns a list of all categories.
64 If ``category`` is supplied, returns a list of all commands within
65 that category.
67 For more information see https://redis.io/commands/acl-cat
68 """
69 pieces: list[EncodableT] = [category] if category else []
70 return self.execute_command("ACL CAT", *pieces, **kwargs)
72 def acl_dryrun(self, username, *args, **kwargs):
73 """
74 Simulate the execution of a given command by a given ``username``.
76 For more information see https://redis.io/commands/acl-dryrun
77 """
78 return self.execute_command("ACL DRYRUN", username, *args, **kwargs)
80 def acl_deluser(self, *username: str, **kwargs) -> ResponseT:
81 """
82 Delete the ACL for the specified ``username``s
84 For more information see https://redis.io/commands/acl-deluser
85 """
86 return self.execute_command("ACL DELUSER", *username, **kwargs)
88 def acl_genpass(self, bits: Union[int, None] = None, **kwargs) -> ResponseT:
89 """Generate a random password value.
90 If ``bits`` is supplied then use this number of bits, rounded to
91 the next multiple of 4.
92 See: https://redis.io/commands/acl-genpass
93 """
94 pieces = []
95 if bits is not None:
96 try:
97 b = int(bits)
98 if b < 0 or b > 4096:
99 raise ValueError
100 pieces.append(b)
101 except ValueError:
102 raise DataError(
103 "genpass optionally accepts a bits argument, between 0 and 4096."
104 )
105 return self.execute_command("ACL GENPASS", *pieces, **kwargs)
107 def acl_getuser(self, username: str, **kwargs) -> ResponseT:
108 """
109 Get the ACL details for the specified ``username``.
111 If ``username`` does not exist, return None
113 For more information see https://redis.io/commands/acl-getuser
114 """
115 return self.execute_command("ACL GETUSER", username, **kwargs)
117 def acl_help(self, **kwargs) -> ResponseT:
118 """The ACL HELP command returns helpful text describing
119 the different subcommands.
121 For more information see https://redis.io/commands/acl-help
122 """
123 return self.execute_command("ACL HELP", **kwargs)
125 def acl_list(self, **kwargs) -> ResponseT:
126 """
127 Return a list of all ACLs on the server
129 For more information see https://redis.io/commands/acl-list
130 """
131 return self.execute_command("ACL LIST", **kwargs)
133 def acl_log(self, count: Union[int, None] = None, **kwargs) -> ResponseT:
134 """
135 Get ACL logs as a list.
136 :param int count: Get logs[0:count].
137 :rtype: List.
139 For more information see https://redis.io/commands/acl-log
140 """
141 args = []
142 if count is not None:
143 if not isinstance(count, int):
144 raise DataError("ACL LOG count must be an integer")
145 args.append(count)
147 return self.execute_command("ACL LOG", *args, **kwargs)
149 def acl_log_reset(self, **kwargs) -> ResponseT:
150 """
151 Reset ACL logs.
152 :rtype: Boolean.
154 For more information see https://redis.io/commands/acl-log
155 """
156 args = [b"RESET"]
157 return self.execute_command("ACL LOG", *args, **kwargs)
159 def acl_load(self, **kwargs) -> ResponseT:
160 """
161 Load ACL rules from the configured ``aclfile``.
163 Note that the server must be configured with the ``aclfile``
164 directive to be able to load ACL rules from an aclfile.
166 For more information see https://redis.io/commands/acl-load
167 """
168 return self.execute_command("ACL LOAD", **kwargs)
170 def acl_save(self, **kwargs) -> ResponseT:
171 """
172 Save ACL rules to the configured ``aclfile``.
174 Note that the server must be configured with the ``aclfile``
175 directive to be able to save ACL rules to an aclfile.
177 For more information see https://redis.io/commands/acl-save
178 """
179 return self.execute_command("ACL SAVE", **kwargs)
181 def acl_setuser(
182 self,
183 username: str,
184 enabled: bool = False,
185 nopass: bool = False,
186 passwords: Union[str, Iterable[str], None] = None,
187 hashed_passwords: Union[str, Iterable[str], None] = None,
188 categories: Optional[Iterable[str]] = None,
189 commands: Optional[Iterable[str]] = None,
190 keys: Optional[Iterable[KeyT]] = None,
191 channels: Optional[Iterable[ChannelT]] = None,
192 selectors: Optional[Iterable[Tuple[str, KeyT]]] = None,
193 reset: bool = False,
194 reset_keys: bool = False,
195 reset_channels: bool = False,
196 reset_passwords: bool = False,
197 **kwargs,
198 ) -> ResponseT:
199 """
200 Create or update an ACL user.
202 Create or update the ACL for ``username``. If the user already exists,
203 the existing ACL is completely overwritten and replaced with the
204 specified values.
206 ``enabled`` is a boolean indicating whether the user should be allowed
207 to authenticate or not. Defaults to ``False``.
209 ``nopass`` is a boolean indicating whether the can authenticate without
210 a password. This cannot be True if ``passwords`` are also specified.
212 ``passwords`` if specified is a list of plain text passwords
213 to add to or remove from the user. Each password must be prefixed with
214 a '+' to add or a '-' to remove. For convenience, the value of
215 ``passwords`` can be a simple prefixed string when adding or
216 removing a single password.
218 ``hashed_passwords`` if specified is a list of SHA-256 hashed passwords
219 to add to or remove from the user. Each hashed password must be
220 prefixed with a '+' to add or a '-' to remove. For convenience,
221 the value of ``hashed_passwords`` can be a simple prefixed string when
222 adding or removing a single password.
224 ``categories`` if specified is a list of strings representing category
225 permissions. Each string must be prefixed with either a '+' to add the
226 category permission or a '-' to remove the category permission.
228 ``commands`` if specified is a list of strings representing command
229 permissions. Each string must be prefixed with either a '+' to add the
230 command permission or a '-' to remove the command permission.
232 ``keys`` if specified is a list of key patterns to grant the user
233 access to. Keys patterns allow '*' to support wildcard matching. For
234 example, '*' grants access to all keys while 'cache:*' grants access
235 to all keys that are prefixed with 'cache:'. ``keys`` should not be
236 prefixed with a '~'.
238 ``reset`` is a boolean indicating whether the user should be fully
239 reset prior to applying the new ACL. Setting this to True will
240 remove all existing passwords, flags and privileges from the user and
241 then apply the specified rules. If this is False, the user's existing
242 passwords, flags and privileges will be kept and any new specified
243 rules will be applied on top.
245 ``reset_keys`` is a boolean indicating whether the user's key
246 permissions should be reset prior to applying any new key permissions
247 specified in ``keys``. If this is False, the user's existing
248 key permissions will be kept and any new specified key permissions
249 will be applied on top.
251 ``reset_channels`` is a boolean indicating whether the user's channel
252 permissions should be reset prior to applying any new channel permissions
253 specified in ``channels``.If this is False, the user's existing
254 channel permissions will be kept and any new specified channel permissions
255 will be applied on top.
257 ``reset_passwords`` is a boolean indicating whether to remove all
258 existing passwords and the 'nopass' flag from the user prior to
259 applying any new passwords specified in 'passwords' or
260 'hashed_passwords'. If this is False, the user's existing passwords
261 and 'nopass' status will be kept and any new specified passwords
262 or hashed_passwords will be applied on top.
264 For more information see https://redis.io/commands/acl-setuser
265 """
266 encoder = self.get_encoder()
267 pieces: List[EncodableT] = [username]
269 if reset:
270 pieces.append(b"reset")
272 if reset_keys:
273 pieces.append(b"resetkeys")
275 if reset_channels:
276 pieces.append(b"resetchannels")
278 if reset_passwords:
279 pieces.append(b"resetpass")
281 if enabled:
282 pieces.append(b"on")
283 else:
284 pieces.append(b"off")
286 if (passwords or hashed_passwords) and nopass:
287 raise DataError(
288 "Cannot set 'nopass' and supply 'passwords' or 'hashed_passwords'"
289 )
291 if passwords:
292 # as most users will have only one password, allow remove_passwords
293 # to be specified as a simple string or a list
294 passwords = list_or_args(passwords, [])
295 for i, password in enumerate(passwords):
296 password = encoder.encode(password)
297 if password.startswith(b"+"):
298 pieces.append(b">%s" % password[1:])
299 elif password.startswith(b"-"):
300 pieces.append(b"<%s" % password[1:])
301 else:
302 raise DataError(
303 f"Password {i} must be prefixed with a "
304 f'"+" to add or a "-" to remove'
305 )
307 if hashed_passwords:
308 # as most users will have only one password, allow remove_passwords
309 # to be specified as a simple string or a list
310 hashed_passwords = list_or_args(hashed_passwords, [])
311 for i, hashed_password in enumerate(hashed_passwords):
312 hashed_password = encoder.encode(hashed_password)
313 if hashed_password.startswith(b"+"):
314 pieces.append(b"#%s" % hashed_password[1:])
315 elif hashed_password.startswith(b"-"):
316 pieces.append(b"!%s" % hashed_password[1:])
317 else:
318 raise DataError(
319 f"Hashed password {i} must be prefixed with a "
320 f'"+" to add or a "-" to remove'
321 )
323 if nopass:
324 pieces.append(b"nopass")
326 if categories:
327 for category in categories:
328 category = encoder.encode(category)
329 # categories can be prefixed with one of (+@, +, -@, -)
330 if category.startswith(b"+@"):
331 pieces.append(category)
332 elif category.startswith(b"+"):
333 pieces.append(b"+@%s" % category[1:])
334 elif category.startswith(b"-@"):
335 pieces.append(category)
336 elif category.startswith(b"-"):
337 pieces.append(b"-@%s" % category[1:])
338 else:
339 raise DataError(
340 f'Category "{encoder.decode(category, force=True)}" '
341 'must be prefixed with "+" or "-"'
342 )
343 if commands:
344 for cmd in commands:
345 cmd = encoder.encode(cmd)
346 if not cmd.startswith(b"+") and not cmd.startswith(b"-"):
347 raise DataError(
348 f'Command "{encoder.decode(cmd, force=True)}" '
349 'must be prefixed with "+" or "-"'
350 )
351 pieces.append(cmd)
353 if keys:
354 for key in keys:
355 key = encoder.encode(key)
356 if not key.startswith(b"%") and not key.startswith(b"~"):
357 key = b"~%s" % key
358 pieces.append(key)
360 if channels:
361 for channel in channels:
362 channel = encoder.encode(channel)
363 pieces.append(b"&%s" % channel)
365 if selectors:
366 for cmd, key in selectors:
367 cmd = encoder.encode(cmd)
368 if not cmd.startswith(b"+") and not cmd.startswith(b"-"):
369 raise DataError(
370 f'Command "{encoder.decode(cmd, force=True)}" '
371 'must be prefixed with "+" or "-"'
372 )
374 key = encoder.encode(key)
375 if not key.startswith(b"%") and not key.startswith(b"~"):
376 key = b"~%s" % key
378 pieces.append(b"(%s %s)" % (cmd, key))
380 return self.execute_command("ACL SETUSER", *pieces, **kwargs)
382 def acl_users(self, **kwargs) -> ResponseT:
383 """Returns a list of all registered users on the server.
385 For more information see https://redis.io/commands/acl-users
386 """
387 return self.execute_command("ACL USERS", **kwargs)
389 def acl_whoami(self, **kwargs) -> ResponseT:
390 """Get the username for the current connection
392 For more information see https://redis.io/commands/acl-whoami
393 """
394 return self.execute_command("ACL WHOAMI", **kwargs)
397AsyncACLCommands = ACLCommands
400class ManagementCommands(CommandsProtocol):
401 """
402 Redis management commands
403 """
405 def auth(self, password: str, username: Optional[str] = None, **kwargs):
406 """
407 Authenticates the user. If you do not pass username, Redis will try to
408 authenticate for the "default" user. If you do pass username, it will
409 authenticate for the given user.
410 For more information see https://redis.io/commands/auth
411 """
412 pieces = []
413 if username is not None:
414 pieces.append(username)
415 pieces.append(password)
416 return self.execute_command("AUTH", *pieces, **kwargs)
418 def bgrewriteaof(self, **kwargs):
419 """Tell the Redis server to rewrite the AOF file from data in memory.
421 For more information see https://redis.io/commands/bgrewriteaof
422 """
423 return self.execute_command("BGREWRITEAOF", **kwargs)
425 def bgsave(self, schedule: bool = True, **kwargs) -> ResponseT:
426 """
427 Tell the Redis server to save its data to disk. Unlike save(),
428 this method is asynchronous and returns immediately.
430 For more information see https://redis.io/commands/bgsave
431 """
432 pieces = []
433 if schedule:
434 pieces.append("SCHEDULE")
435 return self.execute_command("BGSAVE", *pieces, **kwargs)
437 def role(self) -> ResponseT:
438 """
439 Provide information on the role of a Redis instance in
440 the context of replication, by returning if the instance
441 is currently a master, slave, or sentinel.
443 For more information see https://redis.io/commands/role
444 """
445 return self.execute_command("ROLE")
447 def client_kill(self, address: str, **kwargs) -> ResponseT:
448 """Disconnects the client at ``address`` (ip:port)
450 For more information see https://redis.io/commands/client-kill
451 """
452 return self.execute_command("CLIENT KILL", address, **kwargs)
454 def client_kill_filter(
455 self,
456 _id: Union[str, None] = None,
457 _type: Union[str, None] = None,
458 addr: Union[str, None] = None,
459 skipme: Union[bool, None] = None,
460 laddr: Union[bool, None] = None,
461 user: str = None,
462 **kwargs,
463 ) -> ResponseT:
464 """
465 Disconnects client(s) using a variety of filter options
466 :param _id: Kills a client by its unique ID field
467 :param _type: Kills a client by type where type is one of 'normal',
468 'master', 'slave' or 'pubsub'
469 :param addr: Kills a client by its 'address:port'
470 :param skipme: If True, then the client calling the command
471 will not get killed even if it is identified by one of the filter
472 options. If skipme is not provided, the server defaults to skipme=True
473 :param laddr: Kills a client by its 'local (bind) address:port'
474 :param user: Kills a client for a specific user name
475 """
476 args = []
477 if _type is not None:
478 client_types = ("normal", "master", "slave", "pubsub")
479 if str(_type).lower() not in client_types:
480 raise DataError(f"CLIENT KILL type must be one of {client_types!r}")
481 args.extend((b"TYPE", _type))
482 if skipme is not None:
483 if not isinstance(skipme, bool):
484 raise DataError("CLIENT KILL skipme must be a bool")
485 if skipme:
486 args.extend((b"SKIPME", b"YES"))
487 else:
488 args.extend((b"SKIPME", b"NO"))
489 if _id is not None:
490 args.extend((b"ID", _id))
491 if addr is not None:
492 args.extend((b"ADDR", addr))
493 if laddr is not None:
494 args.extend((b"LADDR", laddr))
495 if user is not None:
496 args.extend((b"USER", user))
497 if not args:
498 raise DataError(
499 "CLIENT KILL <filter> <value> ... ... <filter> "
500 "<value> must specify at least one filter"
501 )
502 return self.execute_command("CLIENT KILL", *args, **kwargs)
504 def client_info(self, **kwargs) -> ResponseT:
505 """
506 Returns information and statistics about the current
507 client connection.
509 For more information see https://redis.io/commands/client-info
510 """
511 return self.execute_command("CLIENT INFO", **kwargs)
513 def client_list(
514 self, _type: Union[str, None] = None, client_id: List[EncodableT] = [], **kwargs
515 ) -> ResponseT:
516 """
517 Returns a list of currently connected clients.
518 If type of client specified, only that type will be returned.
520 :param _type: optional. one of the client types (normal, master,
521 replica, pubsub)
522 :param client_id: optional. a list of client ids
524 For more information see https://redis.io/commands/client-list
525 """
526 args = []
527 if _type is not None:
528 client_types = ("normal", "master", "replica", "pubsub")
529 if str(_type).lower() not in client_types:
530 raise DataError(f"CLIENT LIST _type must be one of {client_types!r}")
531 args.append(b"TYPE")
532 args.append(_type)
533 if not isinstance(client_id, list):
534 raise DataError("client_id must be a list")
535 if client_id:
536 args.append(b"ID")
537 args.append(" ".join(client_id))
538 return self.execute_command("CLIENT LIST", *args, **kwargs)
540 def client_getname(self, **kwargs) -> ResponseT:
541 """
542 Returns the current connection name
544 For more information see https://redis.io/commands/client-getname
545 """
546 return self.execute_command("CLIENT GETNAME", **kwargs)
548 def client_getredir(self, **kwargs) -> ResponseT:
549 """
550 Returns the ID (an integer) of the client to whom we are
551 redirecting tracking notifications.
553 see: https://redis.io/commands/client-getredir
554 """
555 return self.execute_command("CLIENT GETREDIR", **kwargs)
557 def client_reply(
558 self, reply: Union[Literal["ON"], Literal["OFF"], Literal["SKIP"]], **kwargs
559 ) -> ResponseT:
560 """
561 Enable and disable redis server replies.
563 ``reply`` Must be ON OFF or SKIP,
564 ON - The default most with server replies to commands
565 OFF - Disable server responses to commands
566 SKIP - Skip the response of the immediately following command.
568 Note: When setting OFF or SKIP replies, you will need a client object
569 with a timeout specified in seconds, and will need to catch the
570 TimeoutError.
571 The test_client_reply unit test illustrates this, and
572 conftest.py has a client with a timeout.
574 See https://redis.io/commands/client-reply
575 """
576 replies = ["ON", "OFF", "SKIP"]
577 if reply not in replies:
578 raise DataError(f"CLIENT REPLY must be one of {replies!r}")
579 return self.execute_command("CLIENT REPLY", reply, **kwargs)
581 def client_id(self, **kwargs) -> ResponseT:
582 """
583 Returns the current connection id
585 For more information see https://redis.io/commands/client-id
586 """
587 return self.execute_command("CLIENT ID", **kwargs)
589 def client_tracking_on(
590 self,
591 clientid: Union[int, None] = None,
592 prefix: Sequence[KeyT] = [],
593 bcast: bool = False,
594 optin: bool = False,
595 optout: bool = False,
596 noloop: bool = False,
597 ) -> ResponseT:
598 """
599 Turn on the tracking mode.
600 For more information about the options look at client_tracking func.
602 See https://redis.io/commands/client-tracking
603 """
604 return self.client_tracking(
605 True, clientid, prefix, bcast, optin, optout, noloop
606 )
608 def client_tracking_off(
609 self,
610 clientid: Union[int, None] = None,
611 prefix: Sequence[KeyT] = [],
612 bcast: bool = False,
613 optin: bool = False,
614 optout: bool = False,
615 noloop: bool = False,
616 ) -> ResponseT:
617 """
618 Turn off the tracking mode.
619 For more information about the options look at client_tracking func.
621 See https://redis.io/commands/client-tracking
622 """
623 return self.client_tracking(
624 False, clientid, prefix, bcast, optin, optout, noloop
625 )
627 def client_tracking(
628 self,
629 on: bool = True,
630 clientid: Union[int, None] = None,
631 prefix: Sequence[KeyT] = [],
632 bcast: bool = False,
633 optin: bool = False,
634 optout: bool = False,
635 noloop: bool = False,
636 **kwargs,
637 ) -> ResponseT:
638 """
639 Enables the tracking feature of the Redis server, that is used
640 for server assisted client side caching.
642 ``on`` indicate for tracking on or tracking off. The dafualt is on.
644 ``clientid`` send invalidation messages to the connection with
645 the specified ID.
647 ``bcast`` enable tracking in broadcasting mode. In this mode
648 invalidation messages are reported for all the prefixes
649 specified, regardless of the keys requested by the connection.
651 ``optin`` when broadcasting is NOT active, normally don't track
652 keys in read only commands, unless they are called immediately
653 after a CLIENT CACHING yes command.
655 ``optout`` when broadcasting is NOT active, normally track keys in
656 read only commands, unless they are called immediately after a
657 CLIENT CACHING no command.
659 ``noloop`` don't send notifications about keys modified by this
660 connection itself.
662 ``prefix`` for broadcasting, register a given key prefix, so that
663 notifications will be provided only for keys starting with this string.
665 See https://redis.io/commands/client-tracking
666 """
668 if len(prefix) != 0 and bcast is False:
669 raise DataError("Prefix can only be used with bcast")
671 pieces = ["ON"] if on else ["OFF"]
672 if clientid is not None:
673 pieces.extend(["REDIRECT", clientid])
674 for p in prefix:
675 pieces.extend(["PREFIX", p])
676 if bcast:
677 pieces.append("BCAST")
678 if optin:
679 pieces.append("OPTIN")
680 if optout:
681 pieces.append("OPTOUT")
682 if noloop:
683 pieces.append("NOLOOP")
685 return self.execute_command("CLIENT TRACKING", *pieces)
687 def client_trackinginfo(self, **kwargs) -> ResponseT:
688 """
689 Returns the information about the current client connection's
690 use of the server assisted client side cache.
692 See https://redis.io/commands/client-trackinginfo
693 """
694 return self.execute_command("CLIENT TRACKINGINFO", **kwargs)
696 def client_setname(self, name: str, **kwargs) -> ResponseT:
697 """
698 Sets the current connection name
700 For more information see https://redis.io/commands/client-setname
702 .. note::
703 This method sets client name only for **current** connection.
705 If you want to set a common name for all connections managed
706 by this client, use ``client_name`` constructor argument.
707 """
708 return self.execute_command("CLIENT SETNAME", name, **kwargs)
710 def client_setinfo(self, attr: str, value: str, **kwargs) -> ResponseT:
711 """
712 Sets the current connection library name or version
713 For mor information see https://redis.io/commands/client-setinfo
714 """
715 return self.execute_command("CLIENT SETINFO", attr, value, **kwargs)
717 def client_unblock(
718 self, client_id: int, error: bool = False, **kwargs
719 ) -> ResponseT:
720 """
721 Unblocks a connection by its client id.
722 If ``error`` is True, unblocks the client with a special error message.
723 If ``error`` is False (default), the client is unblocked using the
724 regular timeout mechanism.
726 For more information see https://redis.io/commands/client-unblock
727 """
728 args = ["CLIENT UNBLOCK", int(client_id)]
729 if error:
730 args.append(b"ERROR")
731 return self.execute_command(*args, **kwargs)
733 def client_pause(self, timeout: int, all: bool = True, **kwargs) -> ResponseT:
734 """
735 Suspend all the Redis clients for the specified amount of time.
738 For more information see https://redis.io/commands/client-pause
740 :param timeout: milliseconds to pause clients
741 :param all: If true (default) all client commands are blocked.
742 otherwise, clients are only blocked if they attempt to execute
743 a write command.
744 For the WRITE mode, some commands have special behavior:
745 EVAL/EVALSHA: Will block client for all scripts.
746 PUBLISH: Will block client.
747 PFCOUNT: Will block client.
748 WAIT: Acknowledgments will be delayed, so this command will
749 appear blocked.
750 """
751 args = ["CLIENT PAUSE", str(timeout)]
752 if not isinstance(timeout, int):
753 raise DataError("CLIENT PAUSE timeout must be an integer")
754 if not all:
755 args.append("WRITE")
756 return self.execute_command(*args, **kwargs)
758 def client_unpause(self, **kwargs) -> ResponseT:
759 """
760 Unpause all redis clients
762 For more information see https://redis.io/commands/client-unpause
763 """
764 return self.execute_command("CLIENT UNPAUSE", **kwargs)
766 def client_no_evict(self, mode: str) -> Union[Awaitable[str], str]:
767 """
768 Sets the client eviction mode for the current connection.
770 For more information see https://redis.io/commands/client-no-evict
771 """
772 return self.execute_command("CLIENT NO-EVICT", mode)
774 def client_no_touch(self, mode: str) -> Union[Awaitable[str], str]:
775 """
776 # The command controls whether commands sent by the client will alter
777 # the LRU/LFU of the keys they access.
778 # When turned on, the current client will not change LFU/LRU stats,
779 # unless it sends the TOUCH command.
781 For more information see https://redis.io/commands/client-no-touch
782 """
783 return self.execute_command("CLIENT NO-TOUCH", mode)
785 def command(self, **kwargs):
786 """
787 Returns dict reply of details about all Redis commands.
789 For more information see https://redis.io/commands/command
790 """
791 return self.execute_command("COMMAND", **kwargs)
793 def command_info(self, **kwargs) -> None:
794 raise NotImplementedError(
795 "COMMAND INFO is intentionally not implemented in the client."
796 )
798 def command_count(self, **kwargs) -> ResponseT:
799 return self.execute_command("COMMAND COUNT", **kwargs)
801 def command_list(
802 self,
803 module: Optional[str] = None,
804 category: Optional[str] = None,
805 pattern: Optional[str] = None,
806 ) -> ResponseT:
807 """
808 Return an array of the server's command names.
809 You can use one of the following filters:
810 ``module``: get the commands that belong to the module
811 ``category``: get the commands in the ACL category
812 ``pattern``: get the commands that match the given pattern
814 For more information see https://redis.io/commands/command-list/
815 """
816 pieces = []
817 if module is not None:
818 pieces.extend(["MODULE", module])
819 if category is not None:
820 pieces.extend(["ACLCAT", category])
821 if pattern is not None:
822 pieces.extend(["PATTERN", pattern])
824 if pieces:
825 pieces.insert(0, "FILTERBY")
827 return self.execute_command("COMMAND LIST", *pieces)
829 def command_getkeysandflags(self, *args: List[str]) -> List[Union[str, List[str]]]:
830 """
831 Returns array of keys from a full Redis command and their usage flags.
833 For more information see https://redis.io/commands/command-getkeysandflags
834 """
835 return self.execute_command("COMMAND GETKEYSANDFLAGS", *args)
837 def command_docs(self, *args):
838 """
839 This function throws a NotImplementedError since it is intentionally
840 not supported.
841 """
842 raise NotImplementedError(
843 "COMMAND DOCS is intentionally not implemented in the client."
844 )
846 def config_get(
847 self, pattern: PatternT = "*", *args: List[PatternT], **kwargs
848 ) -> ResponseT:
849 """
850 Return a dictionary of configuration based on the ``pattern``
852 For more information see https://redis.io/commands/config-get
853 """
854 return self.execute_command("CONFIG GET", pattern, *args, **kwargs)
856 def config_set(
857 self,
858 name: KeyT,
859 value: EncodableT,
860 *args: List[Union[KeyT, EncodableT]],
861 **kwargs,
862 ) -> ResponseT:
863 """Set config item ``name`` with ``value``
865 For more information see https://redis.io/commands/config-set
866 """
867 return self.execute_command("CONFIG SET", name, value, *args, **kwargs)
869 def config_resetstat(self, **kwargs) -> ResponseT:
870 """
871 Reset runtime statistics
873 For more information see https://redis.io/commands/config-resetstat
874 """
875 return self.execute_command("CONFIG RESETSTAT", **kwargs)
877 def config_rewrite(self, **kwargs) -> ResponseT:
878 """
879 Rewrite config file with the minimal change to reflect running config.
881 For more information see https://redis.io/commands/config-rewrite
882 """
883 return self.execute_command("CONFIG REWRITE", **kwargs)
885 def dbsize(self, **kwargs) -> ResponseT:
886 """
887 Returns the number of keys in the current database
889 For more information see https://redis.io/commands/dbsize
890 """
891 return self.execute_command("DBSIZE", **kwargs)
893 def debug_object(self, key: KeyT, **kwargs) -> ResponseT:
894 """
895 Returns version specific meta information about a given key
897 For more information see https://redis.io/commands/debug-object
898 """
899 return self.execute_command("DEBUG OBJECT", key, **kwargs)
901 def debug_segfault(self, **kwargs) -> None:
902 raise NotImplementedError(
903 """
904 DEBUG SEGFAULT is intentionally not implemented in the client.
906 For more information see https://redis.io/commands/debug-segfault
907 """
908 )
910 def echo(self, value: EncodableT, **kwargs) -> ResponseT:
911 """
912 Echo the string back from the server
914 For more information see https://redis.io/commands/echo
915 """
916 return self.execute_command("ECHO", value, **kwargs)
918 def flushall(self, asynchronous: bool = False, **kwargs) -> ResponseT:
919 """
920 Delete all keys in all databases on the current host.
922 ``asynchronous`` indicates whether the operation is
923 executed asynchronously by the server.
925 For more information see https://redis.io/commands/flushall
926 """
927 args = []
928 if asynchronous:
929 args.append(b"ASYNC")
930 return self.execute_command("FLUSHALL", *args, **kwargs)
932 def flushdb(self, asynchronous: bool = False, **kwargs) -> ResponseT:
933 """
934 Delete all keys in the current database.
936 ``asynchronous`` indicates whether the operation is
937 executed asynchronously by the server.
939 For more information see https://redis.io/commands/flushdb
940 """
941 args = []
942 if asynchronous:
943 args.append(b"ASYNC")
944 return self.execute_command("FLUSHDB", *args, **kwargs)
946 def sync(self) -> ResponseT:
947 """
948 Initiates a replication stream from the master.
950 For more information see https://redis.io/commands/sync
951 """
952 from redis.client import NEVER_DECODE
954 options = {}
955 options[NEVER_DECODE] = []
956 return self.execute_command("SYNC", **options)
958 def psync(self, replicationid: str, offset: int):
959 """
960 Initiates a replication stream from the master.
961 Newer version for `sync`.
963 For more information see https://redis.io/commands/sync
964 """
965 from redis.client import NEVER_DECODE
967 options = {}
968 options[NEVER_DECODE] = []
969 return self.execute_command("PSYNC", replicationid, offset, **options)
971 def swapdb(self, first: int, second: int, **kwargs) -> ResponseT:
972 """
973 Swap two databases
975 For more information see https://redis.io/commands/swapdb
976 """
977 return self.execute_command("SWAPDB", first, second, **kwargs)
979 def select(self, index: int, **kwargs) -> ResponseT:
980 """Select the Redis logical database at index.
982 See: https://redis.io/commands/select
983 """
984 return self.execute_command("SELECT", index, **kwargs)
986 def info(
987 self, section: Union[str, None] = None, *args: List[str], **kwargs
988 ) -> ResponseT:
989 """
990 Returns a dictionary containing information about the Redis server
992 The ``section`` option can be used to select a specific section
993 of information
995 The section option is not supported by older versions of Redis Server,
996 and will generate ResponseError
998 For more information see https://redis.io/commands/info
999 """
1000 if section is None:
1001 return self.execute_command("INFO", **kwargs)
1002 else:
1003 return self.execute_command("INFO", section, *args, **kwargs)
1005 def lastsave(self, **kwargs) -> ResponseT:
1006 """
1007 Return a Python datetime object representing the last time the
1008 Redis database was saved to disk
1010 For more information see https://redis.io/commands/lastsave
1011 """
1012 return self.execute_command("LASTSAVE", **kwargs)
1014 def latency_doctor(self):
1015 """Raise a NotImplementedError, as the client will not support LATENCY DOCTOR.
1016 This funcion is best used within the redis-cli.
1018 For more information see https://redis.io/commands/latency-doctor
1019 """
1020 raise NotImplementedError(
1021 """
1022 LATENCY DOCTOR is intentionally not implemented in the client.
1024 For more information see https://redis.io/commands/latency-doctor
1025 """
1026 )
1028 def latency_graph(self):
1029 """Raise a NotImplementedError, as the client will not support LATENCY GRAPH.
1030 This funcion is best used within the redis-cli.
1032 For more information see https://redis.io/commands/latency-graph.
1033 """
1034 raise NotImplementedError(
1035 """
1036 LATENCY GRAPH is intentionally not implemented in the client.
1038 For more information see https://redis.io/commands/latency-graph
1039 """
1040 )
1042 def lolwut(self, *version_numbers: Union[str, float], **kwargs) -> ResponseT:
1043 """
1044 Get the Redis version and a piece of generative computer art
1046 See: https://redis.io/commands/lolwut
1047 """
1048 if version_numbers:
1049 return self.execute_command("LOLWUT VERSION", *version_numbers, **kwargs)
1050 else:
1051 return self.execute_command("LOLWUT", **kwargs)
1053 def reset(self) -> ResponseT:
1054 """Perform a full reset on the connection's server side contenxt.
1056 See: https://redis.io/commands/reset
1057 """
1058 return self.execute_command("RESET")
1060 def migrate(
1061 self,
1062 host: str,
1063 port: int,
1064 keys: KeysT,
1065 destination_db: int,
1066 timeout: int,
1067 copy: bool = False,
1068 replace: bool = False,
1069 auth: Union[str, None] = None,
1070 **kwargs,
1071 ) -> ResponseT:
1072 """
1073 Migrate 1 or more keys from the current Redis server to a different
1074 server specified by the ``host``, ``port`` and ``destination_db``.
1076 The ``timeout``, specified in milliseconds, indicates the maximum
1077 time the connection between the two servers can be idle before the
1078 command is interrupted.
1080 If ``copy`` is True, the specified ``keys`` are NOT deleted from
1081 the source server.
1083 If ``replace`` is True, this operation will overwrite the keys
1084 on the destination server if they exist.
1086 If ``auth`` is specified, authenticate to the destination server with
1087 the password provided.
1089 For more information see https://redis.io/commands/migrate
1090 """
1091 keys = list_or_args(keys, [])
1092 if not keys:
1093 raise DataError("MIGRATE requires at least one key")
1094 pieces = []
1095 if copy:
1096 pieces.append(b"COPY")
1097 if replace:
1098 pieces.append(b"REPLACE")
1099 if auth:
1100 pieces.append(b"AUTH")
1101 pieces.append(auth)
1102 pieces.append(b"KEYS")
1103 pieces.extend(keys)
1104 return self.execute_command(
1105 "MIGRATE", host, port, "", destination_db, timeout, *pieces, **kwargs
1106 )
1108 def object(self, infotype: str, key: KeyT, **kwargs) -> ResponseT:
1109 """
1110 Return the encoding, idletime, or refcount about the key
1111 """
1112 return self.execute_command(
1113 "OBJECT", infotype, key, infotype=infotype, **kwargs
1114 )
1116 def memory_doctor(self, **kwargs) -> None:
1117 raise NotImplementedError(
1118 """
1119 MEMORY DOCTOR is intentionally not implemented in the client.
1121 For more information see https://redis.io/commands/memory-doctor
1122 """
1123 )
1125 def memory_help(self, **kwargs) -> None:
1126 raise NotImplementedError(
1127 """
1128 MEMORY HELP is intentionally not implemented in the client.
1130 For more information see https://redis.io/commands/memory-help
1131 """
1132 )
1134 def memory_stats(self, **kwargs) -> ResponseT:
1135 """
1136 Return a dictionary of memory stats
1138 For more information see https://redis.io/commands/memory-stats
1139 """
1140 return self.execute_command("MEMORY STATS", **kwargs)
1142 def memory_malloc_stats(self, **kwargs) -> ResponseT:
1143 """
1144 Return an internal statistics report from the memory allocator.
1146 See: https://redis.io/commands/memory-malloc-stats
1147 """
1148 return self.execute_command("MEMORY MALLOC-STATS", **kwargs)
1150 def memory_usage(
1151 self, key: KeyT, samples: Union[int, None] = None, **kwargs
1152 ) -> ResponseT:
1153 """
1154 Return the total memory usage for key, its value and associated
1155 administrative overheads.
1157 For nested data structures, ``samples`` is the number of elements to
1158 sample. If left unspecified, the server's default is 5. Use 0 to sample
1159 all elements.
1161 For more information see https://redis.io/commands/memory-usage
1162 """
1163 args = []
1164 if isinstance(samples, int):
1165 args.extend([b"SAMPLES", samples])
1166 return self.execute_command("MEMORY USAGE", key, *args, **kwargs)
1168 def memory_purge(self, **kwargs) -> ResponseT:
1169 """
1170 Attempts to purge dirty pages for reclamation by allocator
1172 For more information see https://redis.io/commands/memory-purge
1173 """
1174 return self.execute_command("MEMORY PURGE", **kwargs)
1176 def latency_histogram(self, *args):
1177 """
1178 This function throws a NotImplementedError since it is intentionally
1179 not supported.
1180 """
1181 raise NotImplementedError(
1182 "LATENCY HISTOGRAM is intentionally not implemented in the client."
1183 )
1185 def latency_history(self, event: str) -> ResponseT:
1186 """
1187 Returns the raw data of the ``event``'s latency spikes time series.
1189 For more information see https://redis.io/commands/latency-history
1190 """
1191 return self.execute_command("LATENCY HISTORY", event)
1193 def latency_latest(self) -> ResponseT:
1194 """
1195 Reports the latest latency events logged.
1197 For more information see https://redis.io/commands/latency-latest
1198 """
1199 return self.execute_command("LATENCY LATEST")
1201 def latency_reset(self, *events: str) -> ResponseT:
1202 """
1203 Resets the latency spikes time series of all, or only some, events.
1205 For more information see https://redis.io/commands/latency-reset
1206 """
1207 return self.execute_command("LATENCY RESET", *events)
1209 def ping(self, **kwargs) -> ResponseT:
1210 """
1211 Ping the Redis server
1213 For more information see https://redis.io/commands/ping
1214 """
1215 return self.execute_command("PING", **kwargs)
1217 def quit(self, **kwargs) -> ResponseT:
1218 """
1219 Ask the server to close the connection.
1221 For more information see https://redis.io/commands/quit
1222 """
1223 return self.execute_command("QUIT", **kwargs)
1225 def replicaof(self, *args, **kwargs) -> ResponseT:
1226 """
1227 Update the replication settings of a redis replica, on the fly.
1229 Examples of valid arguments include:
1231 NO ONE (set no replication)
1232 host port (set to the host and port of a redis server)
1234 For more information see https://redis.io/commands/replicaof
1235 """
1236 return self.execute_command("REPLICAOF", *args, **kwargs)
1238 def save(self, **kwargs) -> ResponseT:
1239 """
1240 Tell the Redis server to save its data to disk,
1241 blocking until the save is complete
1243 For more information see https://redis.io/commands/save
1244 """
1245 return self.execute_command("SAVE", **kwargs)
1247 def shutdown(
1248 self,
1249 save: bool = False,
1250 nosave: bool = False,
1251 now: bool = False,
1252 force: bool = False,
1253 abort: bool = False,
1254 **kwargs,
1255 ) -> None:
1256 """Shutdown the Redis server. If Redis has persistence configured,
1257 data will be flushed before shutdown.
1258 It is possible to specify modifiers to alter the behavior of the command:
1259 ``save`` will force a DB saving operation even if no save points are configured.
1260 ``nosave`` will prevent a DB saving operation even if one or more save points
1261 are configured.
1262 ``now`` skips waiting for lagging replicas, i.e. it bypasses the first step in
1263 the shutdown sequence.
1264 ``force`` ignores any errors that would normally prevent the server from exiting
1265 ``abort`` cancels an ongoing shutdown and cannot be combined with other flags.
1267 For more information see https://redis.io/commands/shutdown
1268 """
1269 if save and nosave:
1270 raise DataError("SHUTDOWN save and nosave cannot both be set")
1271 args = ["SHUTDOWN"]
1272 if save:
1273 args.append("SAVE")
1274 if nosave:
1275 args.append("NOSAVE")
1276 if now:
1277 args.append("NOW")
1278 if force:
1279 args.append("FORCE")
1280 if abort:
1281 args.append("ABORT")
1282 try:
1283 self.execute_command(*args, **kwargs)
1284 except ConnectionError:
1285 # a ConnectionError here is expected
1286 return
1287 raise RedisError("SHUTDOWN seems to have failed.")
1289 def slaveof(
1290 self, host: Union[str, None] = None, port: Union[int, None] = None, **kwargs
1291 ) -> ResponseT:
1292 """
1293 Set the server to be a replicated slave of the instance identified
1294 by the ``host`` and ``port``. If called without arguments, the
1295 instance is promoted to a master instead.
1297 For more information see https://redis.io/commands/slaveof
1298 """
1299 if host is None and port is None:
1300 return self.execute_command("SLAVEOF", b"NO", b"ONE", **kwargs)
1301 return self.execute_command("SLAVEOF", host, port, **kwargs)
1303 def slowlog_get(self, num: Union[int, None] = None, **kwargs) -> ResponseT:
1304 """
1305 Get the entries from the slowlog. If ``num`` is specified, get the
1306 most recent ``num`` items.
1308 For more information see https://redis.io/commands/slowlog-get
1309 """
1310 from redis.client import NEVER_DECODE
1312 args = ["SLOWLOG GET"]
1313 if num is not None:
1314 args.append(num)
1315 decode_responses = self.get_connection_kwargs().get("decode_responses", False)
1316 if decode_responses is True:
1317 kwargs[NEVER_DECODE] = []
1318 return self.execute_command(*args, **kwargs)
1320 def slowlog_len(self, **kwargs) -> ResponseT:
1321 """
1322 Get the number of items in the slowlog
1324 For more information see https://redis.io/commands/slowlog-len
1325 """
1326 return self.execute_command("SLOWLOG LEN", **kwargs)
1328 def slowlog_reset(self, **kwargs) -> ResponseT:
1329 """
1330 Remove all items in the slowlog
1332 For more information see https://redis.io/commands/slowlog-reset
1333 """
1334 return self.execute_command("SLOWLOG RESET", **kwargs)
1336 def time(self, **kwargs) -> ResponseT:
1337 """
1338 Returns the server time as a 2-item tuple of ints:
1339 (seconds since epoch, microseconds into this second).
1341 For more information see https://redis.io/commands/time
1342 """
1343 return self.execute_command("TIME", **kwargs)
1345 def wait(self, num_replicas: int, timeout: int, **kwargs) -> ResponseT:
1346 """
1347 Redis synchronous replication
1348 That returns the number of replicas that processed the query when
1349 we finally have at least ``num_replicas``, or when the ``timeout`` was
1350 reached.
1352 For more information see https://redis.io/commands/wait
1353 """
1354 return self.execute_command("WAIT", num_replicas, timeout, **kwargs)
1356 def waitaof(
1357 self, num_local: int, num_replicas: int, timeout: int, **kwargs
1358 ) -> ResponseT:
1359 """
1360 This command blocks the current client until all previous write
1361 commands by that client are acknowledged as having been fsynced
1362 to the AOF of the local Redis and/or at least the specified number
1363 of replicas.
1365 For more information see https://redis.io/commands/waitaof
1366 """
1367 return self.execute_command(
1368 "WAITAOF", num_local, num_replicas, timeout, **kwargs
1369 )
1371 def hello(self):
1372 """
1373 This function throws a NotImplementedError since it is intentionally
1374 not supported.
1375 """
1376 raise NotImplementedError(
1377 "HELLO is intentionally not implemented in the client."
1378 )
1380 def failover(self):
1381 """
1382 This function throws a NotImplementedError since it is intentionally
1383 not supported.
1384 """
1385 raise NotImplementedError(
1386 "FAILOVER is intentionally not implemented in the client."
1387 )
1390AsyncManagementCommands = ManagementCommands
1393class AsyncManagementCommands(ManagementCommands):
1394 async def command_info(self, **kwargs) -> None:
1395 return super().command_info(**kwargs)
1397 async def debug_segfault(self, **kwargs) -> None:
1398 return super().debug_segfault(**kwargs)
1400 async def memory_doctor(self, **kwargs) -> None:
1401 return super().memory_doctor(**kwargs)
1403 async def memory_help(self, **kwargs) -> None:
1404 return super().memory_help(**kwargs)
1406 async def shutdown(
1407 self,
1408 save: bool = False,
1409 nosave: bool = False,
1410 now: bool = False,
1411 force: bool = False,
1412 abort: bool = False,
1413 **kwargs,
1414 ) -> None:
1415 """Shutdown the Redis server. If Redis has persistence configured,
1416 data will be flushed before shutdown. If the "save" option is set,
1417 a data flush will be attempted even if there is no persistence
1418 configured. If the "nosave" option is set, no data flush will be
1419 attempted. The "save" and "nosave" options cannot both be set.
1421 For more information see https://redis.io/commands/shutdown
1422 """
1423 if save and nosave:
1424 raise DataError("SHUTDOWN save and nosave cannot both be set")
1425 args = ["SHUTDOWN"]
1426 if save:
1427 args.append("SAVE")
1428 if nosave:
1429 args.append("NOSAVE")
1430 if now:
1431 args.append("NOW")
1432 if force:
1433 args.append("FORCE")
1434 if abort:
1435 args.append("ABORT")
1436 try:
1437 await self.execute_command(*args, **kwargs)
1438 except ConnectionError:
1439 # a ConnectionError here is expected
1440 return
1441 raise RedisError("SHUTDOWN seems to have failed.")
1444class BitFieldOperation:
1445 """
1446 Command builder for BITFIELD commands.
1447 """
1449 def __init__(
1450 self,
1451 client: Union["Redis", "AsyncRedis"],
1452 key: str,
1453 default_overflow: Union[str, None] = None,
1454 ):
1455 self.client = client
1456 self.key = key
1457 self._default_overflow = default_overflow
1458 # for typing purposes, run the following in constructor and in reset()
1459 self.operations: list[tuple[EncodableT, ...]] = []
1460 self._last_overflow = "WRAP"
1461 self.reset()
1463 def reset(self):
1464 """
1465 Reset the state of the instance to when it was constructed
1466 """
1467 self.operations = []
1468 self._last_overflow = "WRAP"
1469 self.overflow(self._default_overflow or self._last_overflow)
1471 def overflow(self, overflow: str):
1472 """
1473 Update the overflow algorithm of successive INCRBY operations
1474 :param overflow: Overflow algorithm, one of WRAP, SAT, FAIL. See the
1475 Redis docs for descriptions of these algorithmsself.
1476 :returns: a :py:class:`BitFieldOperation` instance.
1477 """
1478 overflow = overflow.upper()
1479 if overflow != self._last_overflow:
1480 self._last_overflow = overflow
1481 self.operations.append(("OVERFLOW", overflow))
1482 return self
1484 def incrby(
1485 self,
1486 fmt: str,
1487 offset: BitfieldOffsetT,
1488 increment: int,
1489 overflow: Union[str, None] = None,
1490 ):
1491 """
1492 Increment a bitfield by a given amount.
1493 :param fmt: format-string for the bitfield being updated, e.g. 'u8'
1494 for an unsigned 8-bit integer.
1495 :param offset: offset (in number of bits). If prefixed with a
1496 '#', this is an offset multiplier, e.g. given the arguments
1497 fmt='u8', offset='#2', the offset will be 16.
1498 :param int increment: value to increment the bitfield by.
1499 :param str overflow: overflow algorithm. Defaults to WRAP, but other
1500 acceptable values are SAT and FAIL. See the Redis docs for
1501 descriptions of these algorithms.
1502 :returns: a :py:class:`BitFieldOperation` instance.
1503 """
1504 if overflow is not None:
1505 self.overflow(overflow)
1507 self.operations.append(("INCRBY", fmt, offset, increment))
1508 return self
1510 def get(self, fmt: str, offset: BitfieldOffsetT):
1511 """
1512 Get the value of a given bitfield.
1513 :param fmt: format-string for the bitfield being read, e.g. 'u8' for
1514 an unsigned 8-bit integer.
1515 :param offset: offset (in number of bits). If prefixed with a
1516 '#', this is an offset multiplier, e.g. given the arguments
1517 fmt='u8', offset='#2', the offset will be 16.
1518 :returns: a :py:class:`BitFieldOperation` instance.
1519 """
1520 self.operations.append(("GET", fmt, offset))
1521 return self
1523 def set(self, fmt: str, offset: BitfieldOffsetT, value: int):
1524 """
1525 Set the value of a given bitfield.
1526 :param fmt: format-string for the bitfield being read, e.g. 'u8' for
1527 an unsigned 8-bit integer.
1528 :param offset: offset (in number of bits). If prefixed with a
1529 '#', this is an offset multiplier, e.g. given the arguments
1530 fmt='u8', offset='#2', the offset will be 16.
1531 :param int value: value to set at the given position.
1532 :returns: a :py:class:`BitFieldOperation` instance.
1533 """
1534 self.operations.append(("SET", fmt, offset, value))
1535 return self
1537 @property
1538 def command(self):
1539 cmd = ["BITFIELD", self.key]
1540 for ops in self.operations:
1541 cmd.extend(ops)
1542 return cmd
1544 def execute(self) -> ResponseT:
1545 """
1546 Execute the operation(s) in a single BITFIELD command. The return value
1547 is a list of values corresponding to each operation. If the client
1548 used to create this instance was a pipeline, the list of values
1549 will be present within the pipeline's execute.
1550 """
1551 command = self.command
1552 self.reset()
1553 return self.client.execute_command(*command)
1556class BasicKeyCommands(CommandsProtocol):
1557 """
1558 Redis basic key-based commands
1559 """
1561 def append(self, key: KeyT, value: EncodableT) -> ResponseT:
1562 """
1563 Appends the string ``value`` to the value at ``key``. If ``key``
1564 doesn't already exist, create it with a value of ``value``.
1565 Returns the new length of the value at ``key``.
1567 For more information see https://redis.io/commands/append
1568 """
1569 return self.execute_command("APPEND", key, value)
1571 def bitcount(
1572 self,
1573 key: KeyT,
1574 start: Union[int, None] = None,
1575 end: Union[int, None] = None,
1576 mode: Optional[str] = None,
1577 ) -> ResponseT:
1578 """
1579 Returns the count of set bits in the value of ``key``. Optional
1580 ``start`` and ``end`` parameters indicate which bytes to consider
1582 For more information see https://redis.io/commands/bitcount
1583 """
1584 params = [key]
1585 if start is not None and end is not None:
1586 params.append(start)
1587 params.append(end)
1588 elif (start is not None and end is None) or (end is not None and start is None):
1589 raise DataError("Both start and end must be specified")
1590 if mode is not None:
1591 params.append(mode)
1592 return self.execute_command("BITCOUNT", *params, keys=[key])
1594 def bitfield(
1595 self: Union["Redis", "AsyncRedis"],
1596 key: KeyT,
1597 default_overflow: Union[str, None] = None,
1598 ) -> BitFieldOperation:
1599 """
1600 Return a BitFieldOperation instance to conveniently construct one or
1601 more bitfield operations on ``key``.
1603 For more information see https://redis.io/commands/bitfield
1604 """
1605 return BitFieldOperation(self, key, default_overflow=default_overflow)
1607 def bitfield_ro(
1608 self: Union["Redis", "AsyncRedis"],
1609 key: KeyT,
1610 encoding: str,
1611 offset: BitfieldOffsetT,
1612 items: Optional[list] = None,
1613 ) -> ResponseT:
1614 """
1615 Return an array of the specified bitfield values
1616 where the first value is found using ``encoding`` and ``offset``
1617 parameters and remaining values are result of corresponding
1618 encoding/offset pairs in optional list ``items``
1619 Read-only variant of the BITFIELD command.
1621 For more information see https://redis.io/commands/bitfield_ro
1622 """
1623 params = [key, "GET", encoding, offset]
1625 items = items or []
1626 for encoding, offset in items:
1627 params.extend(["GET", encoding, offset])
1628 return self.execute_command("BITFIELD_RO", *params, keys=[key])
1630 def bitop(self, operation: str, dest: KeyT, *keys: KeyT) -> ResponseT:
1631 """
1632 Perform a bitwise operation using ``operation`` between ``keys`` and
1633 store the result in ``dest``.
1635 For more information see https://redis.io/commands/bitop
1636 """
1637 return self.execute_command("BITOP", operation, dest, *keys)
1639 def bitpos(
1640 self,
1641 key: KeyT,
1642 bit: int,
1643 start: Union[int, None] = None,
1644 end: Union[int, None] = None,
1645 mode: Optional[str] = None,
1646 ) -> ResponseT:
1647 """
1648 Return the position of the first bit set to 1 or 0 in a string.
1649 ``start`` and ``end`` defines search range. The range is interpreted
1650 as a range of bytes and not a range of bits, so start=0 and end=2
1651 means to look at the first three bytes.
1653 For more information see https://redis.io/commands/bitpos
1654 """
1655 if bit not in (0, 1):
1656 raise DataError("bit must be 0 or 1")
1657 params = [key, bit]
1659 start is not None and params.append(start)
1661 if start is not None and end is not None:
1662 params.append(end)
1663 elif start is None and end is not None:
1664 raise DataError("start argument is not set, when end is specified")
1666 if mode is not None:
1667 params.append(mode)
1668 return self.execute_command("BITPOS", *params, keys=[key])
1670 def copy(
1671 self,
1672 source: str,
1673 destination: str,
1674 destination_db: Union[str, None] = None,
1675 replace: bool = False,
1676 ) -> ResponseT:
1677 """
1678 Copy the value stored in the ``source`` key to the ``destination`` key.
1680 ``destination_db`` an alternative destination database. By default,
1681 the ``destination`` key is created in the source Redis database.
1683 ``replace`` whether the ``destination`` key should be removed before
1684 copying the value to it. By default, the value is not copied if
1685 the ``destination`` key already exists.
1687 For more information see https://redis.io/commands/copy
1688 """
1689 params = [source, destination]
1690 if destination_db is not None:
1691 params.extend(["DB", destination_db])
1692 if replace:
1693 params.append("REPLACE")
1694 return self.execute_command("COPY", *params)
1696 def decrby(self, name: KeyT, amount: int = 1) -> ResponseT:
1697 """
1698 Decrements the value of ``key`` by ``amount``. If no key exists,
1699 the value will be initialized as 0 - ``amount``
1701 For more information see https://redis.io/commands/decrby
1702 """
1703 return self.execute_command("DECRBY", name, amount)
1705 decr = decrby
1707 def delete(self, *names: KeyT) -> ResponseT:
1708 """
1709 Delete one or more keys specified by ``names``
1710 """
1711 return self.execute_command("DEL", *names)
1713 def __delitem__(self, name: KeyT):
1714 self.delete(name)
1716 def dump(self, name: KeyT) -> ResponseT:
1717 """
1718 Return a serialized version of the value stored at the specified key.
1719 If key does not exist a nil bulk reply is returned.
1721 For more information see https://redis.io/commands/dump
1722 """
1723 from redis.client import NEVER_DECODE
1725 options = {}
1726 options[NEVER_DECODE] = []
1727 return self.execute_command("DUMP", name, **options)
1729 def exists(self, *names: KeyT) -> ResponseT:
1730 """
1731 Returns the number of ``names`` that exist
1733 For more information see https://redis.io/commands/exists
1734 """
1735 return self.execute_command("EXISTS", *names, keys=names)
1737 __contains__ = exists
1739 def expire(
1740 self,
1741 name: KeyT,
1742 time: ExpiryT,
1743 nx: bool = False,
1744 xx: bool = False,
1745 gt: bool = False,
1746 lt: bool = False,
1747 ) -> ResponseT:
1748 """
1749 Set an expire flag on key ``name`` for ``time`` seconds with given
1750 ``option``. ``time`` can be represented by an integer or a Python timedelta
1751 object.
1753 Valid options are:
1754 NX -> Set expiry only when the key has no expiry
1755 XX -> Set expiry only when the key has an existing expiry
1756 GT -> Set expiry only when the new expiry is greater than current one
1757 LT -> Set expiry only when the new expiry is less than current one
1759 For more information see https://redis.io/commands/expire
1760 """
1761 if isinstance(time, datetime.timedelta):
1762 time = int(time.total_seconds())
1764 exp_option = list()
1765 if nx:
1766 exp_option.append("NX")
1767 if xx:
1768 exp_option.append("XX")
1769 if gt:
1770 exp_option.append("GT")
1771 if lt:
1772 exp_option.append("LT")
1774 return self.execute_command("EXPIRE", name, time, *exp_option)
1776 def expireat(
1777 self,
1778 name: KeyT,
1779 when: AbsExpiryT,
1780 nx: bool = False,
1781 xx: bool = False,
1782 gt: bool = False,
1783 lt: bool = False,
1784 ) -> ResponseT:
1785 """
1786 Set an expire flag on key ``name`` with given ``option``. ``when``
1787 can be represented as an integer indicating unix time or a Python
1788 datetime object.
1790 Valid options are:
1791 -> NX -- Set expiry only when the key has no expiry
1792 -> XX -- Set expiry only when the key has an existing expiry
1793 -> GT -- Set expiry only when the new expiry is greater than current one
1794 -> LT -- Set expiry only when the new expiry is less than current one
1796 For more information see https://redis.io/commands/expireat
1797 """
1798 if isinstance(when, datetime.datetime):
1799 when = int(when.timestamp())
1801 exp_option = list()
1802 if nx:
1803 exp_option.append("NX")
1804 if xx:
1805 exp_option.append("XX")
1806 if gt:
1807 exp_option.append("GT")
1808 if lt:
1809 exp_option.append("LT")
1811 return self.execute_command("EXPIREAT", name, when, *exp_option)
1813 def expiretime(self, key: str) -> int:
1814 """
1815 Returns the absolute Unix timestamp (since January 1, 1970) in seconds
1816 at which the given key will expire.
1818 For more information see https://redis.io/commands/expiretime
1819 """
1820 return self.execute_command("EXPIRETIME", key)
1822 def get(self, name: KeyT) -> ResponseT:
1823 """
1824 Return the value at key ``name``, or None if the key doesn't exist
1826 For more information see https://redis.io/commands/get
1827 """
1828 return self.execute_command("GET", name, keys=[name])
1830 def getdel(self, name: KeyT) -> ResponseT:
1831 """
1832 Get the value at key ``name`` and delete the key. This command
1833 is similar to GET, except for the fact that it also deletes
1834 the key on success (if and only if the key's value type
1835 is a string).
1837 For more information see https://redis.io/commands/getdel
1838 """
1839 return self.execute_command("GETDEL", name)
1841 def getex(
1842 self,
1843 name: KeyT,
1844 ex: Union[ExpiryT, None] = None,
1845 px: Union[ExpiryT, None] = None,
1846 exat: Union[AbsExpiryT, None] = None,
1847 pxat: Union[AbsExpiryT, None] = None,
1848 persist: bool = False,
1849 ) -> ResponseT:
1850 """
1851 Get the value of key and optionally set its expiration.
1852 GETEX is similar to GET, but is a write command with
1853 additional options. All time parameters can be given as
1854 datetime.timedelta or integers.
1856 ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
1858 ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
1860 ``exat`` sets an expire flag on key ``name`` for ``ex`` seconds,
1861 specified in unix time.
1863 ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,
1864 specified in unix time.
1866 ``persist`` remove the time to live associated with ``name``.
1868 For more information see https://redis.io/commands/getex
1869 """
1871 opset = {ex, px, exat, pxat}
1872 if len(opset) > 2 or len(opset) > 1 and persist:
1873 raise DataError(
1874 "``ex``, ``px``, ``exat``, ``pxat``, "
1875 "and ``persist`` are mutually exclusive."
1876 )
1878 pieces: list[EncodableT] = []
1879 # similar to set command
1880 if ex is not None:
1881 pieces.append("EX")
1882 if isinstance(ex, datetime.timedelta):
1883 ex = int(ex.total_seconds())
1884 pieces.append(ex)
1885 if px is not None:
1886 pieces.append("PX")
1887 if isinstance(px, datetime.timedelta):
1888 px = int(px.total_seconds() * 1000)
1889 pieces.append(px)
1890 # similar to pexpireat command
1891 if exat is not None:
1892 pieces.append("EXAT")
1893 if isinstance(exat, datetime.datetime):
1894 exat = int(exat.timestamp())
1895 pieces.append(exat)
1896 if pxat is not None:
1897 pieces.append("PXAT")
1898 if isinstance(pxat, datetime.datetime):
1899 pxat = int(pxat.timestamp() * 1000)
1900 pieces.append(pxat)
1901 if persist:
1902 pieces.append("PERSIST")
1904 return self.execute_command("GETEX", name, *pieces)
1906 def __getitem__(self, name: KeyT):
1907 """
1908 Return the value at key ``name``, raises a KeyError if the key
1909 doesn't exist.
1910 """
1911 value = self.get(name)
1912 if value is not None:
1913 return value
1914 raise KeyError(name)
1916 def getbit(self, name: KeyT, offset: int) -> ResponseT:
1917 """
1918 Returns an integer indicating the value of ``offset`` in ``name``
1920 For more information see https://redis.io/commands/getbit
1921 """
1922 return self.execute_command("GETBIT", name, offset, keys=[name])
1924 def getrange(self, key: KeyT, start: int, end: int) -> ResponseT:
1925 """
1926 Returns the substring of the string value stored at ``key``,
1927 determined by the offsets ``start`` and ``end`` (both are inclusive)
1929 For more information see https://redis.io/commands/getrange
1930 """
1931 return self.execute_command("GETRANGE", key, start, end, keys=[key])
1933 def getset(self, name: KeyT, value: EncodableT) -> ResponseT:
1934 """
1935 Sets the value at key ``name`` to ``value``
1936 and returns the old value at key ``name`` atomically.
1938 As per Redis 6.2, GETSET is considered deprecated.
1939 Please use SET with GET parameter in new code.
1941 For more information see https://redis.io/commands/getset
1942 """
1943 return self.execute_command("GETSET", name, value)
1945 def incrby(self, name: KeyT, amount: int = 1) -> ResponseT:
1946 """
1947 Increments the value of ``key`` by ``amount``. If no key exists,
1948 the value will be initialized as ``amount``
1950 For more information see https://redis.io/commands/incrby
1951 """
1952 return self.execute_command("INCRBY", name, amount)
1954 incr = incrby
1956 def incrbyfloat(self, name: KeyT, amount: float = 1.0) -> ResponseT:
1957 """
1958 Increments the value at key ``name`` by floating ``amount``.
1959 If no key exists, the value will be initialized as ``amount``
1961 For more information see https://redis.io/commands/incrbyfloat
1962 """
1963 return self.execute_command("INCRBYFLOAT", name, amount)
1965 def keys(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
1966 """
1967 Returns a list of keys matching ``pattern``
1969 For more information see https://redis.io/commands/keys
1970 """
1971 return self.execute_command("KEYS", pattern, **kwargs)
1973 def lmove(
1974 self, first_list: str, second_list: str, src: str = "LEFT", dest: str = "RIGHT"
1975 ) -> ResponseT:
1976 """
1977 Atomically returns and removes the first/last element of a list,
1978 pushing it as the first/last element on the destination list.
1979 Returns the element being popped and pushed.
1981 For more information see https://redis.io/commands/lmove
1982 """
1983 params = [first_list, second_list, src, dest]
1984 return self.execute_command("LMOVE", *params)
1986 def blmove(
1987 self,
1988 first_list: str,
1989 second_list: str,
1990 timeout: int,
1991 src: str = "LEFT",
1992 dest: str = "RIGHT",
1993 ) -> ResponseT:
1994 """
1995 Blocking version of lmove.
1997 For more information see https://redis.io/commands/blmove
1998 """
1999 params = [first_list, second_list, src, dest, timeout]
2000 return self.execute_command("BLMOVE", *params)
2002 def mget(self, keys: KeysT, *args: EncodableT) -> ResponseT:
2003 """
2004 Returns a list of values ordered identically to ``keys``
2006 For more information see https://redis.io/commands/mget
2007 """
2008 from redis.client import EMPTY_RESPONSE
2010 args = list_or_args(keys, args)
2011 options = {}
2012 if not args:
2013 options[EMPTY_RESPONSE] = []
2014 options["keys"] = args
2015 return self.execute_command("MGET", *args, **options)
2017 def mset(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
2018 """
2019 Sets key/values based on a mapping. Mapping is a dictionary of
2020 key/value pairs. Both keys and values should be strings or types that
2021 can be cast to a string via str().
2023 For more information see https://redis.io/commands/mset
2024 """
2025 items = []
2026 for pair in mapping.items():
2027 items.extend(pair)
2028 return self.execute_command("MSET", *items)
2030 def msetnx(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
2031 """
2032 Sets key/values based on a mapping if none of the keys are already set.
2033 Mapping is a dictionary of key/value pairs. Both keys and values
2034 should be strings or types that can be cast to a string via str().
2035 Returns a boolean indicating if the operation was successful.
2037 For more information see https://redis.io/commands/msetnx
2038 """
2039 items = []
2040 for pair in mapping.items():
2041 items.extend(pair)
2042 return self.execute_command("MSETNX", *items)
2044 def move(self, name: KeyT, db: int) -> ResponseT:
2045 """
2046 Moves the key ``name`` to a different Redis database ``db``
2048 For more information see https://redis.io/commands/move
2049 """
2050 return self.execute_command("MOVE", name, db)
2052 def persist(self, name: KeyT) -> ResponseT:
2053 """
2054 Removes an expiration on ``name``
2056 For more information see https://redis.io/commands/persist
2057 """
2058 return self.execute_command("PERSIST", name)
2060 def pexpire(
2061 self,
2062 name: KeyT,
2063 time: ExpiryT,
2064 nx: bool = False,
2065 xx: bool = False,
2066 gt: bool = False,
2067 lt: bool = False,
2068 ) -> ResponseT:
2069 """
2070 Set an expire flag on key ``name`` for ``time`` milliseconds
2071 with given ``option``. ``time`` can be represented by an
2072 integer or a Python timedelta object.
2074 Valid options are:
2075 NX -> Set expiry only when the key has no expiry
2076 XX -> Set expiry only when the key has an existing expiry
2077 GT -> Set expiry only when the new expiry is greater than current one
2078 LT -> Set expiry only when the new expiry is less than current one
2080 For more information see https://redis.io/commands/pexpire
2081 """
2082 if isinstance(time, datetime.timedelta):
2083 time = int(time.total_seconds() * 1000)
2085 exp_option = list()
2086 if nx:
2087 exp_option.append("NX")
2088 if xx:
2089 exp_option.append("XX")
2090 if gt:
2091 exp_option.append("GT")
2092 if lt:
2093 exp_option.append("LT")
2094 return self.execute_command("PEXPIRE", name, time, *exp_option)
2096 def pexpireat(
2097 self,
2098 name: KeyT,
2099 when: AbsExpiryT,
2100 nx: bool = False,
2101 xx: bool = False,
2102 gt: bool = False,
2103 lt: bool = False,
2104 ) -> ResponseT:
2105 """
2106 Set an expire flag on key ``name`` with given ``option``. ``when``
2107 can be represented as an integer representing unix time in
2108 milliseconds (unix time * 1000) or a Python datetime object.
2110 Valid options are:
2111 NX -> Set expiry only when the key has no expiry
2112 XX -> Set expiry only when the key has an existing expiry
2113 GT -> Set expiry only when the new expiry is greater than current one
2114 LT -> Set expiry only when the new expiry is less than current one
2116 For more information see https://redis.io/commands/pexpireat
2117 """
2118 if isinstance(when, datetime.datetime):
2119 when = int(when.timestamp() * 1000)
2120 exp_option = list()
2121 if nx:
2122 exp_option.append("NX")
2123 if xx:
2124 exp_option.append("XX")
2125 if gt:
2126 exp_option.append("GT")
2127 if lt:
2128 exp_option.append("LT")
2129 return self.execute_command("PEXPIREAT", name, when, *exp_option)
2131 def pexpiretime(self, key: str) -> int:
2132 """
2133 Returns the absolute Unix timestamp (since January 1, 1970) in milliseconds
2134 at which the given key will expire.
2136 For more information see https://redis.io/commands/pexpiretime
2137 """
2138 return self.execute_command("PEXPIRETIME", key)
2140 def psetex(self, name: KeyT, time_ms: ExpiryT, value: EncodableT):
2141 """
2142 Set the value of key ``name`` to ``value`` that expires in ``time_ms``
2143 milliseconds. ``time_ms`` can be represented by an integer or a Python
2144 timedelta object
2146 For more information see https://redis.io/commands/psetex
2147 """
2148 if isinstance(time_ms, datetime.timedelta):
2149 time_ms = int(time_ms.total_seconds() * 1000)
2150 return self.execute_command("PSETEX", name, time_ms, value)
2152 def pttl(self, name: KeyT) -> ResponseT:
2153 """
2154 Returns the number of milliseconds until the key ``name`` will expire
2156 For more information see https://redis.io/commands/pttl
2157 """
2158 return self.execute_command("PTTL", name)
2160 def hrandfield(
2161 self, key: str, count: int = None, withvalues: bool = False
2162 ) -> ResponseT:
2163 """
2164 Return a random field from the hash value stored at key.
2166 count: if the argument is positive, return an array of distinct fields.
2167 If called with a negative count, the behavior changes and the command
2168 is allowed to return the same field multiple times. In this case,
2169 the number of returned fields is the absolute value of the
2170 specified count.
2171 withvalues: The optional WITHVALUES modifier changes the reply so it
2172 includes the respective values of the randomly selected hash fields.
2174 For more information see https://redis.io/commands/hrandfield
2175 """
2176 params = []
2177 if count is not None:
2178 params.append(count)
2179 if withvalues:
2180 params.append("WITHVALUES")
2182 return self.execute_command("HRANDFIELD", key, *params)
2184 def randomkey(self, **kwargs) -> ResponseT:
2185 """
2186 Returns the name of a random key
2188 For more information see https://redis.io/commands/randomkey
2189 """
2190 return self.execute_command("RANDOMKEY", **kwargs)
2192 def rename(self, src: KeyT, dst: KeyT) -> ResponseT:
2193 """
2194 Rename key ``src`` to ``dst``
2196 For more information see https://redis.io/commands/rename
2197 """
2198 return self.execute_command("RENAME", src, dst)
2200 def renamenx(self, src: KeyT, dst: KeyT):
2201 """
2202 Rename key ``src`` to ``dst`` if ``dst`` doesn't already exist
2204 For more information see https://redis.io/commands/renamenx
2205 """
2206 return self.execute_command("RENAMENX", src, dst)
2208 def restore(
2209 self,
2210 name: KeyT,
2211 ttl: float,
2212 value: EncodableT,
2213 replace: bool = False,
2214 absttl: bool = False,
2215 idletime: Union[int, None] = None,
2216 frequency: Union[int, None] = None,
2217 ) -> ResponseT:
2218 """
2219 Create a key using the provided serialized value, previously obtained
2220 using DUMP.
2222 ``replace`` allows an existing key on ``name`` to be overridden. If
2223 it's not specified an error is raised on collision.
2225 ``absttl`` if True, specified ``ttl`` should represent an absolute Unix
2226 timestamp in milliseconds in which the key will expire. (Redis 5.0 or
2227 greater).
2229 ``idletime`` Used for eviction, this is the number of seconds the
2230 key must be idle, prior to execution.
2232 ``frequency`` Used for eviction, this is the frequency counter of
2233 the object stored at the key, prior to execution.
2235 For more information see https://redis.io/commands/restore
2236 """
2237 params = [name, ttl, value]
2238 if replace:
2239 params.append("REPLACE")
2240 if absttl:
2241 params.append("ABSTTL")
2242 if idletime is not None:
2243 params.append("IDLETIME")
2244 try:
2245 params.append(int(idletime))
2246 except ValueError:
2247 raise DataError("idletimemust be an integer")
2249 if frequency is not None:
2250 params.append("FREQ")
2251 try:
2252 params.append(int(frequency))
2253 except ValueError:
2254 raise DataError("frequency must be an integer")
2256 return self.execute_command("RESTORE", *params)
2258 def set(
2259 self,
2260 name: KeyT,
2261 value: EncodableT,
2262 ex: Union[ExpiryT, None] = None,
2263 px: Union[ExpiryT, None] = None,
2264 nx: bool = False,
2265 xx: bool = False,
2266 keepttl: bool = False,
2267 get: bool = False,
2268 exat: Union[AbsExpiryT, None] = None,
2269 pxat: Union[AbsExpiryT, None] = None,
2270 ) -> ResponseT:
2271 """
2272 Set the value at key ``name`` to ``value``
2274 ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
2276 ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
2278 ``nx`` if set to True, set the value at key ``name`` to ``value`` only
2279 if it does not exist.
2281 ``xx`` if set to True, set the value at key ``name`` to ``value`` only
2282 if it already exists.
2284 ``keepttl`` if True, retain the time to live associated with the key.
2285 (Available since Redis 6.0)
2287 ``get`` if True, set the value at key ``name`` to ``value`` and return
2288 the old value stored at key, or None if the key did not exist.
2289 (Available since Redis 6.2)
2291 ``exat`` sets an expire flag on key ``name`` for ``ex`` seconds,
2292 specified in unix time.
2294 ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,
2295 specified in unix time.
2297 For more information see https://redis.io/commands/set
2298 """
2299 pieces: list[EncodableT] = [name, value]
2300 options = {}
2301 if ex is not None:
2302 pieces.append("EX")
2303 if isinstance(ex, datetime.timedelta):
2304 pieces.append(int(ex.total_seconds()))
2305 elif isinstance(ex, int):
2306 pieces.append(ex)
2307 elif isinstance(ex, str) and ex.isdigit():
2308 pieces.append(int(ex))
2309 else:
2310 raise DataError("ex must be datetime.timedelta or int")
2311 if px is not None:
2312 pieces.append("PX")
2313 if isinstance(px, datetime.timedelta):
2314 pieces.append(int(px.total_seconds() * 1000))
2315 elif isinstance(px, int):
2316 pieces.append(px)
2317 else:
2318 raise DataError("px must be datetime.timedelta or int")
2319 if exat is not None:
2320 pieces.append("EXAT")
2321 if isinstance(exat, datetime.datetime):
2322 exat = int(exat.timestamp())
2323 pieces.append(exat)
2324 if pxat is not None:
2325 pieces.append("PXAT")
2326 if isinstance(pxat, datetime.datetime):
2327 pxat = int(pxat.timestamp() * 1000)
2328 pieces.append(pxat)
2329 if keepttl:
2330 pieces.append("KEEPTTL")
2332 if nx:
2333 pieces.append("NX")
2334 if xx:
2335 pieces.append("XX")
2337 if get:
2338 pieces.append("GET")
2339 options["get"] = True
2341 return self.execute_command("SET", *pieces, **options)
2343 def __setitem__(self, name: KeyT, value: EncodableT):
2344 self.set(name, value)
2346 def setbit(self, name: KeyT, offset: int, value: int) -> ResponseT:
2347 """
2348 Flag the ``offset`` in ``name`` as ``value``. Returns an integer
2349 indicating the previous value of ``offset``.
2351 For more information see https://redis.io/commands/setbit
2352 """
2353 value = value and 1 or 0
2354 return self.execute_command("SETBIT", name, offset, value)
2356 def setex(self, name: KeyT, time: ExpiryT, value: EncodableT) -> ResponseT:
2357 """
2358 Set the value of key ``name`` to ``value`` that expires in ``time``
2359 seconds. ``time`` can be represented by an integer or a Python
2360 timedelta object.
2362 For more information see https://redis.io/commands/setex
2363 """
2364 if isinstance(time, datetime.timedelta):
2365 time = int(time.total_seconds())
2366 return self.execute_command("SETEX", name, time, value)
2368 def setnx(self, name: KeyT, value: EncodableT) -> ResponseT:
2369 """
2370 Set the value of key ``name`` to ``value`` if key doesn't exist
2372 For more information see https://redis.io/commands/setnx
2373 """
2374 return self.execute_command("SETNX", name, value)
2376 def setrange(self, name: KeyT, offset: int, value: EncodableT) -> ResponseT:
2377 """
2378 Overwrite bytes in the value of ``name`` starting at ``offset`` with
2379 ``value``. If ``offset`` plus the length of ``value`` exceeds the
2380 length of the original value, the new value will be larger than before.
2381 If ``offset`` exceeds the length of the original value, null bytes
2382 will be used to pad between the end of the previous value and the start
2383 of what's being injected.
2385 Returns the length of the new string.
2387 For more information see https://redis.io/commands/setrange
2388 """
2389 return self.execute_command("SETRANGE", name, offset, value)
2391 def stralgo(
2392 self,
2393 algo: Literal["LCS"],
2394 value1: KeyT,
2395 value2: KeyT,
2396 specific_argument: Union[Literal["strings"], Literal["keys"]] = "strings",
2397 len: bool = False,
2398 idx: bool = False,
2399 minmatchlen: Union[int, None] = None,
2400 withmatchlen: bool = False,
2401 **kwargs,
2402 ) -> ResponseT:
2403 """
2404 Implements complex algorithms that operate on strings.
2405 Right now the only algorithm implemented is the LCS algorithm
2406 (longest common substring). However new algorithms could be
2407 implemented in the future.
2409 ``algo`` Right now must be LCS
2410 ``value1`` and ``value2`` Can be two strings or two keys
2411 ``specific_argument`` Specifying if the arguments to the algorithm
2412 will be keys or strings. strings is the default.
2413 ``len`` Returns just the len of the match.
2414 ``idx`` Returns the match positions in each string.
2415 ``minmatchlen`` Restrict the list of matches to the ones of a given
2416 minimal length. Can be provided only when ``idx`` set to True.
2417 ``withmatchlen`` Returns the matches with the len of the match.
2418 Can be provided only when ``idx`` set to True.
2420 For more information see https://redis.io/commands/stralgo
2421 """
2422 # check validity
2423 supported_algo = ["LCS"]
2424 if algo not in supported_algo:
2425 supported_algos_str = ", ".join(supported_algo)
2426 raise DataError(f"The supported algorithms are: {supported_algos_str}")
2427 if specific_argument not in ["keys", "strings"]:
2428 raise DataError("specific_argument can be only keys or strings")
2429 if len and idx:
2430 raise DataError("len and idx cannot be provided together.")
2432 pieces: list[EncodableT] = [algo, specific_argument.upper(), value1, value2]
2433 if len:
2434 pieces.append(b"LEN")
2435 if idx:
2436 pieces.append(b"IDX")
2437 try:
2438 int(minmatchlen)
2439 pieces.extend([b"MINMATCHLEN", minmatchlen])
2440 except TypeError:
2441 pass
2442 if withmatchlen:
2443 pieces.append(b"WITHMATCHLEN")
2445 return self.execute_command(
2446 "STRALGO",
2447 *pieces,
2448 len=len,
2449 idx=idx,
2450 minmatchlen=minmatchlen,
2451 withmatchlen=withmatchlen,
2452 **kwargs,
2453 )
2455 def strlen(self, name: KeyT) -> ResponseT:
2456 """
2457 Return the number of bytes stored in the value of ``name``
2459 For more information see https://redis.io/commands/strlen
2460 """
2461 return self.execute_command("STRLEN", name, keys=[name])
2463 def substr(self, name: KeyT, start: int, end: int = -1) -> ResponseT:
2464 """
2465 Return a substring of the string at key ``name``. ``start`` and ``end``
2466 are 0-based integers specifying the portion of the string to return.
2467 """
2468 return self.execute_command("SUBSTR", name, start, end, keys=[name])
2470 def touch(self, *args: KeyT) -> ResponseT:
2471 """
2472 Alters the last access time of a key(s) ``*args``. A key is ignored
2473 if it does not exist.
2475 For more information see https://redis.io/commands/touch
2476 """
2477 return self.execute_command("TOUCH", *args)
2479 def ttl(self, name: KeyT) -> ResponseT:
2480 """
2481 Returns the number of seconds until the key ``name`` will expire
2483 For more information see https://redis.io/commands/ttl
2484 """
2485 return self.execute_command("TTL", name)
2487 def type(self, name: KeyT) -> ResponseT:
2488 """
2489 Returns the type of key ``name``
2491 For more information see https://redis.io/commands/type
2492 """
2493 return self.execute_command("TYPE", name, keys=[name])
2495 def watch(self, *names: KeyT) -> None:
2496 """
2497 Watches the values at keys ``names``, or None if the key doesn't exist
2499 For more information see https://redis.io/commands/watch
2500 """
2501 warnings.warn(DeprecationWarning("Call WATCH from a Pipeline object"))
2503 def unwatch(self) -> None:
2504 """
2505 Unwatches the value at key ``name``, or None of the key doesn't exist
2507 For more information see https://redis.io/commands/unwatch
2508 """
2509 warnings.warn(DeprecationWarning("Call UNWATCH from a Pipeline object"))
2511 def unlink(self, *names: KeyT) -> ResponseT:
2512 """
2513 Unlink one or more keys specified by ``names``
2515 For more information see https://redis.io/commands/unlink
2516 """
2517 return self.execute_command("UNLINK", *names)
2519 def lcs(
2520 self,
2521 key1: str,
2522 key2: str,
2523 len: Optional[bool] = False,
2524 idx: Optional[bool] = False,
2525 minmatchlen: Optional[int] = 0,
2526 withmatchlen: Optional[bool] = False,
2527 ) -> Union[str, int, list]:
2528 """
2529 Find the longest common subsequence between ``key1`` and ``key2``.
2530 If ``len`` is true the length of the match will will be returned.
2531 If ``idx`` is true the match position in each strings will be returned.
2532 ``minmatchlen`` restrict the list of matches to the ones of
2533 the given ``minmatchlen``.
2534 If ``withmatchlen`` the length of the match also will be returned.
2535 For more information see https://redis.io/commands/lcs
2536 """
2537 pieces = [key1, key2]
2538 if len:
2539 pieces.append("LEN")
2540 if idx:
2541 pieces.append("IDX")
2542 if minmatchlen != 0:
2543 pieces.extend(["MINMATCHLEN", minmatchlen])
2544 if withmatchlen:
2545 pieces.append("WITHMATCHLEN")
2546 return self.execute_command("LCS", *pieces, keys=[key1, key2])
2549class AsyncBasicKeyCommands(BasicKeyCommands):
2550 def __delitem__(self, name: KeyT):
2551 raise TypeError("Async Redis client does not support class deletion")
2553 def __contains__(self, name: KeyT):
2554 raise TypeError("Async Redis client does not support class inclusion")
2556 def __getitem__(self, name: KeyT):
2557 raise TypeError("Async Redis client does not support class retrieval")
2559 def __setitem__(self, name: KeyT, value: EncodableT):
2560 raise TypeError("Async Redis client does not support class assignment")
2562 async def watch(self, *names: KeyT) -> None:
2563 return super().watch(*names)
2565 async def unwatch(self) -> None:
2566 return super().unwatch()
2569class ListCommands(CommandsProtocol):
2570 """
2571 Redis commands for List data type.
2572 see: https://redis.io/topics/data-types#lists
2573 """
2575 def blpop(
2576 self, keys: List, timeout: Optional[int] = 0
2577 ) -> Union[Awaitable[list], list]:
2578 """
2579 LPOP a value off of the first non-empty list
2580 named in the ``keys`` list.
2582 If none of the lists in ``keys`` has a value to LPOP, then block
2583 for ``timeout`` seconds, or until a value gets pushed on to one
2584 of the lists.
2586 If timeout is 0, then block indefinitely.
2588 For more information see https://redis.io/commands/blpop
2589 """
2590 if timeout is None:
2591 timeout = 0
2592 keys = list_or_args(keys, None)
2593 keys.append(timeout)
2594 return self.execute_command("BLPOP", *keys)
2596 def brpop(
2597 self, keys: List, timeout: Optional[int] = 0
2598 ) -> Union[Awaitable[list], list]:
2599 """
2600 RPOP a value off of the first non-empty list
2601 named in the ``keys`` list.
2603 If none of the lists in ``keys`` has a value to RPOP, then block
2604 for ``timeout`` seconds, or until a value gets pushed on to one
2605 of the lists.
2607 If timeout is 0, then block indefinitely.
2609 For more information see https://redis.io/commands/brpop
2610 """
2611 if timeout is None:
2612 timeout = 0
2613 keys = list_or_args(keys, None)
2614 keys.append(timeout)
2615 return self.execute_command("BRPOP", *keys)
2617 def brpoplpush(
2618 self, src: str, dst: str, timeout: Optional[int] = 0
2619 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
2620 """
2621 Pop a value off the tail of ``src``, push it on the head of ``dst``
2622 and then return it.
2624 This command blocks until a value is in ``src`` or until ``timeout``
2625 seconds elapse, whichever is first. A ``timeout`` value of 0 blocks
2626 forever.
2628 For more information see https://redis.io/commands/brpoplpush
2629 """
2630 if timeout is None:
2631 timeout = 0
2632 return self.execute_command("BRPOPLPUSH", src, dst, timeout)
2634 def blmpop(
2635 self,
2636 timeout: float,
2637 numkeys: int,
2638 *args: List[str],
2639 direction: str,
2640 count: Optional[int] = 1,
2641 ) -> Optional[list]:
2642 """
2643 Pop ``count`` values (default 1) from first non-empty in the list
2644 of provided key names.
2646 When all lists are empty this command blocks the connection until another
2647 client pushes to it or until the timeout, timeout of 0 blocks indefinitely
2649 For more information see https://redis.io/commands/blmpop
2650 """
2651 args = [timeout, numkeys, *args, direction, "COUNT", count]
2653 return self.execute_command("BLMPOP", *args)
2655 def lmpop(
2656 self,
2657 num_keys: int,
2658 *args: List[str],
2659 direction: str,
2660 count: Optional[int] = 1,
2661 ) -> Union[Awaitable[list], list]:
2662 """
2663 Pop ``count`` values (default 1) first non-empty list key from the list
2664 of args provided key names.
2666 For more information see https://redis.io/commands/lmpop
2667 """
2668 args = [num_keys] + list(args) + [direction]
2669 if count != 1:
2670 args.extend(["COUNT", count])
2672 return self.execute_command("LMPOP", *args)
2674 def lindex(
2675 self, name: str, index: int
2676 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
2677 """
2678 Return the item from list ``name`` at position ``index``
2680 Negative indexes are supported and will return an item at the
2681 end of the list
2683 For more information see https://redis.io/commands/lindex
2684 """
2685 return self.execute_command("LINDEX", name, index, keys=[name])
2687 def linsert(
2688 self, name: str, where: str, refvalue: str, value: str
2689 ) -> Union[Awaitable[int], int]:
2690 """
2691 Insert ``value`` in list ``name`` either immediately before or after
2692 [``where``] ``refvalue``
2694 Returns the new length of the list on success or -1 if ``refvalue``
2695 is not in the list.
2697 For more information see https://redis.io/commands/linsert
2698 """
2699 return self.execute_command("LINSERT", name, where, refvalue, value)
2701 def llen(self, name: str) -> Union[Awaitable[int], int]:
2702 """
2703 Return the length of the list ``name``
2705 For more information see https://redis.io/commands/llen
2706 """
2707 return self.execute_command("LLEN", name, keys=[name])
2709 def lpop(
2710 self,
2711 name: str,
2712 count: Optional[int] = None,
2713 ) -> Union[Awaitable[Union[str, List, None]], Union[str, List, None]]:
2714 """
2715 Removes and returns the first elements of the list ``name``.
2717 By default, the command pops a single element from the beginning of
2718 the list. When provided with the optional ``count`` argument, the reply
2719 will consist of up to count elements, depending on the list's length.
2721 For more information see https://redis.io/commands/lpop
2722 """
2723 if count is not None:
2724 return self.execute_command("LPOP", name, count)
2725 else:
2726 return self.execute_command("LPOP", name)
2728 def lpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2729 """
2730 Push ``values`` onto the head of the list ``name``
2732 For more information see https://redis.io/commands/lpush
2733 """
2734 return self.execute_command("LPUSH", name, *values)
2736 def lpushx(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2737 """
2738 Push ``value`` onto the head of the list ``name`` if ``name`` exists
2740 For more information see https://redis.io/commands/lpushx
2741 """
2742 return self.execute_command("LPUSHX", name, *values)
2744 def lrange(self, name: str, start: int, end: int) -> Union[Awaitable[list], list]:
2745 """
2746 Return a slice of the list ``name`` between
2747 position ``start`` and ``end``
2749 ``start`` and ``end`` can be negative numbers just like
2750 Python slicing notation
2752 For more information see https://redis.io/commands/lrange
2753 """
2754 return self.execute_command("LRANGE", name, start, end, keys=[name])
2756 def lrem(self, name: str, count: int, value: str) -> Union[Awaitable[int], int]:
2757 """
2758 Remove the first ``count`` occurrences of elements equal to ``value``
2759 from the list stored at ``name``.
2761 The count argument influences the operation in the following ways:
2762 count > 0: Remove elements equal to value moving from head to tail.
2763 count < 0: Remove elements equal to value moving from tail to head.
2764 count = 0: Remove all elements equal to value.
2766 For more information see https://redis.io/commands/lrem
2767 """
2768 return self.execute_command("LREM", name, count, value)
2770 def lset(self, name: str, index: int, value: str) -> Union[Awaitable[str], str]:
2771 """
2772 Set element at ``index`` of list ``name`` to ``value``
2774 For more information see https://redis.io/commands/lset
2775 """
2776 return self.execute_command("LSET", name, index, value)
2778 def ltrim(self, name: str, start: int, end: int) -> Union[Awaitable[str], str]:
2779 """
2780 Trim the list ``name``, removing all values not within the slice
2781 between ``start`` and ``end``
2783 ``start`` and ``end`` can be negative numbers just like
2784 Python slicing notation
2786 For more information see https://redis.io/commands/ltrim
2787 """
2788 return self.execute_command("LTRIM", name, start, end)
2790 def rpop(
2791 self,
2792 name: str,
2793 count: Optional[int] = None,
2794 ) -> Union[Awaitable[Union[str, List, None]], Union[str, List, None]]:
2795 """
2796 Removes and returns the last elements of the list ``name``.
2798 By default, the command pops a single element from the end of the list.
2799 When provided with the optional ``count`` argument, the reply will
2800 consist of up to count elements, depending on the list's length.
2802 For more information see https://redis.io/commands/rpop
2803 """
2804 if count is not None:
2805 return self.execute_command("RPOP", name, count)
2806 else:
2807 return self.execute_command("RPOP", name)
2809 def rpoplpush(self, src: str, dst: str) -> Union[Awaitable[str], str]:
2810 """
2811 RPOP a value off of the ``src`` list and atomically LPUSH it
2812 on to the ``dst`` list. Returns the value.
2814 For more information see https://redis.io/commands/rpoplpush
2815 """
2816 return self.execute_command("RPOPLPUSH", src, dst)
2818 def rpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2819 """
2820 Push ``values`` onto the tail of the list ``name``
2822 For more information see https://redis.io/commands/rpush
2823 """
2824 return self.execute_command("RPUSH", name, *values)
2826 def rpushx(self, name: str, *values: str) -> Union[Awaitable[int], int]:
2827 """
2828 Push ``value`` onto the tail of the list ``name`` if ``name`` exists
2830 For more information see https://redis.io/commands/rpushx
2831 """
2832 return self.execute_command("RPUSHX", name, *values)
2834 def lpos(
2835 self,
2836 name: str,
2837 value: str,
2838 rank: Optional[int] = None,
2839 count: Optional[int] = None,
2840 maxlen: Optional[int] = None,
2841 ) -> Union[str, List, None]:
2842 """
2843 Get position of ``value`` within the list ``name``
2845 If specified, ``rank`` indicates the "rank" of the first element to
2846 return in case there are multiple copies of ``value`` in the list.
2847 By default, LPOS returns the position of the first occurrence of
2848 ``value`` in the list. When ``rank`` 2, LPOS returns the position of
2849 the second ``value`` in the list. If ``rank`` is negative, LPOS
2850 searches the list in reverse. For example, -1 would return the
2851 position of the last occurrence of ``value`` and -2 would return the
2852 position of the next to last occurrence of ``value``.
2854 If specified, ``count`` indicates that LPOS should return a list of
2855 up to ``count`` positions. A ``count`` of 2 would return a list of
2856 up to 2 positions. A ``count`` of 0 returns a list of all positions
2857 matching ``value``. When ``count`` is specified and but ``value``
2858 does not exist in the list, an empty list is returned.
2860 If specified, ``maxlen`` indicates the maximum number of list
2861 elements to scan. A ``maxlen`` of 1000 will only return the
2862 position(s) of items within the first 1000 entries in the list.
2863 A ``maxlen`` of 0 (the default) will scan the entire list.
2865 For more information see https://redis.io/commands/lpos
2866 """
2867 pieces: list[EncodableT] = [name, value]
2868 if rank is not None:
2869 pieces.extend(["RANK", rank])
2871 if count is not None:
2872 pieces.extend(["COUNT", count])
2874 if maxlen is not None:
2875 pieces.extend(["MAXLEN", maxlen])
2877 return self.execute_command("LPOS", *pieces, keys=[name])
2879 def sort(
2880 self,
2881 name: str,
2882 start: Optional[int] = None,
2883 num: Optional[int] = None,
2884 by: Optional[str] = None,
2885 get: Optional[List[str]] = None,
2886 desc: bool = False,
2887 alpha: bool = False,
2888 store: Optional[str] = None,
2889 groups: Optional[bool] = False,
2890 ) -> Union[List, int]:
2891 """
2892 Sort and return the list, set or sorted set at ``name``.
2894 ``start`` and ``num`` allow for paging through the sorted data
2896 ``by`` allows using an external key to weight and sort the items.
2897 Use an "*" to indicate where in the key the item value is located
2899 ``get`` allows for returning items from external keys rather than the
2900 sorted data itself. Use an "*" to indicate where in the key
2901 the item value is located
2903 ``desc`` allows for reversing the sort
2905 ``alpha`` allows for sorting lexicographically rather than numerically
2907 ``store`` allows for storing the result of the sort into
2908 the key ``store``
2910 ``groups`` if set to True and if ``get`` contains at least two
2911 elements, sort will return a list of tuples, each containing the
2912 values fetched from the arguments to ``get``.
2914 For more information see https://redis.io/commands/sort
2915 """
2916 if (start is not None and num is None) or (num is not None and start is None):
2917 raise DataError("``start`` and ``num`` must both be specified")
2919 pieces: list[EncodableT] = [name]
2920 if by is not None:
2921 pieces.extend([b"BY", by])
2922 if start is not None and num is not None:
2923 pieces.extend([b"LIMIT", start, num])
2924 if get is not None:
2925 # If get is a string assume we want to get a single value.
2926 # Otherwise assume it's an interable and we want to get multiple
2927 # values. We can't just iterate blindly because strings are
2928 # iterable.
2929 if isinstance(get, (bytes, str)):
2930 pieces.extend([b"GET", get])
2931 else:
2932 for g in get:
2933 pieces.extend([b"GET", g])
2934 if desc:
2935 pieces.append(b"DESC")
2936 if alpha:
2937 pieces.append(b"ALPHA")
2938 if store is not None:
2939 pieces.extend([b"STORE", store])
2940 if groups:
2941 if not get or isinstance(get, (bytes, str)) or len(get) < 2:
2942 raise DataError(
2943 'when using "groups" the "get" argument '
2944 "must be specified and contain at least "
2945 "two keys"
2946 )
2948 options = {"groups": len(get) if groups else None}
2949 options["keys"] = [name]
2950 return self.execute_command("SORT", *pieces, **options)
2952 def sort_ro(
2953 self,
2954 key: str,
2955 start: Optional[int] = None,
2956 num: Optional[int] = None,
2957 by: Optional[str] = None,
2958 get: Optional[List[str]] = None,
2959 desc: bool = False,
2960 alpha: bool = False,
2961 ) -> list:
2962 """
2963 Returns the elements contained in the list, set or sorted set at key.
2964 (read-only variant of the SORT command)
2966 ``start`` and ``num`` allow for paging through the sorted data
2968 ``by`` allows using an external key to weight and sort the items.
2969 Use an "*" to indicate where in the key the item value is located
2971 ``get`` allows for returning items from external keys rather than the
2972 sorted data itself. Use an "*" to indicate where in the key
2973 the item value is located
2975 ``desc`` allows for reversing the sort
2977 ``alpha`` allows for sorting lexicographically rather than numerically
2979 For more information see https://redis.io/commands/sort_ro
2980 """
2981 return self.sort(
2982 key, start=start, num=num, by=by, get=get, desc=desc, alpha=alpha
2983 )
2986AsyncListCommands = ListCommands
2989class ScanCommands(CommandsProtocol):
2990 """
2991 Redis SCAN commands.
2992 see: https://redis.io/commands/scan
2993 """
2995 def scan(
2996 self,
2997 cursor: int = 0,
2998 match: Union[PatternT, None] = None,
2999 count: Union[int, None] = None,
3000 _type: Union[str, None] = None,
3001 **kwargs,
3002 ) -> ResponseT:
3003 """
3004 Incrementally return lists of key names. Also return a cursor
3005 indicating the scan position.
3007 ``match`` allows for filtering the keys by pattern
3009 ``count`` provides a hint to Redis about the number of keys to
3010 return per batch.
3012 ``_type`` filters the returned values by a particular Redis type.
3013 Stock Redis instances allow for the following types:
3014 HASH, LIST, SET, STREAM, STRING, ZSET
3015 Additionally, Redis modules can expose other types as well.
3017 For more information see https://redis.io/commands/scan
3018 """
3019 pieces: list[EncodableT] = [cursor]
3020 if match is not None:
3021 pieces.extend([b"MATCH", match])
3022 if count is not None:
3023 pieces.extend([b"COUNT", count])
3024 if _type is not None:
3025 pieces.extend([b"TYPE", _type])
3026 return self.execute_command("SCAN", *pieces, **kwargs)
3028 def scan_iter(
3029 self,
3030 match: Union[PatternT, None] = None,
3031 count: Union[int, None] = None,
3032 _type: Union[str, None] = None,
3033 **kwargs,
3034 ) -> Iterator:
3035 """
3036 Make an iterator using the SCAN command so that the client doesn't
3037 need to remember the cursor position.
3039 ``match`` allows for filtering the keys by pattern
3041 ``count`` provides a hint to Redis about the number of keys to
3042 return per batch.
3044 ``_type`` filters the returned values by a particular Redis type.
3045 Stock Redis instances allow for the following types:
3046 HASH, LIST, SET, STREAM, STRING, ZSET
3047 Additionally, Redis modules can expose other types as well.
3048 """
3049 cursor = "0"
3050 while cursor != 0:
3051 cursor, data = self.scan(
3052 cursor=cursor, match=match, count=count, _type=_type, **kwargs
3053 )
3054 yield from data
3056 def sscan(
3057 self,
3058 name: KeyT,
3059 cursor: int = 0,
3060 match: Union[PatternT, None] = None,
3061 count: Union[int, None] = None,
3062 ) -> ResponseT:
3063 """
3064 Incrementally return lists of elements in a set. Also return a cursor
3065 indicating the scan position.
3067 ``match`` allows for filtering the keys by pattern
3069 ``count`` allows for hint the minimum number of returns
3071 For more information see https://redis.io/commands/sscan
3072 """
3073 pieces: list[EncodableT] = [name, cursor]
3074 if match is not None:
3075 pieces.extend([b"MATCH", match])
3076 if count is not None:
3077 pieces.extend([b"COUNT", count])
3078 return self.execute_command("SSCAN", *pieces)
3080 def sscan_iter(
3081 self,
3082 name: KeyT,
3083 match: Union[PatternT, None] = None,
3084 count: Union[int, None] = None,
3085 ) -> Iterator:
3086 """
3087 Make an iterator using the SSCAN command so that the client doesn't
3088 need to remember the cursor position.
3090 ``match`` allows for filtering the keys by pattern
3092 ``count`` allows for hint the minimum number of returns
3093 """
3094 cursor = "0"
3095 while cursor != 0:
3096 cursor, data = self.sscan(name, cursor=cursor, match=match, count=count)
3097 yield from data
3099 def hscan(
3100 self,
3101 name: KeyT,
3102 cursor: int = 0,
3103 match: Union[PatternT, None] = None,
3104 count: Union[int, None] = None,
3105 ) -> ResponseT:
3106 """
3107 Incrementally return key/value slices in a hash. Also return a cursor
3108 indicating the scan position.
3110 ``match`` allows for filtering the keys by pattern
3112 ``count`` allows for hint the minimum number of returns
3114 For more information see https://redis.io/commands/hscan
3115 """
3116 pieces: list[EncodableT] = [name, cursor]
3117 if match is not None:
3118 pieces.extend([b"MATCH", match])
3119 if count is not None:
3120 pieces.extend([b"COUNT", count])
3121 return self.execute_command("HSCAN", *pieces)
3123 def hscan_iter(
3124 self,
3125 name: str,
3126 match: Union[PatternT, None] = None,
3127 count: Union[int, None] = None,
3128 ) -> Iterator:
3129 """
3130 Make an iterator using the HSCAN command so that the client doesn't
3131 need to remember the cursor position.
3133 ``match`` allows for filtering the keys by pattern
3135 ``count`` allows for hint the minimum number of returns
3136 """
3137 cursor = "0"
3138 while cursor != 0:
3139 cursor, data = self.hscan(name, cursor=cursor, match=match, count=count)
3140 yield from data.items()
3142 def zscan(
3143 self,
3144 name: KeyT,
3145 cursor: int = 0,
3146 match: Union[PatternT, None] = None,
3147 count: Union[int, None] = None,
3148 score_cast_func: Union[type, Callable] = float,
3149 ) -> ResponseT:
3150 """
3151 Incrementally return lists of elements in a sorted set. Also return a
3152 cursor indicating the scan position.
3154 ``match`` allows for filtering the keys by pattern
3156 ``count`` allows for hint the minimum number of returns
3158 ``score_cast_func`` a callable used to cast the score return value
3160 For more information see https://redis.io/commands/zscan
3161 """
3162 pieces = [name, cursor]
3163 if match is not None:
3164 pieces.extend([b"MATCH", match])
3165 if count is not None:
3166 pieces.extend([b"COUNT", count])
3167 options = {"score_cast_func": score_cast_func}
3168 return self.execute_command("ZSCAN", *pieces, **options)
3170 def zscan_iter(
3171 self,
3172 name: KeyT,
3173 match: Union[PatternT, None] = None,
3174 count: Union[int, None] = None,
3175 score_cast_func: Union[type, Callable] = float,
3176 ) -> Iterator:
3177 """
3178 Make an iterator using the ZSCAN command so that the client doesn't
3179 need to remember the cursor position.
3181 ``match`` allows for filtering the keys by pattern
3183 ``count`` allows for hint the minimum number of returns
3185 ``score_cast_func`` a callable used to cast the score return value
3186 """
3187 cursor = "0"
3188 while cursor != 0:
3189 cursor, data = self.zscan(
3190 name,
3191 cursor=cursor,
3192 match=match,
3193 count=count,
3194 score_cast_func=score_cast_func,
3195 )
3196 yield from data
3199class AsyncScanCommands(ScanCommands):
3200 async def scan_iter(
3201 self,
3202 match: Union[PatternT, None] = None,
3203 count: Union[int, None] = None,
3204 _type: Union[str, None] = None,
3205 **kwargs,
3206 ) -> AsyncIterator:
3207 """
3208 Make an iterator using the SCAN command so that the client doesn't
3209 need to remember the cursor position.
3211 ``match`` allows for filtering the keys by pattern
3213 ``count`` provides a hint to Redis about the number of keys to
3214 return per batch.
3216 ``_type`` filters the returned values by a particular Redis type.
3217 Stock Redis instances allow for the following types:
3218 HASH, LIST, SET, STREAM, STRING, ZSET
3219 Additionally, Redis modules can expose other types as well.
3220 """
3221 cursor = "0"
3222 while cursor != 0:
3223 cursor, data = await self.scan(
3224 cursor=cursor, match=match, count=count, _type=_type, **kwargs
3225 )
3226 for d in data:
3227 yield d
3229 async def sscan_iter(
3230 self,
3231 name: KeyT,
3232 match: Union[PatternT, None] = None,
3233 count: Union[int, None] = None,
3234 ) -> AsyncIterator:
3235 """
3236 Make an iterator using the SSCAN command so that the client doesn't
3237 need to remember the cursor position.
3239 ``match`` allows for filtering the keys by pattern
3241 ``count`` allows for hint the minimum number of returns
3242 """
3243 cursor = "0"
3244 while cursor != 0:
3245 cursor, data = await self.sscan(
3246 name, cursor=cursor, match=match, count=count
3247 )
3248 for d in data:
3249 yield d
3251 async def hscan_iter(
3252 self,
3253 name: str,
3254 match: Union[PatternT, None] = None,
3255 count: Union[int, None] = None,
3256 ) -> AsyncIterator:
3257 """
3258 Make an iterator using the HSCAN command so that the client doesn't
3259 need to remember the cursor position.
3261 ``match`` allows for filtering the keys by pattern
3263 ``count`` allows for hint the minimum number of returns
3264 """
3265 cursor = "0"
3266 while cursor != 0:
3267 cursor, data = await self.hscan(
3268 name, cursor=cursor, match=match, count=count
3269 )
3270 for it in data.items():
3271 yield it
3273 async def zscan_iter(
3274 self,
3275 name: KeyT,
3276 match: Union[PatternT, None] = None,
3277 count: Union[int, None] = None,
3278 score_cast_func: Union[type, Callable] = float,
3279 ) -> AsyncIterator:
3280 """
3281 Make an iterator using the ZSCAN command so that the client doesn't
3282 need to remember the cursor position.
3284 ``match`` allows for filtering the keys by pattern
3286 ``count`` allows for hint the minimum number of returns
3288 ``score_cast_func`` a callable used to cast the score return value
3289 """
3290 cursor = "0"
3291 while cursor != 0:
3292 cursor, data = await self.zscan(
3293 name,
3294 cursor=cursor,
3295 match=match,
3296 count=count,
3297 score_cast_func=score_cast_func,
3298 )
3299 for d in data:
3300 yield d
3303class SetCommands(CommandsProtocol):
3304 """
3305 Redis commands for Set data type.
3306 see: https://redis.io/topics/data-types#sets
3307 """
3309 def sadd(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
3310 """
3311 Add ``value(s)`` to set ``name``
3313 For more information see https://redis.io/commands/sadd
3314 """
3315 return self.execute_command("SADD", name, *values)
3317 def scard(self, name: str) -> Union[Awaitable[int], int]:
3318 """
3319 Return the number of elements in set ``name``
3321 For more information see https://redis.io/commands/scard
3322 """
3323 return self.execute_command("SCARD", name, keys=[name])
3325 def sdiff(self, keys: List, *args: List) -> Union[Awaitable[list], list]:
3326 """
3327 Return the difference of sets specified by ``keys``
3329 For more information see https://redis.io/commands/sdiff
3330 """
3331 args = list_or_args(keys, args)
3332 return self.execute_command("SDIFF", *args, keys=args)
3334 def sdiffstore(
3335 self, dest: str, keys: List, *args: List
3336 ) -> Union[Awaitable[int], int]:
3337 """
3338 Store the difference of sets specified by ``keys`` into a new
3339 set named ``dest``. Returns the number of keys in the new set.
3341 For more information see https://redis.io/commands/sdiffstore
3342 """
3343 args = list_or_args(keys, args)
3344 return self.execute_command("SDIFFSTORE", dest, *args)
3346 def sinter(self, keys: List, *args: List) -> Union[Awaitable[list], list]:
3347 """
3348 Return the intersection of sets specified by ``keys``
3350 For more information see https://redis.io/commands/sinter
3351 """
3352 args = list_or_args(keys, args)
3353 return self.execute_command("SINTER", *args, keys=args)
3355 def sintercard(
3356 self, numkeys: int, keys: List[str], limit: int = 0
3357 ) -> Union[Awaitable[int], int]:
3358 """
3359 Return the cardinality of the intersect of multiple sets specified by ``keys`.
3361 When LIMIT provided (defaults to 0 and means unlimited), if the intersection
3362 cardinality reaches limit partway through the computation, the algorithm will
3363 exit and yield limit as the cardinality
3365 For more information see https://redis.io/commands/sintercard
3366 """
3367 args = [numkeys, *keys, "LIMIT", limit]
3368 return self.execute_command("SINTERCARD", *args, keys=keys)
3370 def sinterstore(
3371 self, dest: str, keys: List, *args: List
3372 ) -> Union[Awaitable[int], int]:
3373 """
3374 Store the intersection of sets specified by ``keys`` into a new
3375 set named ``dest``. Returns the number of keys in the new set.
3377 For more information see https://redis.io/commands/sinterstore
3378 """
3379 args = list_or_args(keys, args)
3380 return self.execute_command("SINTERSTORE", dest, *args)
3382 def sismember(
3383 self, name: str, value: str
3384 ) -> Union[Awaitable[Union[Literal[0], Literal[1]]], Union[Literal[0], Literal[1]]]:
3385 """
3386 Return whether ``value`` is a member of set ``name``:
3387 - 1 if the value is a member of the set.
3388 - 0 if the value is not a member of the set or if key does not exist.
3390 For more information see https://redis.io/commands/sismember
3391 """
3392 return self.execute_command("SISMEMBER", name, value, keys=[name])
3394 def smembers(self, name: str) -> Union[Awaitable[Set], Set]:
3395 """
3396 Return all members of the set ``name``
3398 For more information see https://redis.io/commands/smembers
3399 """
3400 return self.execute_command("SMEMBERS", name, keys=[name])
3402 def smismember(self, name: str, values: List, *args: List) -> Union[
3403 Awaitable[List[Union[Literal[0], Literal[1]]]],
3404 List[Union[Literal[0], Literal[1]]],
3405 ]:
3406 """
3407 Return whether each value in ``values`` is a member of the set ``name``
3408 as a list of ``int`` in the order of ``values``:
3409 - 1 if the value is a member of the set.
3410 - 0 if the value is not a member of the set or if key does not exist.
3412 For more information see https://redis.io/commands/smismember
3413 """
3414 args = list_or_args(values, args)
3415 return self.execute_command("SMISMEMBER", name, *args, keys=[name])
3417 def smove(self, src: str, dst: str, value: str) -> Union[Awaitable[bool], bool]:
3418 """
3419 Move ``value`` from set ``src`` to set ``dst`` atomically
3421 For more information see https://redis.io/commands/smove
3422 """
3423 return self.execute_command("SMOVE", src, dst, value)
3425 def spop(self, name: str, count: Optional[int] = None) -> Union[str, List, None]:
3426 """
3427 Remove and return a random member of set ``name``
3429 For more information see https://redis.io/commands/spop
3430 """
3431 args = (count is not None) and [count] or []
3432 return self.execute_command("SPOP", name, *args)
3434 def srandmember(
3435 self, name: str, number: Optional[int] = None
3436 ) -> Union[str, List, None]:
3437 """
3438 If ``number`` is None, returns a random member of set ``name``.
3440 If ``number`` is supplied, returns a list of ``number`` random
3441 members of set ``name``. Note this is only available when running
3442 Redis 2.6+.
3444 For more information see https://redis.io/commands/srandmember
3445 """
3446 args = (number is not None) and [number] or []
3447 return self.execute_command("SRANDMEMBER", name, *args)
3449 def srem(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
3450 """
3451 Remove ``values`` from set ``name``
3453 For more information see https://redis.io/commands/srem
3454 """
3455 return self.execute_command("SREM", name, *values)
3457 def sunion(self, keys: List, *args: List) -> Union[Awaitable[List], List]:
3458 """
3459 Return the union of sets specified by ``keys``
3461 For more information see https://redis.io/commands/sunion
3462 """
3463 args = list_or_args(keys, args)
3464 return self.execute_command("SUNION", *args, keys=args)
3466 def sunionstore(
3467 self, dest: str, keys: List, *args: List
3468 ) -> Union[Awaitable[int], int]:
3469 """
3470 Store the union of sets specified by ``keys`` into a new
3471 set named ``dest``. Returns the number of keys in the new set.
3473 For more information see https://redis.io/commands/sunionstore
3474 """
3475 args = list_or_args(keys, args)
3476 return self.execute_command("SUNIONSTORE", dest, *args)
3479AsyncSetCommands = SetCommands
3482class StreamCommands(CommandsProtocol):
3483 """
3484 Redis commands for Stream data type.
3485 see: https://redis.io/topics/streams-intro
3486 """
3488 def xack(self, name: KeyT, groupname: GroupT, *ids: StreamIdT) -> ResponseT:
3489 """
3490 Acknowledges the successful processing of one or more messages.
3491 name: name of the stream.
3492 groupname: name of the consumer group.
3493 *ids: message ids to acknowledge.
3495 For more information see https://redis.io/commands/xack
3496 """
3497 return self.execute_command("XACK", name, groupname, *ids)
3499 def xadd(
3500 self,
3501 name: KeyT,
3502 fields: Dict[FieldT, EncodableT],
3503 id: StreamIdT = "*",
3504 maxlen: Union[int, None] = None,
3505 approximate: bool = True,
3506 nomkstream: bool = False,
3507 minid: Union[StreamIdT, None] = None,
3508 limit: Union[int, None] = None,
3509 ) -> ResponseT:
3510 """
3511 Add to a stream.
3512 name: name of the stream
3513 fields: dict of field/value pairs to insert into the stream
3514 id: Location to insert this record. By default it is appended.
3515 maxlen: truncate old stream members beyond this size.
3516 Can't be specified with minid.
3517 approximate: actual stream length may be slightly more than maxlen
3518 nomkstream: When set to true, do not make a stream
3519 minid: the minimum id in the stream to query.
3520 Can't be specified with maxlen.
3521 limit: specifies the maximum number of entries to retrieve
3523 For more information see https://redis.io/commands/xadd
3524 """
3525 pieces: list[EncodableT] = []
3526 if maxlen is not None and minid is not None:
3527 raise DataError("Only one of ```maxlen``` or ```minid``` may be specified")
3529 if maxlen is not None:
3530 if not isinstance(maxlen, int) or maxlen < 0:
3531 raise DataError("XADD maxlen must be non-negative integer")
3532 pieces.append(b"MAXLEN")
3533 if approximate:
3534 pieces.append(b"~")
3535 pieces.append(str(maxlen))
3536 if minid is not None:
3537 pieces.append(b"MINID")
3538 if approximate:
3539 pieces.append(b"~")
3540 pieces.append(minid)
3541 if limit is not None:
3542 pieces.extend([b"LIMIT", limit])
3543 if nomkstream:
3544 pieces.append(b"NOMKSTREAM")
3545 pieces.append(id)
3546 if not isinstance(fields, dict) or len(fields) == 0:
3547 raise DataError("XADD fields must be a non-empty dict")
3548 for pair in fields.items():
3549 pieces.extend(pair)
3550 return self.execute_command("XADD", name, *pieces)
3552 def xautoclaim(
3553 self,
3554 name: KeyT,
3555 groupname: GroupT,
3556 consumername: ConsumerT,
3557 min_idle_time: int,
3558 start_id: StreamIdT = "0-0",
3559 count: Union[int, None] = None,
3560 justid: bool = False,
3561 ) -> ResponseT:
3562 """
3563 Transfers ownership of pending stream entries that match the specified
3564 criteria. Conceptually, equivalent to calling XPENDING and then XCLAIM,
3565 but provides a more straightforward way to deal with message delivery
3566 failures via SCAN-like semantics.
3567 name: name of the stream.
3568 groupname: name of the consumer group.
3569 consumername: name of a consumer that claims the message.
3570 min_idle_time: filter messages that were idle less than this amount of
3571 milliseconds.
3572 start_id: filter messages with equal or greater ID.
3573 count: optional integer, upper limit of the number of entries that the
3574 command attempts to claim. Set to 100 by default.
3575 justid: optional boolean, false by default. Return just an array of IDs
3576 of messages successfully claimed, without returning the actual message
3578 For more information see https://redis.io/commands/xautoclaim
3579 """
3580 try:
3581 if int(min_idle_time) < 0:
3582 raise DataError(
3583 "XAUTOCLAIM min_idle_time must be a nonnegative integer"
3584 )
3585 except TypeError:
3586 pass
3588 kwargs = {}
3589 pieces = [name, groupname, consumername, min_idle_time, start_id]
3591 try:
3592 if int(count) < 0:
3593 raise DataError("XPENDING count must be a integer >= 0")
3594 pieces.extend([b"COUNT", count])
3595 except TypeError:
3596 pass
3597 if justid:
3598 pieces.append(b"JUSTID")
3599 kwargs["parse_justid"] = True
3601 return self.execute_command("XAUTOCLAIM", *pieces, **kwargs)
3603 def xclaim(
3604 self,
3605 name: KeyT,
3606 groupname: GroupT,
3607 consumername: ConsumerT,
3608 min_idle_time: int,
3609 message_ids: Union[List[StreamIdT], Tuple[StreamIdT]],
3610 idle: Union[int, None] = None,
3611 time: Union[int, None] = None,
3612 retrycount: Union[int, None] = None,
3613 force: bool = False,
3614 justid: bool = False,
3615 ) -> ResponseT:
3616 """
3617 Changes the ownership of a pending message.
3619 name: name of the stream.
3621 groupname: name of the consumer group.
3623 consumername: name of a consumer that claims the message.
3625 min_idle_time: filter messages that were idle less than this amount of
3626 milliseconds
3628 message_ids: non-empty list or tuple of message IDs to claim
3630 idle: optional. Set the idle time (last time it was delivered) of the
3631 message in ms
3633 time: optional integer. This is the same as idle but instead of a
3634 relative amount of milliseconds, it sets the idle time to a specific
3635 Unix time (in milliseconds).
3637 retrycount: optional integer. set the retry counter to the specified
3638 value. This counter is incremented every time a message is delivered
3639 again.
3641 force: optional boolean, false by default. Creates the pending message
3642 entry in the PEL even if certain specified IDs are not already in the
3643 PEL assigned to a different client.
3645 justid: optional boolean, false by default. Return just an array of IDs
3646 of messages successfully claimed, without returning the actual message
3648 For more information see https://redis.io/commands/xclaim
3649 """
3650 if not isinstance(min_idle_time, int) or min_idle_time < 0:
3651 raise DataError("XCLAIM min_idle_time must be a non negative integer")
3652 if not isinstance(message_ids, (list, tuple)) or not message_ids:
3653 raise DataError(
3654 "XCLAIM message_ids must be a non empty list or "
3655 "tuple of message IDs to claim"
3656 )
3658 kwargs = {}
3659 pieces: list[EncodableT] = [name, groupname, consumername, str(min_idle_time)]
3660 pieces.extend(list(message_ids))
3662 if idle is not None:
3663 if not isinstance(idle, int):
3664 raise DataError("XCLAIM idle must be an integer")
3665 pieces.extend((b"IDLE", str(idle)))
3666 if time is not None:
3667 if not isinstance(time, int):
3668 raise DataError("XCLAIM time must be an integer")
3669 pieces.extend((b"TIME", str(time)))
3670 if retrycount is not None:
3671 if not isinstance(retrycount, int):
3672 raise DataError("XCLAIM retrycount must be an integer")
3673 pieces.extend((b"RETRYCOUNT", str(retrycount)))
3675 if force:
3676 if not isinstance(force, bool):
3677 raise DataError("XCLAIM force must be a boolean")
3678 pieces.append(b"FORCE")
3679 if justid:
3680 if not isinstance(justid, bool):
3681 raise DataError("XCLAIM justid must be a boolean")
3682 pieces.append(b"JUSTID")
3683 kwargs["parse_justid"] = True
3684 return self.execute_command("XCLAIM", *pieces, **kwargs)
3686 def xdel(self, name: KeyT, *ids: StreamIdT) -> ResponseT:
3687 """
3688 Deletes one or more messages from a stream.
3689 name: name of the stream.
3690 *ids: message ids to delete.
3692 For more information see https://redis.io/commands/xdel
3693 """
3694 return self.execute_command("XDEL", name, *ids)
3696 def xgroup_create(
3697 self,
3698 name: KeyT,
3699 groupname: GroupT,
3700 id: StreamIdT = "$",
3701 mkstream: bool = False,
3702 entries_read: Optional[int] = None,
3703 ) -> ResponseT:
3704 """
3705 Create a new consumer group associated with a stream.
3706 name: name of the stream.
3707 groupname: name of the consumer group.
3708 id: ID of the last item in the stream to consider already delivered.
3710 For more information see https://redis.io/commands/xgroup-create
3711 """
3712 pieces: list[EncodableT] = ["XGROUP CREATE", name, groupname, id]
3713 if mkstream:
3714 pieces.append(b"MKSTREAM")
3715 if entries_read is not None:
3716 pieces.extend(["ENTRIESREAD", entries_read])
3718 return self.execute_command(*pieces)
3720 def xgroup_delconsumer(
3721 self, name: KeyT, groupname: GroupT, consumername: ConsumerT
3722 ) -> ResponseT:
3723 """
3724 Remove a specific consumer from a consumer group.
3725 Returns the number of pending messages that the consumer had before it
3726 was deleted.
3727 name: name of the stream.
3728 groupname: name of the consumer group.
3729 consumername: name of consumer to delete
3731 For more information see https://redis.io/commands/xgroup-delconsumer
3732 """
3733 return self.execute_command("XGROUP DELCONSUMER", name, groupname, consumername)
3735 def xgroup_destroy(self, name: KeyT, groupname: GroupT) -> ResponseT:
3736 """
3737 Destroy a consumer group.
3738 name: name of the stream.
3739 groupname: name of the consumer group.
3741 For more information see https://redis.io/commands/xgroup-destroy
3742 """
3743 return self.execute_command("XGROUP DESTROY", name, groupname)
3745 def xgroup_createconsumer(
3746 self, name: KeyT, groupname: GroupT, consumername: ConsumerT
3747 ) -> ResponseT:
3748 """
3749 Consumers in a consumer group are auto-created every time a new
3750 consumer name is mentioned by some command.
3751 They can be explicitly created by using this command.
3752 name: name of the stream.
3753 groupname: name of the consumer group.
3754 consumername: name of consumer to create.
3756 See: https://redis.io/commands/xgroup-createconsumer
3757 """
3758 return self.execute_command(
3759 "XGROUP CREATECONSUMER", name, groupname, consumername
3760 )
3762 def xgroup_setid(
3763 self,
3764 name: KeyT,
3765 groupname: GroupT,
3766 id: StreamIdT,
3767 entries_read: Optional[int] = None,
3768 ) -> ResponseT:
3769 """
3770 Set the consumer group last delivered ID to something else.
3771 name: name of the stream.
3772 groupname: name of the consumer group.
3773 id: ID of the last item in the stream to consider already delivered.
3775 For more information see https://redis.io/commands/xgroup-setid
3776 """
3777 pieces = [name, groupname, id]
3778 if entries_read is not None:
3779 pieces.extend(["ENTRIESREAD", entries_read])
3780 return self.execute_command("XGROUP SETID", *pieces)
3782 def xinfo_consumers(self, name: KeyT, groupname: GroupT) -> ResponseT:
3783 """
3784 Returns general information about the consumers in the group.
3785 name: name of the stream.
3786 groupname: name of the consumer group.
3788 For more information see https://redis.io/commands/xinfo-consumers
3789 """
3790 return self.execute_command("XINFO CONSUMERS", name, groupname)
3792 def xinfo_groups(self, name: KeyT) -> ResponseT:
3793 """
3794 Returns general information about the consumer groups of the stream.
3795 name: name of the stream.
3797 For more information see https://redis.io/commands/xinfo-groups
3798 """
3799 return self.execute_command("XINFO GROUPS", name)
3801 def xinfo_stream(self, name: KeyT, full: bool = False) -> ResponseT:
3802 """
3803 Returns general information about the stream.
3804 name: name of the stream.
3805 full: optional boolean, false by default. Return full summary
3807 For more information see https://redis.io/commands/xinfo-stream
3808 """
3809 pieces = [name]
3810 options = {}
3811 if full:
3812 pieces.append(b"FULL")
3813 options = {"full": full}
3814 return self.execute_command("XINFO STREAM", *pieces, **options)
3816 def xlen(self, name: KeyT) -> ResponseT:
3817 """
3818 Returns the number of elements in a given stream.
3820 For more information see https://redis.io/commands/xlen
3821 """
3822 return self.execute_command("XLEN", name, keys=[name])
3824 def xpending(self, name: KeyT, groupname: GroupT) -> ResponseT:
3825 """
3826 Returns information about pending messages of a group.
3827 name: name of the stream.
3828 groupname: name of the consumer group.
3830 For more information see https://redis.io/commands/xpending
3831 """
3832 return self.execute_command("XPENDING", name, groupname, keys=[name])
3834 def xpending_range(
3835 self,
3836 name: KeyT,
3837 groupname: GroupT,
3838 min: StreamIdT,
3839 max: StreamIdT,
3840 count: int,
3841 consumername: Union[ConsumerT, None] = None,
3842 idle: Union[int, None] = None,
3843 ) -> ResponseT:
3844 """
3845 Returns information about pending messages, in a range.
3847 name: name of the stream.
3848 groupname: name of the consumer group.
3849 idle: available from version 6.2. filter entries by their
3850 idle-time, given in milliseconds (optional).
3851 min: minimum stream ID.
3852 max: maximum stream ID.
3853 count: number of messages to return
3854 consumername: name of a consumer to filter by (optional).
3855 """
3856 if {min, max, count} == {None}:
3857 if idle is not None or consumername is not None:
3858 raise DataError(
3859 "if XPENDING is provided with idle time"
3860 " or consumername, it must be provided"
3861 " with min, max and count parameters"
3862 )
3863 return self.xpending(name, groupname)
3865 pieces = [name, groupname]
3866 if min is None or max is None or count is None:
3867 raise DataError(
3868 "XPENDING must be provided with min, max "
3869 "and count parameters, or none of them."
3870 )
3871 # idle
3872 try:
3873 if int(idle) < 0:
3874 raise DataError("XPENDING idle must be a integer >= 0")
3875 pieces.extend(["IDLE", idle])
3876 except TypeError:
3877 pass
3878 # count
3879 try:
3880 if int(count) < 0:
3881 raise DataError("XPENDING count must be a integer >= 0")
3882 pieces.extend([min, max, count])
3883 except TypeError:
3884 pass
3885 # consumername
3886 if consumername:
3887 pieces.append(consumername)
3889 return self.execute_command("XPENDING", *pieces, parse_detail=True)
3891 def xrange(
3892 self,
3893 name: KeyT,
3894 min: StreamIdT = "-",
3895 max: StreamIdT = "+",
3896 count: Union[int, None] = None,
3897 ) -> ResponseT:
3898 """
3899 Read stream values within an interval.
3901 name: name of the stream.
3903 start: first stream ID. defaults to '-',
3904 meaning the earliest available.
3906 finish: last stream ID. defaults to '+',
3907 meaning the latest available.
3909 count: if set, only return this many items, beginning with the
3910 earliest available.
3912 For more information see https://redis.io/commands/xrange
3913 """
3914 pieces = [min, max]
3915 if count is not None:
3916 if not isinstance(count, int) or count < 1:
3917 raise DataError("XRANGE count must be a positive integer")
3918 pieces.append(b"COUNT")
3919 pieces.append(str(count))
3921 return self.execute_command("XRANGE", name, *pieces, keys=[name])
3923 def xread(
3924 self,
3925 streams: Dict[KeyT, StreamIdT],
3926 count: Union[int, None] = None,
3927 block: Union[int, None] = None,
3928 ) -> ResponseT:
3929 """
3930 Block and monitor multiple streams for new data.
3932 streams: a dict of stream names to stream IDs, where
3933 IDs indicate the last ID already seen.
3935 count: if set, only return this many items, beginning with the
3936 earliest available.
3938 block: number of milliseconds to wait, if nothing already present.
3940 For more information see https://redis.io/commands/xread
3941 """
3942 pieces = []
3943 if block is not None:
3944 if not isinstance(block, int) or block < 0:
3945 raise DataError("XREAD block must be a non-negative integer")
3946 pieces.append(b"BLOCK")
3947 pieces.append(str(block))
3948 if count is not None:
3949 if not isinstance(count, int) or count < 1:
3950 raise DataError("XREAD count must be a positive integer")
3951 pieces.append(b"COUNT")
3952 pieces.append(str(count))
3953 if not isinstance(streams, dict) or len(streams) == 0:
3954 raise DataError("XREAD streams must be a non empty dict")
3955 pieces.append(b"STREAMS")
3956 keys, values = zip(*streams.items())
3957 pieces.extend(keys)
3958 pieces.extend(values)
3959 return self.execute_command("XREAD", *pieces, keys=keys)
3961 def xreadgroup(
3962 self,
3963 groupname: str,
3964 consumername: str,
3965 streams: Dict[KeyT, StreamIdT],
3966 count: Union[int, None] = None,
3967 block: Union[int, None] = None,
3968 noack: bool = False,
3969 ) -> ResponseT:
3970 """
3971 Read from a stream via a consumer group.
3973 groupname: name of the consumer group.
3975 consumername: name of the requesting consumer.
3977 streams: a dict of stream names to stream IDs, where
3978 IDs indicate the last ID already seen.
3980 count: if set, only return this many items, beginning with the
3981 earliest available.
3983 block: number of milliseconds to wait, if nothing already present.
3984 noack: do not add messages to the PEL
3986 For more information see https://redis.io/commands/xreadgroup
3987 """
3988 pieces: list[EncodableT] = [b"GROUP", groupname, consumername]
3989 if count is not None:
3990 if not isinstance(count, int) or count < 1:
3991 raise DataError("XREADGROUP count must be a positive integer")
3992 pieces.append(b"COUNT")
3993 pieces.append(str(count))
3994 if block is not None:
3995 if not isinstance(block, int) or block < 0:
3996 raise DataError("XREADGROUP block must be a non-negative integer")
3997 pieces.append(b"BLOCK")
3998 pieces.append(str(block))
3999 if noack:
4000 pieces.append(b"NOACK")
4001 if not isinstance(streams, dict) or len(streams) == 0:
4002 raise DataError("XREADGROUP streams must be a non empty dict")
4003 pieces.append(b"STREAMS")
4004 pieces.extend(streams.keys())
4005 pieces.extend(streams.values())
4006 return self.execute_command("XREADGROUP", *pieces)
4008 def xrevrange(
4009 self,
4010 name: KeyT,
4011 max: StreamIdT = "+",
4012 min: StreamIdT = "-",
4013 count: Union[int, None] = None,
4014 ) -> ResponseT:
4015 """
4016 Read stream values within an interval, in reverse order.
4018 name: name of the stream
4020 start: first stream ID. defaults to '+',
4021 meaning the latest available.
4023 finish: last stream ID. defaults to '-',
4024 meaning the earliest available.
4026 count: if set, only return this many items, beginning with the
4027 latest available.
4029 For more information see https://redis.io/commands/xrevrange
4030 """
4031 pieces: list[EncodableT] = [max, min]
4032 if count is not None:
4033 if not isinstance(count, int) or count < 1:
4034 raise DataError("XREVRANGE count must be a positive integer")
4035 pieces.append(b"COUNT")
4036 pieces.append(str(count))
4038 return self.execute_command("XREVRANGE", name, *pieces, keys=[name])
4040 def xtrim(
4041 self,
4042 name: KeyT,
4043 maxlen: Union[int, None] = None,
4044 approximate: bool = True,
4045 minid: Union[StreamIdT, None] = None,
4046 limit: Union[int, None] = None,
4047 ) -> ResponseT:
4048 """
4049 Trims old messages from a stream.
4050 name: name of the stream.
4051 maxlen: truncate old stream messages beyond this size
4052 Can't be specified with minid.
4053 approximate: actual stream length may be slightly more than maxlen
4054 minid: the minimum id in the stream to query
4055 Can't be specified with maxlen.
4056 limit: specifies the maximum number of entries to retrieve
4058 For more information see https://redis.io/commands/xtrim
4059 """
4060 pieces: list[EncodableT] = []
4061 if maxlen is not None and minid is not None:
4062 raise DataError("Only one of ``maxlen`` or ``minid`` may be specified")
4064 if maxlen is None and minid is None:
4065 raise DataError("One of ``maxlen`` or ``minid`` must be specified")
4067 if maxlen is not None:
4068 pieces.append(b"MAXLEN")
4069 if minid is not None:
4070 pieces.append(b"MINID")
4071 if approximate:
4072 pieces.append(b"~")
4073 if maxlen is not None:
4074 pieces.append(maxlen)
4075 if minid is not None:
4076 pieces.append(minid)
4077 if limit is not None:
4078 pieces.append(b"LIMIT")
4079 pieces.append(limit)
4081 return self.execute_command("XTRIM", name, *pieces)
4084AsyncStreamCommands = StreamCommands
4087class SortedSetCommands(CommandsProtocol):
4088 """
4089 Redis commands for Sorted Sets data type.
4090 see: https://redis.io/topics/data-types-intro#redis-sorted-sets
4091 """
4093 def zadd(
4094 self,
4095 name: KeyT,
4096 mapping: Mapping[AnyKeyT, EncodableT],
4097 nx: bool = False,
4098 xx: bool = False,
4099 ch: bool = False,
4100 incr: bool = False,
4101 gt: bool = False,
4102 lt: bool = False,
4103 ) -> ResponseT:
4104 """
4105 Set any number of element-name, score pairs to the key ``name``. Pairs
4106 are specified as a dict of element-names keys to score values.
4108 ``nx`` forces ZADD to only create new elements and not to update
4109 scores for elements that already exist.
4111 ``xx`` forces ZADD to only update scores of elements that already
4112 exist. New elements will not be added.
4114 ``ch`` modifies the return value to be the numbers of elements changed.
4115 Changed elements include new elements that were added and elements
4116 whose scores changed.
4118 ``incr`` modifies ZADD to behave like ZINCRBY. In this mode only a
4119 single element/score pair can be specified and the score is the amount
4120 the existing score will be incremented by. When using this mode the
4121 return value of ZADD will be the new score of the element.
4123 ``LT`` Only update existing elements if the new score is less than
4124 the current score. This flag doesn't prevent adding new elements.
4126 ``GT`` Only update existing elements if the new score is greater than
4127 the current score. This flag doesn't prevent adding new elements.
4129 The return value of ZADD varies based on the mode specified. With no
4130 options, ZADD returns the number of new elements added to the sorted
4131 set.
4133 ``NX``, ``LT``, and ``GT`` are mutually exclusive options.
4135 See: https://redis.io/commands/ZADD
4136 """
4137 if not mapping:
4138 raise DataError("ZADD requires at least one element/score pair")
4139 if nx and xx:
4140 raise DataError("ZADD allows either 'nx' or 'xx', not both")
4141 if gt and lt:
4142 raise DataError("ZADD allows either 'gt' or 'lt', not both")
4143 if incr and len(mapping) != 1:
4144 raise DataError(
4145 "ZADD option 'incr' only works when passing a "
4146 "single element/score pair"
4147 )
4148 if nx and (gt or lt):
4149 raise DataError("Only one of 'nx', 'lt', or 'gr' may be defined.")
4151 pieces: list[EncodableT] = []
4152 options = {}
4153 if nx:
4154 pieces.append(b"NX")
4155 if xx:
4156 pieces.append(b"XX")
4157 if ch:
4158 pieces.append(b"CH")
4159 if incr:
4160 pieces.append(b"INCR")
4161 options["as_score"] = True
4162 if gt:
4163 pieces.append(b"GT")
4164 if lt:
4165 pieces.append(b"LT")
4166 for pair in mapping.items():
4167 pieces.append(pair[1])
4168 pieces.append(pair[0])
4169 return self.execute_command("ZADD", name, *pieces, **options)
4171 def zcard(self, name: KeyT) -> ResponseT:
4172 """
4173 Return the number of elements in the sorted set ``name``
4175 For more information see https://redis.io/commands/zcard
4176 """
4177 return self.execute_command("ZCARD", name, keys=[name])
4179 def zcount(self, name: KeyT, min: ZScoreBoundT, max: ZScoreBoundT) -> ResponseT:
4180 """
4181 Returns the number of elements in the sorted set at key ``name`` with
4182 a score between ``min`` and ``max``.
4184 For more information see https://redis.io/commands/zcount
4185 """
4186 return self.execute_command("ZCOUNT", name, min, max, keys=[name])
4188 def zdiff(self, keys: KeysT, withscores: bool = False) -> ResponseT:
4189 """
4190 Returns the difference between the first and all successive input
4191 sorted sets provided in ``keys``.
4193 For more information see https://redis.io/commands/zdiff
4194 """
4195 pieces = [len(keys), *keys]
4196 if withscores:
4197 pieces.append("WITHSCORES")
4198 return self.execute_command("ZDIFF", *pieces, keys=keys)
4200 def zdiffstore(self, dest: KeyT, keys: KeysT) -> ResponseT:
4201 """
4202 Computes the difference between the first and all successive input
4203 sorted sets provided in ``keys`` and stores the result in ``dest``.
4205 For more information see https://redis.io/commands/zdiffstore
4206 """
4207 pieces = [len(keys), *keys]
4208 return self.execute_command("ZDIFFSTORE", dest, *pieces)
4210 def zincrby(self, name: KeyT, amount: float, value: EncodableT) -> ResponseT:
4211 """
4212 Increment the score of ``value`` in sorted set ``name`` by ``amount``
4214 For more information see https://redis.io/commands/zincrby
4215 """
4216 return self.execute_command("ZINCRBY", name, amount, value)
4218 def zinter(
4219 self, keys: KeysT, aggregate: Union[str, None] = None, withscores: bool = False
4220 ) -> ResponseT:
4221 """
4222 Return the intersect of multiple sorted sets specified by ``keys``.
4223 With the ``aggregate`` option, it is possible to specify how the
4224 results of the union are aggregated. This option defaults to SUM,
4225 where the score of an element is summed across the inputs where it
4226 exists. When this option is set to either MIN or MAX, the resulting
4227 set will contain the minimum or maximum score of an element across
4228 the inputs where it exists.
4230 For more information see https://redis.io/commands/zinter
4231 """
4232 return self._zaggregate("ZINTER", None, keys, aggregate, withscores=withscores)
4234 def zinterstore(
4235 self,
4236 dest: KeyT,
4237 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4238 aggregate: Union[str, None] = None,
4239 ) -> ResponseT:
4240 """
4241 Intersect multiple sorted sets specified by ``keys`` into a new
4242 sorted set, ``dest``. Scores in the destination will be aggregated
4243 based on the ``aggregate``. This option defaults to SUM, where the
4244 score of an element is summed across the inputs where it exists.
4245 When this option is set to either MIN or MAX, the resulting set will
4246 contain the minimum or maximum score of an element across the inputs
4247 where it exists.
4249 For more information see https://redis.io/commands/zinterstore
4250 """
4251 return self._zaggregate("ZINTERSTORE", dest, keys, aggregate)
4253 def zintercard(
4254 self, numkeys: int, keys: List[str], limit: int = 0
4255 ) -> Union[Awaitable[int], int]:
4256 """
4257 Return the cardinality of the intersect of multiple sorted sets
4258 specified by ``keys`.
4259 When LIMIT provided (defaults to 0 and means unlimited), if the intersection
4260 cardinality reaches limit partway through the computation, the algorithm will
4261 exit and yield limit as the cardinality
4263 For more information see https://redis.io/commands/zintercard
4264 """
4265 args = [numkeys, *keys, "LIMIT", limit]
4266 return self.execute_command("ZINTERCARD", *args, keys=keys)
4268 def zlexcount(self, name, min, max):
4269 """
4270 Return the number of items in the sorted set ``name`` between the
4271 lexicographical range ``min`` and ``max``.
4273 For more information see https://redis.io/commands/zlexcount
4274 """
4275 return self.execute_command("ZLEXCOUNT", name, min, max, keys=[name])
4277 def zpopmax(self, name: KeyT, count: Union[int, None] = None) -> ResponseT:
4278 """
4279 Remove and return up to ``count`` members with the highest scores
4280 from the sorted set ``name``.
4282 For more information see https://redis.io/commands/zpopmax
4283 """
4284 args = (count is not None) and [count] or []
4285 options = {"withscores": True}
4286 return self.execute_command("ZPOPMAX", name, *args, **options)
4288 def zpopmin(self, name: KeyT, count: Union[int, None] = None) -> ResponseT:
4289 """
4290 Remove and return up to ``count`` members with the lowest scores
4291 from the sorted set ``name``.
4293 For more information see https://redis.io/commands/zpopmin
4294 """
4295 args = (count is not None) and [count] or []
4296 options = {"withscores": True}
4297 return self.execute_command("ZPOPMIN", name, *args, **options)
4299 def zrandmember(
4300 self, key: KeyT, count: int = None, withscores: bool = False
4301 ) -> ResponseT:
4302 """
4303 Return a random element from the sorted set value stored at key.
4305 ``count`` if the argument is positive, return an array of distinct
4306 fields. If called with a negative count, the behavior changes and
4307 the command is allowed to return the same field multiple times.
4308 In this case, the number of returned fields is the absolute value
4309 of the specified count.
4311 ``withscores`` The optional WITHSCORES modifier changes the reply so it
4312 includes the respective scores of the randomly selected elements from
4313 the sorted set.
4315 For more information see https://redis.io/commands/zrandmember
4316 """
4317 params = []
4318 if count is not None:
4319 params.append(count)
4320 if withscores:
4321 params.append("WITHSCORES")
4323 return self.execute_command("ZRANDMEMBER", key, *params)
4325 def bzpopmax(self, keys: KeysT, timeout: TimeoutSecT = 0) -> ResponseT:
4326 """
4327 ZPOPMAX a value off of the first non-empty sorted set
4328 named in the ``keys`` list.
4330 If none of the sorted sets in ``keys`` has a value to ZPOPMAX,
4331 then block for ``timeout`` seconds, or until a member gets added
4332 to one of the sorted sets.
4334 If timeout is 0, then block indefinitely.
4336 For more information see https://redis.io/commands/bzpopmax
4337 """
4338 if timeout is None:
4339 timeout = 0
4340 keys = list_or_args(keys, None)
4341 keys.append(timeout)
4342 return self.execute_command("BZPOPMAX", *keys)
4344 def bzpopmin(self, keys: KeysT, timeout: TimeoutSecT = 0) -> ResponseT:
4345 """
4346 ZPOPMIN a value off of the first non-empty sorted set
4347 named in the ``keys`` list.
4349 If none of the sorted sets in ``keys`` has a value to ZPOPMIN,
4350 then block for ``timeout`` seconds, or until a member gets added
4351 to one of the sorted sets.
4353 If timeout is 0, then block indefinitely.
4355 For more information see https://redis.io/commands/bzpopmin
4356 """
4357 if timeout is None:
4358 timeout = 0
4359 keys: list[EncodableT] = list_or_args(keys, None)
4360 keys.append(timeout)
4361 return self.execute_command("BZPOPMIN", *keys)
4363 def zmpop(
4364 self,
4365 num_keys: int,
4366 keys: List[str],
4367 min: Optional[bool] = False,
4368 max: Optional[bool] = False,
4369 count: Optional[int] = 1,
4370 ) -> Union[Awaitable[list], list]:
4371 """
4372 Pop ``count`` values (default 1) off of the first non-empty sorted set
4373 named in the ``keys`` list.
4374 For more information see https://redis.io/commands/zmpop
4375 """
4376 args = [num_keys] + keys
4377 if (min and max) or (not min and not max):
4378 raise DataError
4379 elif min:
4380 args.append("MIN")
4381 else:
4382 args.append("MAX")
4383 if count != 1:
4384 args.extend(["COUNT", count])
4386 return self.execute_command("ZMPOP", *args)
4388 def bzmpop(
4389 self,
4390 timeout: float,
4391 numkeys: int,
4392 keys: List[str],
4393 min: Optional[bool] = False,
4394 max: Optional[bool] = False,
4395 count: Optional[int] = 1,
4396 ) -> Optional[list]:
4397 """
4398 Pop ``count`` values (default 1) off of the first non-empty sorted set
4399 named in the ``keys`` list.
4401 If none of the sorted sets in ``keys`` has a value to pop,
4402 then block for ``timeout`` seconds, or until a member gets added
4403 to one of the sorted sets.
4405 If timeout is 0, then block indefinitely.
4407 For more information see https://redis.io/commands/bzmpop
4408 """
4409 args = [timeout, numkeys, *keys]
4410 if (min and max) or (not min and not max):
4411 raise DataError("Either min or max, but not both must be set")
4412 elif min:
4413 args.append("MIN")
4414 else:
4415 args.append("MAX")
4416 args.extend(["COUNT", count])
4418 return self.execute_command("BZMPOP", *args)
4420 def _zrange(
4421 self,
4422 command,
4423 dest: Union[KeyT, None],
4424 name: KeyT,
4425 start: int,
4426 end: int,
4427 desc: bool = False,
4428 byscore: bool = False,
4429 bylex: bool = False,
4430 withscores: bool = False,
4431 score_cast_func: Union[type, Callable, None] = float,
4432 offset: Union[int, None] = None,
4433 num: Union[int, None] = None,
4434 ) -> ResponseT:
4435 if byscore and bylex:
4436 raise DataError("``byscore`` and ``bylex`` can not be specified together.")
4437 if (offset is not None and num is None) or (num is not None and offset is None):
4438 raise DataError("``offset`` and ``num`` must both be specified.")
4439 if bylex and withscores:
4440 raise DataError(
4441 "``withscores`` not supported in combination with ``bylex``."
4442 )
4443 pieces = [command]
4444 if dest:
4445 pieces.append(dest)
4446 pieces.extend([name, start, end])
4447 if byscore:
4448 pieces.append("BYSCORE")
4449 if bylex:
4450 pieces.append("BYLEX")
4451 if desc:
4452 pieces.append("REV")
4453 if offset is not None and num is not None:
4454 pieces.extend(["LIMIT", offset, num])
4455 if withscores:
4456 pieces.append("WITHSCORES")
4457 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4458 options["keys"] = [name]
4459 return self.execute_command(*pieces, **options)
4461 def zrange(
4462 self,
4463 name: KeyT,
4464 start: int,
4465 end: int,
4466 desc: bool = False,
4467 withscores: bool = False,
4468 score_cast_func: Union[type, Callable] = float,
4469 byscore: bool = False,
4470 bylex: bool = False,
4471 offset: int = None,
4472 num: int = None,
4473 ) -> ResponseT:
4474 """
4475 Return a range of values from sorted set ``name`` between
4476 ``start`` and ``end`` sorted in ascending order.
4478 ``start`` and ``end`` can be negative, indicating the end of the range.
4480 ``desc`` a boolean indicating whether to sort the results in reversed
4481 order.
4483 ``withscores`` indicates to return the scores along with the values.
4484 The return type is a list of (value, score) pairs.
4486 ``score_cast_func`` a callable used to cast the score return value.
4488 ``byscore`` when set to True, returns the range of elements from the
4489 sorted set having scores equal or between ``start`` and ``end``.
4491 ``bylex`` when set to True, returns the range of elements from the
4492 sorted set between the ``start`` and ``end`` lexicographical closed
4493 range intervals.
4494 Valid ``start`` and ``end`` must start with ( or [, in order to specify
4495 whether the range interval is exclusive or inclusive, respectively.
4497 ``offset`` and ``num`` are specified, then return a slice of the range.
4498 Can't be provided when using ``bylex``.
4500 For more information see https://redis.io/commands/zrange
4501 """
4502 # Need to support ``desc`` also when using old redis version
4503 # because it was supported in 3.5.3 (of redis-py)
4504 if not byscore and not bylex and (offset is None and num is None) and desc:
4505 return self.zrevrange(name, start, end, withscores, score_cast_func)
4507 return self._zrange(
4508 "ZRANGE",
4509 None,
4510 name,
4511 start,
4512 end,
4513 desc,
4514 byscore,
4515 bylex,
4516 withscores,
4517 score_cast_func,
4518 offset,
4519 num,
4520 )
4522 def zrevrange(
4523 self,
4524 name: KeyT,
4525 start: int,
4526 end: int,
4527 withscores: bool = False,
4528 score_cast_func: Union[type, Callable] = float,
4529 ) -> ResponseT:
4530 """
4531 Return a range of values from sorted set ``name`` between
4532 ``start`` and ``end`` sorted in descending order.
4534 ``start`` and ``end`` can be negative, indicating the end of the range.
4536 ``withscores`` indicates to return the scores along with the values
4537 The return type is a list of (value, score) pairs
4539 ``score_cast_func`` a callable used to cast the score return value
4541 For more information see https://redis.io/commands/zrevrange
4542 """
4543 pieces = ["ZREVRANGE", name, start, end]
4544 if withscores:
4545 pieces.append(b"WITHSCORES")
4546 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4547 options["keys"] = name
4548 return self.execute_command(*pieces, **options)
4550 def zrangestore(
4551 self,
4552 dest: KeyT,
4553 name: KeyT,
4554 start: int,
4555 end: int,
4556 byscore: bool = False,
4557 bylex: bool = False,
4558 desc: bool = False,
4559 offset: Union[int, None] = None,
4560 num: Union[int, None] = None,
4561 ) -> ResponseT:
4562 """
4563 Stores in ``dest`` the result of a range of values from sorted set
4564 ``name`` between ``start`` and ``end`` sorted in ascending order.
4566 ``start`` and ``end`` can be negative, indicating the end of the range.
4568 ``byscore`` when set to True, returns the range of elements from the
4569 sorted set having scores equal or between ``start`` and ``end``.
4571 ``bylex`` when set to True, returns the range of elements from the
4572 sorted set between the ``start`` and ``end`` lexicographical closed
4573 range intervals.
4574 Valid ``start`` and ``end`` must start with ( or [, in order to specify
4575 whether the range interval is exclusive or inclusive, respectively.
4577 ``desc`` a boolean indicating whether to sort the results in reversed
4578 order.
4580 ``offset`` and ``num`` are specified, then return a slice of the range.
4581 Can't be provided when using ``bylex``.
4583 For more information see https://redis.io/commands/zrangestore
4584 """
4585 return self._zrange(
4586 "ZRANGESTORE",
4587 dest,
4588 name,
4589 start,
4590 end,
4591 desc,
4592 byscore,
4593 bylex,
4594 False,
4595 None,
4596 offset,
4597 num,
4598 )
4600 def zrangebylex(
4601 self,
4602 name: KeyT,
4603 min: EncodableT,
4604 max: EncodableT,
4605 start: Union[int, None] = None,
4606 num: Union[int, None] = None,
4607 ) -> ResponseT:
4608 """
4609 Return the lexicographical range of values from sorted set ``name``
4610 between ``min`` and ``max``.
4612 If ``start`` and ``num`` are specified, then return a slice of the
4613 range.
4615 For more information see https://redis.io/commands/zrangebylex
4616 """
4617 if (start is not None and num is None) or (num is not None and start is None):
4618 raise DataError("``start`` and ``num`` must both be specified")
4619 pieces = ["ZRANGEBYLEX", name, min, max]
4620 if start is not None and num is not None:
4621 pieces.extend([b"LIMIT", start, num])
4622 return self.execute_command(*pieces, keys=[name])
4624 def zrevrangebylex(
4625 self,
4626 name: KeyT,
4627 max: EncodableT,
4628 min: EncodableT,
4629 start: Union[int, None] = None,
4630 num: Union[int, None] = None,
4631 ) -> ResponseT:
4632 """
4633 Return the reversed lexicographical range of values from sorted set
4634 ``name`` between ``max`` and ``min``.
4636 If ``start`` and ``num`` are specified, then return a slice of the
4637 range.
4639 For more information see https://redis.io/commands/zrevrangebylex
4640 """
4641 if (start is not None and num is None) or (num is not None and start is None):
4642 raise DataError("``start`` and ``num`` must both be specified")
4643 pieces = ["ZREVRANGEBYLEX", name, max, min]
4644 if start is not None and num is not None:
4645 pieces.extend(["LIMIT", start, num])
4646 return self.execute_command(*pieces, keys=[name])
4648 def zrangebyscore(
4649 self,
4650 name: KeyT,
4651 min: ZScoreBoundT,
4652 max: ZScoreBoundT,
4653 start: Union[int, None] = None,
4654 num: Union[int, None] = None,
4655 withscores: bool = False,
4656 score_cast_func: Union[type, Callable] = float,
4657 ) -> ResponseT:
4658 """
4659 Return a range of values from the sorted set ``name`` with scores
4660 between ``min`` and ``max``.
4662 If ``start`` and ``num`` are specified, then return a slice
4663 of the range.
4665 ``withscores`` indicates to return the scores along with the values.
4666 The return type is a list of (value, score) pairs
4668 `score_cast_func`` a callable used to cast the score return value
4670 For more information see https://redis.io/commands/zrangebyscore
4671 """
4672 if (start is not None and num is None) or (num is not None and start is None):
4673 raise DataError("``start`` and ``num`` must both be specified")
4674 pieces = ["ZRANGEBYSCORE", name, min, max]
4675 if start is not None and num is not None:
4676 pieces.extend(["LIMIT", start, num])
4677 if withscores:
4678 pieces.append("WITHSCORES")
4679 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4680 options["keys"] = [name]
4681 return self.execute_command(*pieces, **options)
4683 def zrevrangebyscore(
4684 self,
4685 name: KeyT,
4686 max: ZScoreBoundT,
4687 min: ZScoreBoundT,
4688 start: Union[int, None] = None,
4689 num: Union[int, None] = None,
4690 withscores: bool = False,
4691 score_cast_func: Union[type, Callable] = float,
4692 ):
4693 """
4694 Return a range of values from the sorted set ``name`` with scores
4695 between ``min`` and ``max`` in descending order.
4697 If ``start`` and ``num`` are specified, then return a slice
4698 of the range.
4700 ``withscores`` indicates to return the scores along with the values.
4701 The return type is a list of (value, score) pairs
4703 ``score_cast_func`` a callable used to cast the score return value
4705 For more information see https://redis.io/commands/zrevrangebyscore
4706 """
4707 if (start is not None and num is None) or (num is not None and start is None):
4708 raise DataError("``start`` and ``num`` must both be specified")
4709 pieces = ["ZREVRANGEBYSCORE", name, max, min]
4710 if start is not None and num is not None:
4711 pieces.extend(["LIMIT", start, num])
4712 if withscores:
4713 pieces.append("WITHSCORES")
4714 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4715 options["keys"] = [name]
4716 return self.execute_command(*pieces, **options)
4718 def zrank(
4719 self,
4720 name: KeyT,
4721 value: EncodableT,
4722 withscore: bool = False,
4723 ) -> ResponseT:
4724 """
4725 Returns a 0-based value indicating the rank of ``value`` in sorted set
4726 ``name``.
4727 The optional WITHSCORE argument supplements the command's
4728 reply with the score of the element returned.
4730 For more information see https://redis.io/commands/zrank
4731 """
4732 if withscore:
4733 return self.execute_command("ZRANK", name, value, "WITHSCORE", keys=[name])
4734 return self.execute_command("ZRANK", name, value, keys=[name])
4736 def zrem(self, name: KeyT, *values: FieldT) -> ResponseT:
4737 """
4738 Remove member ``values`` from sorted set ``name``
4740 For more information see https://redis.io/commands/zrem
4741 """
4742 return self.execute_command("ZREM", name, *values)
4744 def zremrangebylex(self, name: KeyT, min: EncodableT, max: EncodableT) -> ResponseT:
4745 """
4746 Remove all elements in the sorted set ``name`` between the
4747 lexicographical range specified by ``min`` and ``max``.
4749 Returns the number of elements removed.
4751 For more information see https://redis.io/commands/zremrangebylex
4752 """
4753 return self.execute_command("ZREMRANGEBYLEX", name, min, max)
4755 def zremrangebyrank(self, name: KeyT, min: int, max: int) -> ResponseT:
4756 """
4757 Remove all elements in the sorted set ``name`` with ranks between
4758 ``min`` and ``max``. Values are 0-based, ordered from smallest score
4759 to largest. Values can be negative indicating the highest scores.
4760 Returns the number of elements removed
4762 For more information see https://redis.io/commands/zremrangebyrank
4763 """
4764 return self.execute_command("ZREMRANGEBYRANK", name, min, max)
4766 def zremrangebyscore(
4767 self, name: KeyT, min: ZScoreBoundT, max: ZScoreBoundT
4768 ) -> ResponseT:
4769 """
4770 Remove all elements in the sorted set ``name`` with scores
4771 between ``min`` and ``max``. Returns the number of elements removed.
4773 For more information see https://redis.io/commands/zremrangebyscore
4774 """
4775 return self.execute_command("ZREMRANGEBYSCORE", name, min, max)
4777 def zrevrank(
4778 self,
4779 name: KeyT,
4780 value: EncodableT,
4781 withscore: bool = False,
4782 ) -> ResponseT:
4783 """
4784 Returns a 0-based value indicating the descending rank of
4785 ``value`` in sorted set ``name``.
4786 The optional ``withscore`` argument supplements the command's
4787 reply with the score of the element returned.
4789 For more information see https://redis.io/commands/zrevrank
4790 """
4791 if withscore:
4792 return self.execute_command(
4793 "ZREVRANK", name, value, "WITHSCORE", keys=[name]
4794 )
4795 return self.execute_command("ZREVRANK", name, value, keys=[name])
4797 def zscore(self, name: KeyT, value: EncodableT) -> ResponseT:
4798 """
4799 Return the score of element ``value`` in sorted set ``name``
4801 For more information see https://redis.io/commands/zscore
4802 """
4803 return self.execute_command("ZSCORE", name, value, keys=[name])
4805 def zunion(
4806 self,
4807 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4808 aggregate: Union[str, None] = None,
4809 withscores: bool = False,
4810 ) -> ResponseT:
4811 """
4812 Return the union of multiple sorted sets specified by ``keys``.
4813 ``keys`` can be provided as dictionary of keys and their weights.
4814 Scores will be aggregated based on the ``aggregate``, or SUM if
4815 none is provided.
4817 For more information see https://redis.io/commands/zunion
4818 """
4819 return self._zaggregate("ZUNION", None, keys, aggregate, withscores=withscores)
4821 def zunionstore(
4822 self,
4823 dest: KeyT,
4824 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4825 aggregate: Union[str, None] = None,
4826 ) -> ResponseT:
4827 """
4828 Union multiple sorted sets specified by ``keys`` into
4829 a new sorted set, ``dest``. Scores in the destination will be
4830 aggregated based on the ``aggregate``, or SUM if none is provided.
4832 For more information see https://redis.io/commands/zunionstore
4833 """
4834 return self._zaggregate("ZUNIONSTORE", dest, keys, aggregate)
4836 def zmscore(self, key: KeyT, members: List[str]) -> ResponseT:
4837 """
4838 Returns the scores associated with the specified members
4839 in the sorted set stored at key.
4840 ``members`` should be a list of the member name.
4841 Return type is a list of score.
4842 If the member does not exist, a None will be returned
4843 in corresponding position.
4845 For more information see https://redis.io/commands/zmscore
4846 """
4847 if not members:
4848 raise DataError("ZMSCORE members must be a non-empty list")
4849 pieces = [key] + members
4850 return self.execute_command("ZMSCORE", *pieces, keys=[key])
4852 def _zaggregate(
4853 self,
4854 command: str,
4855 dest: Union[KeyT, None],
4856 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4857 aggregate: Union[str, None] = None,
4858 **options,
4859 ) -> ResponseT:
4860 pieces: list[EncodableT] = [command]
4861 if dest is not None:
4862 pieces.append(dest)
4863 pieces.append(len(keys))
4864 if isinstance(keys, dict):
4865 keys, weights = keys.keys(), keys.values()
4866 else:
4867 weights = None
4868 pieces.extend(keys)
4869 if weights:
4870 pieces.append(b"WEIGHTS")
4871 pieces.extend(weights)
4872 if aggregate:
4873 if aggregate.upper() in ["SUM", "MIN", "MAX"]:
4874 pieces.append(b"AGGREGATE")
4875 pieces.append(aggregate)
4876 else:
4877 raise DataError("aggregate can be sum, min or max.")
4878 if options.get("withscores", False):
4879 pieces.append(b"WITHSCORES")
4880 options["keys"] = keys
4881 return self.execute_command(*pieces, **options)
4884AsyncSortedSetCommands = SortedSetCommands
4887class HyperlogCommands(CommandsProtocol):
4888 """
4889 Redis commands of HyperLogLogs data type.
4890 see: https://redis.io/topics/data-types-intro#hyperloglogs
4891 """
4893 def pfadd(self, name: KeyT, *values: FieldT) -> ResponseT:
4894 """
4895 Adds the specified elements to the specified HyperLogLog.
4897 For more information see https://redis.io/commands/pfadd
4898 """
4899 return self.execute_command("PFADD", name, *values)
4901 def pfcount(self, *sources: KeyT) -> ResponseT:
4902 """
4903 Return the approximated cardinality of
4904 the set observed by the HyperLogLog at key(s).
4906 For more information see https://redis.io/commands/pfcount
4907 """
4908 return self.execute_command("PFCOUNT", *sources)
4910 def pfmerge(self, dest: KeyT, *sources: KeyT) -> ResponseT:
4911 """
4912 Merge N different HyperLogLogs into a single one.
4914 For more information see https://redis.io/commands/pfmerge
4915 """
4916 return self.execute_command("PFMERGE", dest, *sources)
4919AsyncHyperlogCommands = HyperlogCommands
4922class HashCommands(CommandsProtocol):
4923 """
4924 Redis commands for Hash data type.
4925 see: https://redis.io/topics/data-types-intro#redis-hashes
4926 """
4928 def hdel(self, name: str, *keys: str) -> Union[Awaitable[int], int]:
4929 """
4930 Delete ``keys`` from hash ``name``
4932 For more information see https://redis.io/commands/hdel
4933 """
4934 return self.execute_command("HDEL", name, *keys)
4936 def hexists(self, name: str, key: str) -> Union[Awaitable[bool], bool]:
4937 """
4938 Returns a boolean indicating if ``key`` exists within hash ``name``
4940 For more information see https://redis.io/commands/hexists
4941 """
4942 return self.execute_command("HEXISTS", name, key, keys=[name])
4944 def hget(
4945 self, name: str, key: str
4946 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
4947 """
4948 Return the value of ``key`` within the hash ``name``
4950 For more information see https://redis.io/commands/hget
4951 """
4952 return self.execute_command("HGET", name, key, keys=[name])
4954 def hgetall(self, name: str) -> Union[Awaitable[dict], dict]:
4955 """
4956 Return a Python dict of the hash's name/value pairs
4958 For more information see https://redis.io/commands/hgetall
4959 """
4960 return self.execute_command("HGETALL", name, keys=[name])
4962 def hincrby(
4963 self, name: str, key: str, amount: int = 1
4964 ) -> Union[Awaitable[int], int]:
4965 """
4966 Increment the value of ``key`` in hash ``name`` by ``amount``
4968 For more information see https://redis.io/commands/hincrby
4969 """
4970 return self.execute_command("HINCRBY", name, key, amount)
4972 def hincrbyfloat(
4973 self, name: str, key: str, amount: float = 1.0
4974 ) -> Union[Awaitable[float], float]:
4975 """
4976 Increment the value of ``key`` in hash ``name`` by floating ``amount``
4978 For more information see https://redis.io/commands/hincrbyfloat
4979 """
4980 return self.execute_command("HINCRBYFLOAT", name, key, amount)
4982 def hkeys(self, name: str) -> Union[Awaitable[List], List]:
4983 """
4984 Return the list of keys within hash ``name``
4986 For more information see https://redis.io/commands/hkeys
4987 """
4988 return self.execute_command("HKEYS", name, keys=[name])
4990 def hlen(self, name: str) -> Union[Awaitable[int], int]:
4991 """
4992 Return the number of elements in hash ``name``
4994 For more information see https://redis.io/commands/hlen
4995 """
4996 return self.execute_command("HLEN", name, keys=[name])
4998 def hset(
4999 self,
5000 name: str,
5001 key: Optional[str] = None,
5002 value: Optional[str] = None,
5003 mapping: Optional[dict] = None,
5004 items: Optional[list] = None,
5005 ) -> Union[Awaitable[int], int]:
5006 """
5007 Set ``key`` to ``value`` within hash ``name``,
5008 ``mapping`` accepts a dict of key/value pairs that will be
5009 added to hash ``name``.
5010 ``items`` accepts a list of key/value pairs that will be
5011 added to hash ``name``.
5012 Returns the number of fields that were added.
5014 For more information see https://redis.io/commands/hset
5015 """
5016 if key is None and not mapping and not items:
5017 raise DataError("'hset' with no key value pairs")
5018 pieces = []
5019 if items:
5020 pieces.extend(items)
5021 if key is not None:
5022 pieces.extend((key, value))
5023 if mapping:
5024 for pair in mapping.items():
5025 pieces.extend(pair)
5027 return self.execute_command("HSET", name, *pieces)
5029 def hsetnx(self, name: str, key: str, value: str) -> Union[Awaitable[bool], bool]:
5030 """
5031 Set ``key`` to ``value`` within hash ``name`` if ``key`` does not
5032 exist. Returns 1 if HSETNX created a field, otherwise 0.
5034 For more information see https://redis.io/commands/hsetnx
5035 """
5036 return self.execute_command("HSETNX", name, key, value)
5038 def hmset(self, name: str, mapping: dict) -> Union[Awaitable[str], str]:
5039 """
5040 Set key to value within hash ``name`` for each corresponding
5041 key and value from the ``mapping`` dict.
5043 For more information see https://redis.io/commands/hmset
5044 """
5045 warnings.warn(
5046 f"{self.__class__.__name__}.hmset() is deprecated. "
5047 f"Use {self.__class__.__name__}.hset() instead.",
5048 DeprecationWarning,
5049 stacklevel=2,
5050 )
5051 if not mapping:
5052 raise DataError("'hmset' with 'mapping' of length 0")
5053 items = []
5054 for pair in mapping.items():
5055 items.extend(pair)
5056 return self.execute_command("HMSET", name, *items)
5058 def hmget(self, name: str, keys: List, *args: List) -> Union[Awaitable[List], List]:
5059 """
5060 Returns a list of values ordered identically to ``keys``
5062 For more information see https://redis.io/commands/hmget
5063 """
5064 args = list_or_args(keys, args)
5065 return self.execute_command("HMGET", name, *args, keys=[name])
5067 def hvals(self, name: str) -> Union[Awaitable[List], List]:
5068 """
5069 Return the list of values within hash ``name``
5071 For more information see https://redis.io/commands/hvals
5072 """
5073 return self.execute_command("HVALS", name, keys=[name])
5075 def hstrlen(self, name: str, key: str) -> Union[Awaitable[int], int]:
5076 """
5077 Return the number of bytes stored in the value of ``key``
5078 within hash ``name``
5080 For more information see https://redis.io/commands/hstrlen
5081 """
5082 return self.execute_command("HSTRLEN", name, key, keys=[name])
5085AsyncHashCommands = HashCommands
5088class Script:
5089 """
5090 An executable Lua script object returned by ``register_script``
5091 """
5093 def __init__(self, registered_client: "Redis", script: ScriptTextT):
5094 self.registered_client = registered_client
5095 self.script = script
5096 # Precalculate and store the SHA1 hex digest of the script.
5098 if isinstance(script, str):
5099 # We need the encoding from the client in order to generate an
5100 # accurate byte representation of the script
5101 try:
5102 encoder = registered_client.connection_pool.get_encoder()
5103 except AttributeError:
5104 # Cluster
5105 encoder = registered_client.get_encoder()
5106 script = encoder.encode(script)
5107 self.sha = hashlib.sha1(script).hexdigest()
5109 def __call__(
5110 self,
5111 keys: Union[Sequence[KeyT], None] = None,
5112 args: Union[Iterable[EncodableT], None] = None,
5113 client: Union["Redis", None] = None,
5114 ):
5115 """Execute the script, passing any required ``args``"""
5116 keys = keys or []
5117 args = args or []
5118 if client is None:
5119 client = self.registered_client
5120 args = tuple(keys) + tuple(args)
5121 # make sure the Redis server knows about the script
5122 from redis.client import Pipeline
5124 if isinstance(client, Pipeline):
5125 # Make sure the pipeline can register the script before executing.
5126 client.scripts.add(self)
5127 try:
5128 return client.evalsha(self.sha, len(keys), *args)
5129 except NoScriptError:
5130 # Maybe the client is pointed to a different server than the client
5131 # that created this instance?
5132 # Overwrite the sha just in case there was a discrepancy.
5133 self.sha = client.script_load(self.script)
5134 return client.evalsha(self.sha, len(keys), *args)
5137class AsyncScript:
5138 """
5139 An executable Lua script object returned by ``register_script``
5140 """
5142 def __init__(self, registered_client: "AsyncRedis", script: ScriptTextT):
5143 self.registered_client = registered_client
5144 self.script = script
5145 # Precalculate and store the SHA1 hex digest of the script.
5147 if isinstance(script, str):
5148 # We need the encoding from the client in order to generate an
5149 # accurate byte representation of the script
5150 try:
5151 encoder = registered_client.connection_pool.get_encoder()
5152 except AttributeError:
5153 # Cluster
5154 encoder = registered_client.get_encoder()
5155 script = encoder.encode(script)
5156 self.sha = hashlib.sha1(script).hexdigest()
5158 async def __call__(
5159 self,
5160 keys: Union[Sequence[KeyT], None] = None,
5161 args: Union[Iterable[EncodableT], None] = None,
5162 client: Union["AsyncRedis", None] = None,
5163 ):
5164 """Execute the script, passing any required ``args``"""
5165 keys = keys or []
5166 args = args or []
5167 if client is None:
5168 client = self.registered_client
5169 args = tuple(keys) + tuple(args)
5170 # make sure the Redis server knows about the script
5171 from redis.asyncio.client import Pipeline
5173 if isinstance(client, Pipeline):
5174 # Make sure the pipeline can register the script before executing.
5175 client.scripts.add(self)
5176 try:
5177 return await client.evalsha(self.sha, len(keys), *args)
5178 except NoScriptError:
5179 # Maybe the client is pointed to a different server than the client
5180 # that created this instance?
5181 # Overwrite the sha just in case there was a discrepancy.
5182 self.sha = await client.script_load(self.script)
5183 return await client.evalsha(self.sha, len(keys), *args)
5186class PubSubCommands(CommandsProtocol):
5187 """
5188 Redis PubSub commands.
5189 see https://redis.io/topics/pubsub
5190 """
5192 def publish(self, channel: ChannelT, message: EncodableT, **kwargs) -> ResponseT:
5193 """
5194 Publish ``message`` on ``channel``.
5195 Returns the number of subscribers the message was delivered to.
5197 For more information see https://redis.io/commands/publish
5198 """
5199 return self.execute_command("PUBLISH", channel, message, **kwargs)
5201 def spublish(self, shard_channel: ChannelT, message: EncodableT) -> ResponseT:
5202 """
5203 Posts a message to the given shard channel.
5204 Returns the number of clients that received the message
5206 For more information see https://redis.io/commands/spublish
5207 """
5208 return self.execute_command("SPUBLISH", shard_channel, message)
5210 def pubsub_channels(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
5211 """
5212 Return a list of channels that have at least one subscriber
5214 For more information see https://redis.io/commands/pubsub-channels
5215 """
5216 return self.execute_command("PUBSUB CHANNELS", pattern, **kwargs)
5218 def pubsub_shardchannels(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
5219 """
5220 Return a list of shard_channels that have at least one subscriber
5222 For more information see https://redis.io/commands/pubsub-shardchannels
5223 """
5224 return self.execute_command("PUBSUB SHARDCHANNELS", pattern, **kwargs)
5226 def pubsub_numpat(self, **kwargs) -> ResponseT:
5227 """
5228 Returns the number of subscriptions to patterns
5230 For more information see https://redis.io/commands/pubsub-numpat
5231 """
5232 return self.execute_command("PUBSUB NUMPAT", **kwargs)
5234 def pubsub_numsub(self, *args: ChannelT, **kwargs) -> ResponseT:
5235 """
5236 Return a list of (channel, number of subscribers) tuples
5237 for each channel given in ``*args``
5239 For more information see https://redis.io/commands/pubsub-numsub
5240 """
5241 return self.execute_command("PUBSUB NUMSUB", *args, **kwargs)
5243 def pubsub_shardnumsub(self, *args: ChannelT, **kwargs) -> ResponseT:
5244 """
5245 Return a list of (shard_channel, number of subscribers) tuples
5246 for each channel given in ``*args``
5248 For more information see https://redis.io/commands/pubsub-shardnumsub
5249 """
5250 return self.execute_command("PUBSUB SHARDNUMSUB", *args, **kwargs)
5253AsyncPubSubCommands = PubSubCommands
5256class ScriptCommands(CommandsProtocol):
5257 """
5258 Redis Lua script commands. see:
5259 https://redis.com/ebook/part-3-next-steps/chapter-11-scripting-redis-with-lua/
5260 """
5262 def _eval(
5263 self, command: str, script: str, numkeys: int, *keys_and_args: str
5264 ) -> Union[Awaitable[str], str]:
5265 return self.execute_command(command, script, numkeys, *keys_and_args)
5267 def eval(
5268 self, script: str, numkeys: int, *keys_and_args: str
5269 ) -> Union[Awaitable[str], str]:
5270 """
5271 Execute the Lua ``script``, specifying the ``numkeys`` the script
5272 will touch and the key names and argument values in ``keys_and_args``.
5273 Returns the result of the script.
5275 In practice, use the object returned by ``register_script``. This
5276 function exists purely for Redis API completion.
5278 For more information see https://redis.io/commands/eval
5279 """
5280 return self._eval("EVAL", script, numkeys, *keys_and_args)
5282 def eval_ro(
5283 self, script: str, numkeys: int, *keys_and_args: str
5284 ) -> Union[Awaitable[str], str]:
5285 """
5286 The read-only variant of the EVAL command
5288 Execute the read-only Lua ``script`` specifying the ``numkeys`` the script
5289 will touch and the key names and argument values in ``keys_and_args``.
5290 Returns the result of the script.
5292 For more information see https://redis.io/commands/eval_ro
5293 """
5294 return self._eval("EVAL_RO", script, numkeys, *keys_and_args)
5296 def _evalsha(
5297 self, command: str, sha: str, numkeys: int, *keys_and_args: list
5298 ) -> Union[Awaitable[str], str]:
5299 return self.execute_command(command, sha, numkeys, *keys_and_args)
5301 def evalsha(
5302 self, sha: str, numkeys: int, *keys_and_args: str
5303 ) -> Union[Awaitable[str], str]:
5304 """
5305 Use the ``sha`` to execute a Lua script already registered via EVAL
5306 or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
5307 key names and argument values in ``keys_and_args``. Returns the result
5308 of the script.
5310 In practice, use the object returned by ``register_script``. This
5311 function exists purely for Redis API completion.
5313 For more information see https://redis.io/commands/evalsha
5314 """
5315 return self._evalsha("EVALSHA", sha, numkeys, *keys_and_args)
5317 def evalsha_ro(
5318 self, sha: str, numkeys: int, *keys_and_args: str
5319 ) -> Union[Awaitable[str], str]:
5320 """
5321 The read-only variant of the EVALSHA command
5323 Use the ``sha`` to execute a read-only Lua script already registered via EVAL
5324 or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
5325 key names and argument values in ``keys_and_args``. Returns the result
5326 of the script.
5328 For more information see https://redis.io/commands/evalsha_ro
5329 """
5330 return self._evalsha("EVALSHA_RO", sha, numkeys, *keys_and_args)
5332 def script_exists(self, *args: str) -> ResponseT:
5333 """
5334 Check if a script exists in the script cache by specifying the SHAs of
5335 each script as ``args``. Returns a list of boolean values indicating if
5336 if each already script exists in the cache.
5338 For more information see https://redis.io/commands/script-exists
5339 """
5340 return self.execute_command("SCRIPT EXISTS", *args)
5342 def script_debug(self, *args) -> None:
5343 raise NotImplementedError(
5344 "SCRIPT DEBUG is intentionally not implemented in the client."
5345 )
5347 def script_flush(
5348 self, sync_type: Union[Literal["SYNC"], Literal["ASYNC"]] = None
5349 ) -> ResponseT:
5350 """Flush all scripts from the script cache.
5352 ``sync_type`` is by default SYNC (synchronous) but it can also be
5353 ASYNC.
5355 For more information see https://redis.io/commands/script-flush
5356 """
5358 # Redis pre 6 had no sync_type.
5359 if sync_type not in ["SYNC", "ASYNC", None]:
5360 raise DataError(
5361 "SCRIPT FLUSH defaults to SYNC in redis > 6.2, or "
5362 "accepts SYNC/ASYNC. For older versions, "
5363 "of redis leave as None."
5364 )
5365 if sync_type is None:
5366 pieces = []
5367 else:
5368 pieces = [sync_type]
5369 return self.execute_command("SCRIPT FLUSH", *pieces)
5371 def script_kill(self) -> ResponseT:
5372 """
5373 Kill the currently executing Lua script
5375 For more information see https://redis.io/commands/script-kill
5376 """
5377 return self.execute_command("SCRIPT KILL")
5379 def script_load(self, script: ScriptTextT) -> ResponseT:
5380 """
5381 Load a Lua ``script`` into the script cache. Returns the SHA.
5383 For more information see https://redis.io/commands/script-load
5384 """
5385 return self.execute_command("SCRIPT LOAD", script)
5387 def register_script(self: "Redis", script: ScriptTextT) -> Script:
5388 """
5389 Register a Lua ``script`` specifying the ``keys`` it will touch.
5390 Returns a Script object that is callable and hides the complexity of
5391 deal with scripts, keys, and shas. This is the preferred way to work
5392 with Lua scripts.
5393 """
5394 return Script(self, script)
5397class AsyncScriptCommands(ScriptCommands):
5398 async def script_debug(self, *args) -> None:
5399 return super().script_debug()
5401 def register_script(self: "AsyncRedis", script: ScriptTextT) -> AsyncScript:
5402 """
5403 Register a Lua ``script`` specifying the ``keys`` it will touch.
5404 Returns a Script object that is callable and hides the complexity of
5405 deal with scripts, keys, and shas. This is the preferred way to work
5406 with Lua scripts.
5407 """
5408 return AsyncScript(self, script)
5411class GeoCommands(CommandsProtocol):
5412 """
5413 Redis Geospatial commands.
5414 see: https://redis.com/redis-best-practices/indexing-patterns/geospatial/
5415 """
5417 def geoadd(
5418 self,
5419 name: KeyT,
5420 values: Sequence[EncodableT],
5421 nx: bool = False,
5422 xx: bool = False,
5423 ch: bool = False,
5424 ) -> ResponseT:
5425 """
5426 Add the specified geospatial items to the specified key identified
5427 by the ``name`` argument. The Geospatial items are given as ordered
5428 members of the ``values`` argument, each item or place is formed by
5429 the triad longitude, latitude and name.
5431 Note: You can use ZREM to remove elements.
5433 ``nx`` forces ZADD to only create new elements and not to update
5434 scores for elements that already exist.
5436 ``xx`` forces ZADD to only update scores of elements that already
5437 exist. New elements will not be added.
5439 ``ch`` modifies the return value to be the numbers of elements changed.
5440 Changed elements include new elements that were added and elements
5441 whose scores changed.
5443 For more information see https://redis.io/commands/geoadd
5444 """
5445 if nx and xx:
5446 raise DataError("GEOADD allows either 'nx' or 'xx', not both")
5447 if len(values) % 3 != 0:
5448 raise DataError("GEOADD requires places with lon, lat and name values")
5449 pieces = [name]
5450 if nx:
5451 pieces.append("NX")
5452 if xx:
5453 pieces.append("XX")
5454 if ch:
5455 pieces.append("CH")
5456 pieces.extend(values)
5457 return self.execute_command("GEOADD", *pieces)
5459 def geodist(
5460 self, name: KeyT, place1: FieldT, place2: FieldT, unit: Union[str, None] = None
5461 ) -> ResponseT:
5462 """
5463 Return the distance between ``place1`` and ``place2`` members of the
5464 ``name`` key.
5465 The units must be one of the following : m, km mi, ft. By default
5466 meters are used.
5468 For more information see https://redis.io/commands/geodist
5469 """
5470 pieces: list[EncodableT] = [name, place1, place2]
5471 if unit and unit not in ("m", "km", "mi", "ft"):
5472 raise DataError("GEODIST invalid unit")
5473 elif unit:
5474 pieces.append(unit)
5475 return self.execute_command("GEODIST", *pieces, keys=[name])
5477 def geohash(self, name: KeyT, *values: FieldT) -> ResponseT:
5478 """
5479 Return the geo hash string for each item of ``values`` members of
5480 the specified key identified by the ``name`` argument.
5482 For more information see https://redis.io/commands/geohash
5483 """
5484 return self.execute_command("GEOHASH", name, *values, keys=[name])
5486 def geopos(self, name: KeyT, *values: FieldT) -> ResponseT:
5487 """
5488 Return the positions of each item of ``values`` as members of
5489 the specified key identified by the ``name`` argument. Each position
5490 is represented by the pairs lon and lat.
5492 For more information see https://redis.io/commands/geopos
5493 """
5494 return self.execute_command("GEOPOS", name, *values, keys=[name])
5496 def georadius(
5497 self,
5498 name: KeyT,
5499 longitude: float,
5500 latitude: float,
5501 radius: float,
5502 unit: Union[str, None] = None,
5503 withdist: bool = False,
5504 withcoord: bool = False,
5505 withhash: bool = False,
5506 count: Union[int, None] = None,
5507 sort: Union[str, None] = None,
5508 store: Union[KeyT, None] = None,
5509 store_dist: Union[KeyT, None] = None,
5510 any: bool = False,
5511 ) -> ResponseT:
5512 """
5513 Return the members of the specified key identified by the
5514 ``name`` argument which are within the borders of the area specified
5515 with the ``latitude`` and ``longitude`` location and the maximum
5516 distance from the center specified by the ``radius`` value.
5518 The units must be one of the following : m, km mi, ft. By default
5520 ``withdist`` indicates to return the distances of each place.
5522 ``withcoord`` indicates to return the latitude and longitude of
5523 each place.
5525 ``withhash`` indicates to return the geohash string of each place.
5527 ``count`` indicates to return the number of elements up to N.
5529 ``sort`` indicates to return the places in a sorted way, ASC for
5530 nearest to fairest and DESC for fairest to nearest.
5532 ``store`` indicates to save the places names in a sorted set named
5533 with a specific key, each element of the destination sorted set is
5534 populated with the score got from the original geo sorted set.
5536 ``store_dist`` indicates to save the places names in a sorted set
5537 named with a specific key, instead of ``store`` the sorted set
5538 destination score is set with the distance.
5540 For more information see https://redis.io/commands/georadius
5541 """
5542 return self._georadiusgeneric(
5543 "GEORADIUS",
5544 name,
5545 longitude,
5546 latitude,
5547 radius,
5548 unit=unit,
5549 withdist=withdist,
5550 withcoord=withcoord,
5551 withhash=withhash,
5552 count=count,
5553 sort=sort,
5554 store=store,
5555 store_dist=store_dist,
5556 any=any,
5557 )
5559 def georadiusbymember(
5560 self,
5561 name: KeyT,
5562 member: FieldT,
5563 radius: float,
5564 unit: Union[str, None] = None,
5565 withdist: bool = False,
5566 withcoord: bool = False,
5567 withhash: bool = False,
5568 count: Union[int, None] = None,
5569 sort: Union[str, None] = None,
5570 store: Union[KeyT, None] = None,
5571 store_dist: Union[KeyT, None] = None,
5572 any: bool = False,
5573 ) -> ResponseT:
5574 """
5575 This command is exactly like ``georadius`` with the sole difference
5576 that instead of taking, as the center of the area to query, a longitude
5577 and latitude value, it takes the name of a member already existing
5578 inside the geospatial index represented by the sorted set.
5580 For more information see https://redis.io/commands/georadiusbymember
5581 """
5582 return self._georadiusgeneric(
5583 "GEORADIUSBYMEMBER",
5584 name,
5585 member,
5586 radius,
5587 unit=unit,
5588 withdist=withdist,
5589 withcoord=withcoord,
5590 withhash=withhash,
5591 count=count,
5592 sort=sort,
5593 store=store,
5594 store_dist=store_dist,
5595 any=any,
5596 )
5598 def _georadiusgeneric(
5599 self, command: str, *args: EncodableT, **kwargs: Union[EncodableT, None]
5600 ) -> ResponseT:
5601 pieces = list(args)
5602 if kwargs["unit"] and kwargs["unit"] not in ("m", "km", "mi", "ft"):
5603 raise DataError("GEORADIUS invalid unit")
5604 elif kwargs["unit"]:
5605 pieces.append(kwargs["unit"])
5606 else:
5607 pieces.append("m")
5609 if kwargs["any"] and kwargs["count"] is None:
5610 raise DataError("``any`` can't be provided without ``count``")
5612 for arg_name, byte_repr in (
5613 ("withdist", "WITHDIST"),
5614 ("withcoord", "WITHCOORD"),
5615 ("withhash", "WITHHASH"),
5616 ):
5617 if kwargs[arg_name]:
5618 pieces.append(byte_repr)
5620 if kwargs["count"] is not None:
5621 pieces.extend(["COUNT", kwargs["count"]])
5622 if kwargs["any"]:
5623 pieces.append("ANY")
5625 if kwargs["sort"]:
5626 if kwargs["sort"] == "ASC":
5627 pieces.append("ASC")
5628 elif kwargs["sort"] == "DESC":
5629 pieces.append("DESC")
5630 else:
5631 raise DataError("GEORADIUS invalid sort")
5633 if kwargs["store"] and kwargs["store_dist"]:
5634 raise DataError("GEORADIUS store and store_dist cant be set together")
5636 if kwargs["store"]:
5637 pieces.extend([b"STORE", kwargs["store"]])
5639 if kwargs["store_dist"]:
5640 pieces.extend([b"STOREDIST", kwargs["store_dist"]])
5642 return self.execute_command(command, *pieces, **kwargs)
5644 def geosearch(
5645 self,
5646 name: KeyT,
5647 member: Union[FieldT, None] = None,
5648 longitude: Union[float, None] = None,
5649 latitude: Union[float, None] = None,
5650 unit: str = "m",
5651 radius: Union[float, None] = None,
5652 width: Union[float, None] = None,
5653 height: Union[float, None] = None,
5654 sort: Union[str, None] = None,
5655 count: Union[int, None] = None,
5656 any: bool = False,
5657 withcoord: bool = False,
5658 withdist: bool = False,
5659 withhash: bool = False,
5660 ) -> ResponseT:
5661 """
5662 Return the members of specified key identified by the
5663 ``name`` argument, which are within the borders of the
5664 area specified by a given shape. This command extends the
5665 GEORADIUS command, so in addition to searching within circular
5666 areas, it supports searching within rectangular areas.
5668 This command should be used in place of the deprecated
5669 GEORADIUS and GEORADIUSBYMEMBER commands.
5671 ``member`` Use the position of the given existing
5672 member in the sorted set. Can't be given with ``longitude``
5673 and ``latitude``.
5675 ``longitude`` and ``latitude`` Use the position given by
5676 this coordinates. Can't be given with ``member``
5677 ``radius`` Similar to GEORADIUS, search inside circular
5678 area according the given radius. Can't be given with
5679 ``height`` and ``width``.
5680 ``height`` and ``width`` Search inside an axis-aligned
5681 rectangle, determined by the given height and width.
5682 Can't be given with ``radius``
5684 ``unit`` must be one of the following : m, km, mi, ft.
5685 `m` for meters (the default value), `km` for kilometers,
5686 `mi` for miles and `ft` for feet.
5688 ``sort`` indicates to return the places in a sorted way,
5689 ASC for nearest to furthest and DESC for furthest to nearest.
5691 ``count`` limit the results to the first count matching items.
5693 ``any`` is set to True, the command will return as soon as
5694 enough matches are found. Can't be provided without ``count``
5696 ``withdist`` indicates to return the distances of each place.
5697 ``withcoord`` indicates to return the latitude and longitude of
5698 each place.
5700 ``withhash`` indicates to return the geohash string of each place.
5702 For more information see https://redis.io/commands/geosearch
5703 """
5705 return self._geosearchgeneric(
5706 "GEOSEARCH",
5707 name,
5708 member=member,
5709 longitude=longitude,
5710 latitude=latitude,
5711 unit=unit,
5712 radius=radius,
5713 width=width,
5714 height=height,
5715 sort=sort,
5716 count=count,
5717 any=any,
5718 withcoord=withcoord,
5719 withdist=withdist,
5720 withhash=withhash,
5721 store=None,
5722 store_dist=None,
5723 )
5725 def geosearchstore(
5726 self,
5727 dest: KeyT,
5728 name: KeyT,
5729 member: Union[FieldT, None] = None,
5730 longitude: Union[float, None] = None,
5731 latitude: Union[float, None] = None,
5732 unit: str = "m",
5733 radius: Union[float, None] = None,
5734 width: Union[float, None] = None,
5735 height: Union[float, None] = None,
5736 sort: Union[str, None] = None,
5737 count: Union[int, None] = None,
5738 any: bool = False,
5739 storedist: bool = False,
5740 ) -> ResponseT:
5741 """
5742 This command is like GEOSEARCH, but stores the result in
5743 ``dest``. By default, it stores the results in the destination
5744 sorted set with their geospatial information.
5745 if ``store_dist`` set to True, the command will stores the
5746 items in a sorted set populated with their distance from the
5747 center of the circle or box, as a floating-point number.
5749 For more information see https://redis.io/commands/geosearchstore
5750 """
5751 return self._geosearchgeneric(
5752 "GEOSEARCHSTORE",
5753 dest,
5754 name,
5755 member=member,
5756 longitude=longitude,
5757 latitude=latitude,
5758 unit=unit,
5759 radius=radius,
5760 width=width,
5761 height=height,
5762 sort=sort,
5763 count=count,
5764 any=any,
5765 withcoord=None,
5766 withdist=None,
5767 withhash=None,
5768 store=None,
5769 store_dist=storedist,
5770 )
5772 def _geosearchgeneric(
5773 self, command: str, *args: EncodableT, **kwargs: Union[EncodableT, None]
5774 ) -> ResponseT:
5775 pieces = list(args)
5777 # FROMMEMBER or FROMLONLAT
5778 if kwargs["member"] is None:
5779 if kwargs["longitude"] is None or kwargs["latitude"] is None:
5780 raise DataError("GEOSEARCH must have member or longitude and latitude")
5781 if kwargs["member"]:
5782 if kwargs["longitude"] or kwargs["latitude"]:
5783 raise DataError(
5784 "GEOSEARCH member and longitude or latitude cant be set together"
5785 )
5786 pieces.extend([b"FROMMEMBER", kwargs["member"]])
5787 if kwargs["longitude"] is not None and kwargs["latitude"] is not None:
5788 pieces.extend([b"FROMLONLAT", kwargs["longitude"], kwargs["latitude"]])
5790 # BYRADIUS or BYBOX
5791 if kwargs["radius"] is None:
5792 if kwargs["width"] is None or kwargs["height"] is None:
5793 raise DataError("GEOSEARCH must have radius or width and height")
5794 if kwargs["unit"] is None:
5795 raise DataError("GEOSEARCH must have unit")
5796 if kwargs["unit"].lower() not in ("m", "km", "mi", "ft"):
5797 raise DataError("GEOSEARCH invalid unit")
5798 if kwargs["radius"]:
5799 if kwargs["width"] or kwargs["height"]:
5800 raise DataError(
5801 "GEOSEARCH radius and width or height cant be set together"
5802 )
5803 pieces.extend([b"BYRADIUS", kwargs["radius"], kwargs["unit"]])
5804 if kwargs["width"] and kwargs["height"]:
5805 pieces.extend([b"BYBOX", kwargs["width"], kwargs["height"], kwargs["unit"]])
5807 # sort
5808 if kwargs["sort"]:
5809 if kwargs["sort"].upper() == "ASC":
5810 pieces.append(b"ASC")
5811 elif kwargs["sort"].upper() == "DESC":
5812 pieces.append(b"DESC")
5813 else:
5814 raise DataError("GEOSEARCH invalid sort")
5816 # count any
5817 if kwargs["count"]:
5818 pieces.extend([b"COUNT", kwargs["count"]])
5819 if kwargs["any"]:
5820 pieces.append(b"ANY")
5821 elif kwargs["any"]:
5822 raise DataError("GEOSEARCH ``any`` can't be provided without count")
5824 # other properties
5825 for arg_name, byte_repr in (
5826 ("withdist", b"WITHDIST"),
5827 ("withcoord", b"WITHCOORD"),
5828 ("withhash", b"WITHHASH"),
5829 ("store_dist", b"STOREDIST"),
5830 ):
5831 if kwargs[arg_name]:
5832 pieces.append(byte_repr)
5834 kwargs["keys"] = [args[0] if command == "GEOSEARCH" else args[1]]
5836 return self.execute_command(command, *pieces, **kwargs)
5839AsyncGeoCommands = GeoCommands
5842class ModuleCommands(CommandsProtocol):
5843 """
5844 Redis Module commands.
5845 see: https://redis.io/topics/modules-intro
5846 """
5848 def module_load(self, path, *args) -> ResponseT:
5849 """
5850 Loads the module from ``path``.
5851 Passes all ``*args`` to the module, during loading.
5852 Raises ``ModuleError`` if a module is not found at ``path``.
5854 For more information see https://redis.io/commands/module-load
5855 """
5856 return self.execute_command("MODULE LOAD", path, *args)
5858 def module_loadex(
5859 self,
5860 path: str,
5861 options: Optional[List[str]] = None,
5862 args: Optional[List[str]] = None,
5863 ) -> ResponseT:
5864 """
5865 Loads a module from a dynamic library at runtime with configuration directives.
5867 For more information see https://redis.io/commands/module-loadex
5868 """
5869 pieces = []
5870 if options is not None:
5871 pieces.append("CONFIG")
5872 pieces.extend(options)
5873 if args is not None:
5874 pieces.append("ARGS")
5875 pieces.extend(args)
5877 return self.execute_command("MODULE LOADEX", path, *pieces)
5879 def module_unload(self, name) -> ResponseT:
5880 """
5881 Unloads the module ``name``.
5882 Raises ``ModuleError`` if ``name`` is not in loaded modules.
5884 For more information see https://redis.io/commands/module-unload
5885 """
5886 return self.execute_command("MODULE UNLOAD", name)
5888 def module_list(self) -> ResponseT:
5889 """
5890 Returns a list of dictionaries containing the name and version of
5891 all loaded modules.
5893 For more information see https://redis.io/commands/module-list
5894 """
5895 return self.execute_command("MODULE LIST")
5897 def command_info(self) -> None:
5898 raise NotImplementedError(
5899 "COMMAND INFO is intentionally not implemented in the client."
5900 )
5902 def command_count(self) -> ResponseT:
5903 return self.execute_command("COMMAND COUNT")
5905 def command_getkeys(self, *args) -> ResponseT:
5906 return self.execute_command("COMMAND GETKEYS", *args)
5908 def command(self) -> ResponseT:
5909 return self.execute_command("COMMAND")
5912class Script:
5913 """
5914 An executable Lua script object returned by ``register_script``
5915 """
5917 def __init__(self, registered_client, script):
5918 self.registered_client = registered_client
5919 self.script = script
5920 # Precalculate and store the SHA1 hex digest of the script.
5922 if isinstance(script, str):
5923 # We need the encoding from the client in order to generate an
5924 # accurate byte representation of the script
5925 encoder = self.get_encoder()
5926 script = encoder.encode(script)
5927 self.sha = hashlib.sha1(script).hexdigest()
5929 def __call__(self, keys=[], args=[], client=None):
5930 "Execute the script, passing any required ``args``"
5931 if client is None:
5932 client = self.registered_client
5933 args = tuple(keys) + tuple(args)
5934 # make sure the Redis server knows about the script
5935 from redis.client import Pipeline
5937 if isinstance(client, Pipeline):
5938 # Make sure the pipeline can register the script before executing.
5939 client.scripts.add(self)
5940 try:
5941 return client.evalsha(self.sha, len(keys), *args)
5942 except NoScriptError:
5943 # Maybe the client is pointed to a different server than the client
5944 # that created this instance?
5945 # Overwrite the sha just in case there was a discrepancy.
5946 self.sha = client.script_load(self.script)
5947 return client.evalsha(self.sha, len(keys), *args)
5949 def get_encoder(self):
5950 """Get the encoder to encode string scripts into bytes."""
5951 try:
5952 return self.registered_client.get_encoder()
5953 except AttributeError:
5954 # DEPRECATED
5955 # In version <=4.1.2, this was the code we used to get the encoder.
5956 # However, after 4.1.2 we added support for scripting in clustered
5957 # redis. ClusteredRedis doesn't have a `.connection_pool` attribute
5958 # so we changed the Script class to use
5959 # `self.registered_client.get_encoder` (see above).
5960 # However, that is technically a breaking change, as consumers who
5961 # use Scripts directly might inject a `registered_client` that
5962 # doesn't have a `.get_encoder` field. This try/except prevents us
5963 # from breaking backward-compatibility. Ideally, it would be
5964 # removed in the next major release.
5965 return self.registered_client.connection_pool.get_encoder()
5968class AsyncModuleCommands(ModuleCommands):
5969 async def command_info(self) -> None:
5970 return super().command_info()
5973class ClusterCommands(CommandsProtocol):
5974 """
5975 Class for Redis Cluster commands
5976 """
5978 def cluster(self, cluster_arg, *args, **kwargs) -> ResponseT:
5979 return self.execute_command(f"CLUSTER {cluster_arg.upper()}", *args, **kwargs)
5981 def readwrite(self, **kwargs) -> ResponseT:
5982 """
5983 Disables read queries for a connection to a Redis Cluster slave node.
5985 For more information see https://redis.io/commands/readwrite
5986 """
5987 return self.execute_command("READWRITE", **kwargs)
5989 def readonly(self, **kwargs) -> ResponseT:
5990 """
5991 Enables read queries for a connection to a Redis Cluster replica node.
5993 For more information see https://redis.io/commands/readonly
5994 """
5995 return self.execute_command("READONLY", **kwargs)
5998AsyncClusterCommands = ClusterCommands
6001class FunctionCommands:
6002 """
6003 Redis Function commands
6004 """
6006 def function_load(
6007 self, code: str, replace: Optional[bool] = False
6008 ) -> Union[Awaitable[str], str]:
6009 """
6010 Load a library to Redis.
6011 :param code: the source code (must start with
6012 Shebang statement that provides a metadata about the library)
6013 :param replace: changes the behavior to overwrite the existing library
6014 with the new contents.
6015 Return the library name that was loaded.
6017 For more information see https://redis.io/commands/function-load
6018 """
6019 pieces = ["REPLACE"] if replace else []
6020 pieces.append(code)
6021 return self.execute_command("FUNCTION LOAD", *pieces)
6023 def function_delete(self, library: str) -> Union[Awaitable[str], str]:
6024 """
6025 Delete the library called ``library`` and all its functions.
6027 For more information see https://redis.io/commands/function-delete
6028 """
6029 return self.execute_command("FUNCTION DELETE", library)
6031 def function_flush(self, mode: str = "SYNC") -> Union[Awaitable[str], str]:
6032 """
6033 Deletes all the libraries.
6035 For more information see https://redis.io/commands/function-flush
6036 """
6037 return self.execute_command("FUNCTION FLUSH", mode)
6039 def function_list(
6040 self, library: Optional[str] = "*", withcode: Optional[bool] = False
6041 ) -> Union[Awaitable[List], List]:
6042 """
6043 Return information about the functions and libraries.
6044 :param library: pecify a pattern for matching library names
6045 :param withcode: cause the server to include the libraries source
6046 implementation in the reply
6047 """
6048 args = ["LIBRARYNAME", library]
6049 if withcode:
6050 args.append("WITHCODE")
6051 return self.execute_command("FUNCTION LIST", *args)
6053 def _fcall(
6054 self, command: str, function, numkeys: int, *keys_and_args: Optional[List]
6055 ) -> Union[Awaitable[str], str]:
6056 return self.execute_command(command, function, numkeys, *keys_and_args)
6058 def fcall(
6059 self, function, numkeys: int, *keys_and_args: Optional[List]
6060 ) -> Union[Awaitable[str], str]:
6061 """
6062 Invoke a function.
6064 For more information see https://redis.io/commands/fcall
6065 """
6066 return self._fcall("FCALL", function, numkeys, *keys_and_args)
6068 def fcall_ro(
6069 self, function, numkeys: int, *keys_and_args: Optional[List]
6070 ) -> Union[Awaitable[str], str]:
6071 """
6072 This is a read-only variant of the FCALL command that cannot
6073 execute commands that modify data.
6075 For more information see https://redis.io/commands/fcal_ro
6076 """
6077 return self._fcall("FCALL_RO", function, numkeys, *keys_and_args)
6079 def function_dump(self) -> Union[Awaitable[str], str]:
6080 """
6081 Return the serialized payload of loaded libraries.
6083 For more information see https://redis.io/commands/function-dump
6084 """
6085 from redis.client import NEVER_DECODE
6087 options = {}
6088 options[NEVER_DECODE] = []
6090 return self.execute_command("FUNCTION DUMP", **options)
6092 def function_restore(
6093 self, payload: str, policy: Optional[str] = "APPEND"
6094 ) -> Union[Awaitable[str], str]:
6095 """
6096 Restore libraries from the serialized ``payload``.
6097 You can use the optional policy argument to provide a policy
6098 for handling existing libraries.
6100 For more information see https://redis.io/commands/function-restore
6101 """
6102 return self.execute_command("FUNCTION RESTORE", payload, policy)
6104 def function_kill(self) -> Union[Awaitable[str], str]:
6105 """
6106 Kill a function that is currently executing.
6108 For more information see https://redis.io/commands/function-kill
6109 """
6110 return self.execute_command("FUNCTION KILL")
6112 def function_stats(self) -> Union[Awaitable[List], List]:
6113 """
6114 Return information about the function that's currently running
6115 and information about the available execution engines.
6117 For more information see https://redis.io/commands/function-stats
6118 """
6119 return self.execute_command("FUNCTION STATS")
6122AsyncFunctionCommands = FunctionCommands
6125class GearsCommands:
6126 def tfunction_load(
6127 self, lib_code: str, replace: bool = False, config: Union[str, None] = None
6128 ) -> ResponseT:
6129 """
6130 Load a new library to RedisGears.
6132 ``lib_code`` - the library code.
6133 ``config`` - a string representation of a JSON object
6134 that will be provided to the library on load time,
6135 for more information refer to
6136 https://github.com/RedisGears/RedisGears/blob/master/docs/function_advance_topics.md#library-configuration
6137 ``replace`` - an optional argument, instructs RedisGears to replace the
6138 function if its already exists
6140 For more information see https://redis.io/commands/tfunction-load/
6141 """
6142 pieces = []
6143 if replace:
6144 pieces.append("REPLACE")
6145 if config is not None:
6146 pieces.extend(["CONFIG", config])
6147 pieces.append(lib_code)
6148 return self.execute_command("TFUNCTION LOAD", *pieces)
6150 def tfunction_delete(self, lib_name: str) -> ResponseT:
6151 """
6152 Delete a library from RedisGears.
6154 ``lib_name`` the library name to delete.
6156 For more information see https://redis.io/commands/tfunction-delete/
6157 """
6158 return self.execute_command("TFUNCTION DELETE", lib_name)
6160 def tfunction_list(
6161 self,
6162 with_code: bool = False,
6163 verbose: int = 0,
6164 lib_name: Union[str, None] = None,
6165 ) -> ResponseT:
6166 """
6167 List the functions with additional information about each function.
6169 ``with_code`` Show libraries code.
6170 ``verbose`` output verbosity level, higher number will increase verbosity level
6171 ``lib_name`` specifying a library name (can be used multiple times to show multiple libraries in a single command) # noqa
6173 For more information see https://redis.io/commands/tfunction-list/
6174 """
6175 pieces = []
6176 if with_code:
6177 pieces.append("WITHCODE")
6178 if verbose >= 1 and verbose <= 3:
6179 pieces.append("v" * verbose)
6180 else:
6181 raise DataError("verbose can be 1, 2 or 3")
6182 if lib_name is not None:
6183 pieces.append("LIBRARY")
6184 pieces.append(lib_name)
6186 return self.execute_command("TFUNCTION LIST", *pieces)
6188 def _tfcall(
6189 self,
6190 lib_name: str,
6191 func_name: str,
6192 keys: KeysT = None,
6193 _async: bool = False,
6194 *args: List,
6195 ) -> ResponseT:
6196 pieces = [f"{lib_name}.{func_name}"]
6197 if keys is not None:
6198 pieces.append(len(keys))
6199 pieces.extend(keys)
6200 else:
6201 pieces.append(0)
6202 if args is not None:
6203 pieces.extend(args)
6204 if _async:
6205 return self.execute_command("TFCALLASYNC", *pieces)
6206 return self.execute_command("TFCALL", *pieces)
6208 def tfcall(
6209 self,
6210 lib_name: str,
6211 func_name: str,
6212 keys: KeysT = None,
6213 *args: List,
6214 ) -> ResponseT:
6215 """
6216 Invoke a function.
6218 ``lib_name`` - the library name contains the function.
6219 ``func_name`` - the function name to run.
6220 ``keys`` - the keys that will be touched by the function.
6221 ``args`` - Additional argument to pass to the function.
6223 For more information see https://redis.io/commands/tfcall/
6224 """
6225 return self._tfcall(lib_name, func_name, keys, False, *args)
6227 def tfcall_async(
6228 self,
6229 lib_name: str,
6230 func_name: str,
6231 keys: KeysT = None,
6232 *args: List,
6233 ) -> ResponseT:
6234 """
6235 Invoke an async function (coroutine).
6237 ``lib_name`` - the library name contains the function.
6238 ``func_name`` - the function name to run.
6239 ``keys`` - the keys that will be touched by the function.
6240 ``args`` - Additional argument to pass to the function.
6242 For more information see https://redis.io/commands/tfcall/
6243 """
6244 return self._tfcall(lib_name, func_name, keys, True, *args)
6247AsyncGearsCommands = GearsCommands
6250class DataAccessCommands(
6251 BasicKeyCommands,
6252 HyperlogCommands,
6253 HashCommands,
6254 GeoCommands,
6255 ListCommands,
6256 ScanCommands,
6257 SetCommands,
6258 StreamCommands,
6259 SortedSetCommands,
6260):
6261 """
6262 A class containing all of the implemented data access redis commands.
6263 This class is to be used as a mixin for synchronous Redis clients.
6264 """
6267class AsyncDataAccessCommands(
6268 AsyncBasicKeyCommands,
6269 AsyncHyperlogCommands,
6270 AsyncHashCommands,
6271 AsyncGeoCommands,
6272 AsyncListCommands,
6273 AsyncScanCommands,
6274 AsyncSetCommands,
6275 AsyncStreamCommands,
6276 AsyncSortedSetCommands,
6277):
6278 """
6279 A class containing all of the implemented data access redis commands.
6280 This class is to be used as a mixin for asynchronous Redis clients.
6281 """
6284class CoreCommands(
6285 ACLCommands,
6286 ClusterCommands,
6287 DataAccessCommands,
6288 ManagementCommands,
6289 ModuleCommands,
6290 PubSubCommands,
6291 ScriptCommands,
6292 FunctionCommands,
6293 GearsCommands,
6294):
6295 """
6296 A class containing all of the implemented redis commands. This class is
6297 to be used as a mixin for synchronous Redis clients.
6298 """
6301class AsyncCoreCommands(
6302 AsyncACLCommands,
6303 AsyncClusterCommands,
6304 AsyncDataAccessCommands,
6305 AsyncManagementCommands,
6306 AsyncModuleCommands,
6307 AsyncPubSubCommands,
6308 AsyncScriptCommands,
6309 AsyncFunctionCommands,
6310 AsyncGearsCommands,
6311):
6312 """
6313 A class containing all of the implemented redis commands. This class is
6314 to be used as a mixin for asynchronous Redis clients.
6315 """