Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/redis/commands/core.py: 22%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# from __future__ import annotations
3import datetime
4import hashlib
5import warnings
6from enum import Enum
7from typing import (
8 TYPE_CHECKING,
9 Any,
10 AsyncIterator,
11 Awaitable,
12 Callable,
13 Dict,
14 Iterable,
15 Iterator,
16 List,
17 Literal,
18 Mapping,
19 Optional,
20 Sequence,
21 Set,
22 Tuple,
23 Union,
24)
26from redis.exceptions import ConnectionError, DataError, NoScriptError, RedisError
27from redis.typing import (
28 AbsExpiryT,
29 AnyKeyT,
30 BitfieldOffsetT,
31 ChannelT,
32 CommandsProtocol,
33 ConsumerT,
34 EncodableT,
35 ExpiryT,
36 FieldT,
37 GroupT,
38 KeysT,
39 KeyT,
40 Number,
41 PatternT,
42 ResponseT,
43 ScriptTextT,
44 StreamIdT,
45 TimeoutSecT,
46 ZScoreBoundT,
47)
48from redis.utils import (
49 deprecated_function,
50 extract_expire_flags,
51)
53from .helpers import list_or_args
55if TYPE_CHECKING:
56 import redis.asyncio.client
57 import redis.client
60class ACLCommands(CommandsProtocol):
61 """
62 Redis Access Control List (ACL) commands.
63 see: https://redis.io/topics/acl
64 """
66 def acl_cat(self, category: Optional[str] = None, **kwargs) -> ResponseT:
67 """
68 Returns a list of categories or commands within a category.
70 If ``category`` is not supplied, returns a list of all categories.
71 If ``category`` is supplied, returns a list of all commands within
72 that category.
74 For more information, see https://redis.io/commands/acl-cat
75 """
76 pieces: list[EncodableT] = [category] if category else []
77 return self.execute_command("ACL CAT", *pieces, **kwargs)
79 def acl_dryrun(self, username, *args, **kwargs):
80 """
81 Simulate the execution of a given command by a given ``username``.
83 For more information, see https://redis.io/commands/acl-dryrun
84 """
85 return self.execute_command("ACL DRYRUN", username, *args, **kwargs)
87 def acl_deluser(self, *username: str, **kwargs) -> ResponseT:
88 """
89 Delete the ACL for the specified ``username``\\s
91 For more information, see https://redis.io/commands/acl-deluser
92 """
93 return self.execute_command("ACL DELUSER", *username, **kwargs)
95 def acl_genpass(self, bits: Optional[int] = None, **kwargs) -> ResponseT:
96 """Generate a random password value.
97 If ``bits`` is supplied then use this number of bits, rounded to
98 the next multiple of 4.
99 See: https://redis.io/commands/acl-genpass
100 """
101 pieces = []
102 if bits is not None:
103 try:
104 b = int(bits)
105 if b < 0 or b > 4096:
106 raise ValueError
107 pieces.append(b)
108 except ValueError:
109 raise DataError(
110 "genpass optionally accepts a bits argument, between 0 and 4096."
111 )
112 return self.execute_command("ACL GENPASS", *pieces, **kwargs)
114 def acl_getuser(self, username: str, **kwargs) -> ResponseT:
115 """
116 Get the ACL details for the specified ``username``.
118 If ``username`` does not exist, return None
120 For more information, see https://redis.io/commands/acl-getuser
121 """
122 return self.execute_command("ACL GETUSER", username, **kwargs)
124 def acl_help(self, **kwargs) -> ResponseT:
125 """The ACL HELP command returns helpful text describing
126 the different subcommands.
128 For more information, see https://redis.io/commands/acl-help
129 """
130 return self.execute_command("ACL HELP", **kwargs)
132 def acl_list(self, **kwargs) -> ResponseT:
133 """
134 Return a list of all ACLs on the server
136 For more information, see https://redis.io/commands/acl-list
137 """
138 return self.execute_command("ACL LIST", **kwargs)
140 def acl_log(self, count: Optional[int] = None, **kwargs) -> ResponseT:
141 """
142 Get ACL logs as a list.
143 :param int count: Get logs[0:count].
144 :rtype: List.
146 For more information, see https://redis.io/commands/acl-log
147 """
148 args = []
149 if count is not None:
150 if not isinstance(count, int):
151 raise DataError("ACL LOG count must be an integer")
152 args.append(count)
154 return self.execute_command("ACL LOG", *args, **kwargs)
156 def acl_log_reset(self, **kwargs) -> ResponseT:
157 """
158 Reset ACL logs.
159 :rtype: Boolean.
161 For more information, see https://redis.io/commands/acl-log
162 """
163 args = [b"RESET"]
164 return self.execute_command("ACL LOG", *args, **kwargs)
166 def acl_load(self, **kwargs) -> ResponseT:
167 """
168 Load ACL rules from the configured ``aclfile``.
170 Note that the server must be configured with the ``aclfile``
171 directive to be able to load ACL rules from an aclfile.
173 For more information, see https://redis.io/commands/acl-load
174 """
175 return self.execute_command("ACL LOAD", **kwargs)
177 def acl_save(self, **kwargs) -> ResponseT:
178 """
179 Save ACL rules to the configured ``aclfile``.
181 Note that the server must be configured with the ``aclfile``
182 directive to be able to save ACL rules to an aclfile.
184 For more information, see https://redis.io/commands/acl-save
185 """
186 return self.execute_command("ACL SAVE", **kwargs)
188 def acl_setuser(
189 self,
190 username: str,
191 enabled: bool = False,
192 nopass: bool = False,
193 passwords: Optional[Union[str, Iterable[str]]] = None,
194 hashed_passwords: Optional[Union[str, Iterable[str]]] = None,
195 categories: Optional[Iterable[str]] = None,
196 commands: Optional[Iterable[str]] = None,
197 keys: Optional[Iterable[KeyT]] = None,
198 channels: Optional[Iterable[ChannelT]] = None,
199 selectors: Optional[Iterable[Tuple[str, KeyT]]] = None,
200 reset: bool = False,
201 reset_keys: bool = False,
202 reset_channels: bool = False,
203 reset_passwords: bool = False,
204 **kwargs,
205 ) -> ResponseT:
206 """
207 Create or update an ACL user.
209 Create or update the ACL for `username`. If the user already exists,
210 the existing ACL is completely overwritten and replaced with the
211 specified values.
213 For more information, see https://redis.io/commands/acl-setuser
215 Args:
216 username: The name of the user whose ACL is to be created or updated.
217 enabled: Indicates whether the user should be allowed to authenticate.
218 Defaults to `False`.
219 nopass: Indicates whether the user can authenticate without a password.
220 This cannot be `True` if `passwords` are also specified.
221 passwords: A list of plain text passwords to add to or remove from the user.
222 Each password must be prefixed with a '+' to add or a '-' to
223 remove. For convenience, a single prefixed string can be used
224 when adding or removing a single password.
225 hashed_passwords: A list of SHA-256 hashed passwords to add to or remove
226 from the user. Each hashed password must be prefixed with
227 a '+' to add or a '-' to remove. For convenience, a single
228 prefixed string can be used when adding or removing a
229 single password.
230 categories: A list of strings representing category permissions. Each string
231 must be prefixed with either a '+' to add the category
232 permission or a '-' to remove the category permission.
233 commands: A list of strings representing command permissions. Each string
234 must be prefixed with either a '+' to add the command permission
235 or a '-' to remove the command permission.
236 keys: A list of key patterns to grant the user access to. Key patterns allow
237 ``'*'`` to support wildcard matching. For example, ``'*'`` grants
238 access to all keys while ``'cache:*'`` grants access to all keys that
239 are prefixed with ``cache:``.
240 `keys` should not be prefixed with a ``'~'``.
241 reset: Indicates whether the user should be fully reset prior to applying
242 the new ACL. Setting this to `True` will remove all existing
243 passwords, flags, and privileges from the user and then apply the
244 specified rules. If `False`, the user's existing passwords, flags,
245 and privileges will be kept and any new specified rules will be
246 applied on top.
247 reset_keys: Indicates whether the user's key permissions should be reset
248 prior to applying any new key permissions specified in `keys`.
249 If `False`, the user's existing key permissions will be kept and
250 any new specified key permissions will be applied on top.
251 reset_channels: Indicates whether the user's channel permissions should be
252 reset prior to applying any new channel permissions
253 specified in `channels`. If `False`, the user's existing
254 channel permissions will be kept and any new specified
255 channel permissions will be applied on top.
256 reset_passwords: Indicates whether to remove all existing passwords and the
257 `nopass` flag from the user prior to applying any new
258 passwords specified in `passwords` or `hashed_passwords`.
259 If `False`, the user's existing passwords and `nopass`
260 status will be kept and any new specified passwords or
261 hashed passwords will be applied on top.
262 """
263 encoder = self.get_encoder()
264 pieces: List[EncodableT] = [username]
266 if reset:
267 pieces.append(b"reset")
269 if reset_keys:
270 pieces.append(b"resetkeys")
272 if reset_channels:
273 pieces.append(b"resetchannels")
275 if reset_passwords:
276 pieces.append(b"resetpass")
278 if enabled:
279 pieces.append(b"on")
280 else:
281 pieces.append(b"off")
283 if (passwords or hashed_passwords) and nopass:
284 raise DataError(
285 "Cannot set 'nopass' and supply 'passwords' or 'hashed_passwords'"
286 )
288 if passwords:
289 # as most users will have only one password, allow remove_passwords
290 # to be specified as a simple string or a list
291 passwords = list_or_args(passwords, [])
292 for i, password in enumerate(passwords):
293 password = encoder.encode(password)
294 if password.startswith(b"+"):
295 pieces.append(b">%s" % password[1:])
296 elif password.startswith(b"-"):
297 pieces.append(b"<%s" % password[1:])
298 else:
299 raise DataError(
300 f"Password {i} must be prefixed with a "
301 f'"+" to add or a "-" to remove'
302 )
304 if hashed_passwords:
305 # as most users will have only one password, allow remove_passwords
306 # to be specified as a simple string or a list
307 hashed_passwords = list_or_args(hashed_passwords, [])
308 for i, hashed_password in enumerate(hashed_passwords):
309 hashed_password = encoder.encode(hashed_password)
310 if hashed_password.startswith(b"+"):
311 pieces.append(b"#%s" % hashed_password[1:])
312 elif hashed_password.startswith(b"-"):
313 pieces.append(b"!%s" % hashed_password[1:])
314 else:
315 raise DataError(
316 f"Hashed password {i} must be prefixed with a "
317 f'"+" to add or a "-" to remove'
318 )
320 if nopass:
321 pieces.append(b"nopass")
323 if categories:
324 for category in categories:
325 category = encoder.encode(category)
326 # categories can be prefixed with one of (+@, +, -@, -)
327 if category.startswith(b"+@"):
328 pieces.append(category)
329 elif category.startswith(b"+"):
330 pieces.append(b"+@%s" % category[1:])
331 elif category.startswith(b"-@"):
332 pieces.append(category)
333 elif category.startswith(b"-"):
334 pieces.append(b"-@%s" % category[1:])
335 else:
336 raise DataError(
337 f'Category "{encoder.decode(category, force=True)}" '
338 'must be prefixed with "+" or "-"'
339 )
340 if commands:
341 for cmd in commands:
342 cmd = encoder.encode(cmd)
343 if not cmd.startswith(b"+") and not cmd.startswith(b"-"):
344 raise DataError(
345 f'Command "{encoder.decode(cmd, force=True)}" '
346 'must be prefixed with "+" or "-"'
347 )
348 pieces.append(cmd)
350 if keys:
351 for key in keys:
352 key = encoder.encode(key)
353 if not key.startswith(b"%") and not key.startswith(b"~"):
354 key = b"~%s" % key
355 pieces.append(key)
357 if channels:
358 for channel in channels:
359 channel = encoder.encode(channel)
360 pieces.append(b"&%s" % channel)
362 if selectors:
363 for cmd, key in selectors:
364 cmd = encoder.encode(cmd)
365 if not cmd.startswith(b"+") and not cmd.startswith(b"-"):
366 raise DataError(
367 f'Command "{encoder.decode(cmd, force=True)}" '
368 'must be prefixed with "+" or "-"'
369 )
371 key = encoder.encode(key)
372 if not key.startswith(b"%") and not key.startswith(b"~"):
373 key = b"~%s" % key
375 pieces.append(b"(%s %s)" % (cmd, key))
377 return self.execute_command("ACL SETUSER", *pieces, **kwargs)
379 def acl_users(self, **kwargs) -> ResponseT:
380 """Returns a list of all registered users on the server.
382 For more information, see https://redis.io/commands/acl-users
383 """
384 return self.execute_command("ACL USERS", **kwargs)
386 def acl_whoami(self, **kwargs) -> ResponseT:
387 """Get the username for the current connection
389 For more information, see https://redis.io/commands/acl-whoami
390 """
391 return self.execute_command("ACL WHOAMI", **kwargs)
394AsyncACLCommands = ACLCommands
397class ManagementCommands(CommandsProtocol):
398 """
399 Redis management commands
400 """
402 def auth(self, password: str, username: Optional[str] = None, **kwargs):
403 """
404 Authenticates the user. If you do not pass username, Redis will try to
405 authenticate for the "default" user. If you do pass username, it will
406 authenticate for the given user.
407 For more information, see https://redis.io/commands/auth
408 """
409 pieces = []
410 if username is not None:
411 pieces.append(username)
412 pieces.append(password)
413 return self.execute_command("AUTH", *pieces, **kwargs)
415 def bgrewriteaof(self, **kwargs):
416 """Tell the Redis server to rewrite the AOF file from data in memory.
418 For more information, see https://redis.io/commands/bgrewriteaof
419 """
420 return self.execute_command("BGREWRITEAOF", **kwargs)
422 def bgsave(self, schedule: bool = True, **kwargs) -> ResponseT:
423 """
424 Tell the Redis server to save its data to disk. Unlike save(),
425 this method is asynchronous and returns immediately.
427 For more information, see https://redis.io/commands/bgsave
428 """
429 pieces = []
430 if schedule:
431 pieces.append("SCHEDULE")
432 return self.execute_command("BGSAVE", *pieces, **kwargs)
434 def role(self) -> ResponseT:
435 """
436 Provide information on the role of a Redis instance in
437 the context of replication, by returning if the instance
438 is currently a master, slave, or sentinel.
440 For more information, see https://redis.io/commands/role
441 """
442 return self.execute_command("ROLE")
444 def client_kill(self, address: str, **kwargs) -> ResponseT:
445 """Disconnects the client at ``address`` (ip:port)
447 For more information, see https://redis.io/commands/client-kill
448 """
449 return self.execute_command("CLIENT KILL", address, **kwargs)
451 def client_kill_filter(
452 self,
453 _id: Optional[str] = None,
454 _type: Optional[str] = None,
455 addr: Optional[str] = None,
456 skipme: Optional[bool] = None,
457 laddr: Optional[bool] = None,
458 user: Optional[str] = None,
459 maxage: Optional[int] = None,
460 **kwargs,
461 ) -> ResponseT:
462 """
463 Disconnects client(s) using a variety of filter options
464 :param _id: Kills a client by its unique ID field
465 :param _type: Kills a client by type where type is one of 'normal',
466 'master', 'slave' or 'pubsub'
467 :param addr: Kills a client by its 'address:port'
468 :param skipme: If True, then the client calling the command
469 will not get killed even if it is identified by one of the filter
470 options. If skipme is not provided, the server defaults to skipme=True
471 :param laddr: Kills a client by its 'local (bind) address:port'
472 :param user: Kills a client for a specific user name
473 :param maxage: Kills clients that are older than the specified age in seconds
474 """
475 args = []
476 if _type is not None:
477 client_types = ("normal", "master", "slave", "pubsub")
478 if str(_type).lower() not in client_types:
479 raise DataError(f"CLIENT KILL type must be one of {client_types!r}")
480 args.extend((b"TYPE", _type))
481 if skipme is not None:
482 if not isinstance(skipme, bool):
483 raise DataError("CLIENT KILL skipme must be a bool")
484 if skipme:
485 args.extend((b"SKIPME", b"YES"))
486 else:
487 args.extend((b"SKIPME", b"NO"))
488 if _id is not None:
489 args.extend((b"ID", _id))
490 if addr is not None:
491 args.extend((b"ADDR", addr))
492 if laddr is not None:
493 args.extend((b"LADDR", laddr))
494 if user is not None:
495 args.extend((b"USER", user))
496 if maxage is not None:
497 args.extend((b"MAXAGE", maxage))
498 if not args:
499 raise DataError(
500 "CLIENT KILL <filter> <value> ... ... <filter> "
501 "<value> must specify at least one filter"
502 )
503 return self.execute_command("CLIENT KILL", *args, **kwargs)
505 def client_info(self, **kwargs) -> ResponseT:
506 """
507 Returns information and statistics about the current
508 client connection.
510 For more information, see https://redis.io/commands/client-info
511 """
512 return self.execute_command("CLIENT INFO", **kwargs)
514 def client_list(
515 self, _type: Optional[str] = None, client_id: List[EncodableT] = [], **kwargs
516 ) -> ResponseT:
517 """
518 Returns a list of currently connected clients.
519 If type of client specified, only that type will be returned.
521 :param _type: optional. one of the client types (normal, master,
522 replica, pubsub)
523 :param client_id: optional. a list of client ids
525 For more information, see https://redis.io/commands/client-list
526 """
527 args = []
528 if _type is not None:
529 client_types = ("normal", "master", "replica", "pubsub")
530 if str(_type).lower() not in client_types:
531 raise DataError(f"CLIENT LIST _type must be one of {client_types!r}")
532 args.append(b"TYPE")
533 args.append(_type)
534 if not isinstance(client_id, list):
535 raise DataError("client_id must be a list")
536 if client_id:
537 args.append(b"ID")
538 args += client_id
539 return self.execute_command("CLIENT LIST", *args, **kwargs)
541 def client_getname(self, **kwargs) -> ResponseT:
542 """
543 Returns the current connection name
545 For more information, see https://redis.io/commands/client-getname
546 """
547 return self.execute_command("CLIENT GETNAME", **kwargs)
549 def client_getredir(self, **kwargs) -> ResponseT:
550 """
551 Returns the ID (an integer) of the client to whom we are
552 redirecting tracking notifications.
554 see: https://redis.io/commands/client-getredir
555 """
556 return self.execute_command("CLIENT GETREDIR", **kwargs)
558 def client_reply(
559 self, reply: Union[Literal["ON"], Literal["OFF"], Literal["SKIP"]], **kwargs
560 ) -> ResponseT:
561 """
562 Enable and disable redis server replies.
564 ``reply`` Must be ON OFF or SKIP,
565 ON - The default most with server replies to commands
566 OFF - Disable server responses to commands
567 SKIP - Skip the response of the immediately following command.
569 Note: When setting OFF or SKIP replies, you will need a client object
570 with a timeout specified in seconds, and will need to catch the
571 TimeoutError.
572 The test_client_reply unit test illustrates this, and
573 conftest.py has a client with a timeout.
575 See https://redis.io/commands/client-reply
576 """
577 replies = ["ON", "OFF", "SKIP"]
578 if reply not in replies:
579 raise DataError(f"CLIENT REPLY must be one of {replies!r}")
580 return self.execute_command("CLIENT REPLY", reply, **kwargs)
582 def client_id(self, **kwargs) -> ResponseT:
583 """
584 Returns the current connection id
586 For more information, see https://redis.io/commands/client-id
587 """
588 return self.execute_command("CLIENT ID", **kwargs)
590 def client_tracking_on(
591 self,
592 clientid: Optional[int] = None,
593 prefix: Sequence[KeyT] = [],
594 bcast: bool = False,
595 optin: bool = False,
596 optout: bool = False,
597 noloop: bool = False,
598 ) -> ResponseT:
599 """
600 Turn on the tracking mode.
601 For more information, about the options look at client_tracking func.
603 See https://redis.io/commands/client-tracking
604 """
605 return self.client_tracking(
606 True, clientid, prefix, bcast, optin, optout, noloop
607 )
609 def client_tracking_off(
610 self,
611 clientid: Optional[int] = None,
612 prefix: Sequence[KeyT] = [],
613 bcast: bool = False,
614 optin: bool = False,
615 optout: bool = False,
616 noloop: bool = False,
617 ) -> ResponseT:
618 """
619 Turn off the tracking mode.
620 For more information, about the options look at client_tracking func.
622 See https://redis.io/commands/client-tracking
623 """
624 return self.client_tracking(
625 False, clientid, prefix, bcast, optin, optout, noloop
626 )
628 def client_tracking(
629 self,
630 on: bool = True,
631 clientid: Optional[int] = None,
632 prefix: Sequence[KeyT] = [],
633 bcast: bool = False,
634 optin: bool = False,
635 optout: bool = False,
636 noloop: bool = False,
637 **kwargs,
638 ) -> ResponseT:
639 """
640 Enables the tracking feature of the Redis server, that is used
641 for server assisted client side caching.
643 ``on`` indicate for tracking on or tracking off. The default is on.
645 ``clientid`` send invalidation messages to the connection with
646 the specified ID.
648 ``bcast`` enable tracking in broadcasting mode. In this mode
649 invalidation messages are reported for all the prefixes
650 specified, regardless of the keys requested by the connection.
652 ``optin`` when broadcasting is NOT active, normally don't track
653 keys in read only commands, unless they are called immediately
654 after a CLIENT CACHING yes command.
656 ``optout`` when broadcasting is NOT active, normally track keys in
657 read only commands, unless they are called immediately after a
658 CLIENT CACHING no command.
660 ``noloop`` don't send notifications about keys modified by this
661 connection itself.
663 ``prefix`` for broadcasting, register a given key prefix, so that
664 notifications will be provided only for keys starting with this string.
666 See https://redis.io/commands/client-tracking
667 """
669 if len(prefix) != 0 and bcast is False:
670 raise DataError("Prefix can only be used with bcast")
672 pieces = ["ON"] if on else ["OFF"]
673 if clientid is not None:
674 pieces.extend(["REDIRECT", clientid])
675 for p in prefix:
676 pieces.extend(["PREFIX", p])
677 if bcast:
678 pieces.append("BCAST")
679 if optin:
680 pieces.append("OPTIN")
681 if optout:
682 pieces.append("OPTOUT")
683 if noloop:
684 pieces.append("NOLOOP")
686 return self.execute_command("CLIENT TRACKING", *pieces)
688 def client_trackinginfo(self, **kwargs) -> ResponseT:
689 """
690 Returns the information about the current client connection's
691 use of the server assisted client side cache.
693 See https://redis.io/commands/client-trackinginfo
694 """
695 return self.execute_command("CLIENT TRACKINGINFO", **kwargs)
697 def client_setname(self, name: str, **kwargs) -> ResponseT:
698 """
699 Sets the current connection name
701 For more information, see https://redis.io/commands/client-setname
703 .. note::
704 This method sets client name only for **current** connection.
706 If you want to set a common name for all connections managed
707 by this client, use ``client_name`` constructor argument.
708 """
709 return self.execute_command("CLIENT SETNAME", name, **kwargs)
711 def client_setinfo(self, attr: str, value: str, **kwargs) -> ResponseT:
712 """
713 Sets the current connection library name or version
714 For mor information see https://redis.io/commands/client-setinfo
715 """
716 return self.execute_command("CLIENT SETINFO", attr, value, **kwargs)
718 def client_unblock(
719 self, client_id: int, error: bool = False, **kwargs
720 ) -> ResponseT:
721 """
722 Unblocks a connection by its client id.
723 If ``error`` is True, unblocks the client with a special error message.
724 If ``error`` is False (default), the client is unblocked using the
725 regular timeout mechanism.
727 For more information, see https://redis.io/commands/client-unblock
728 """
729 args = ["CLIENT UNBLOCK", int(client_id)]
730 if error:
731 args.append(b"ERROR")
732 return self.execute_command(*args, **kwargs)
734 def client_pause(self, timeout: int, all: bool = True, **kwargs) -> ResponseT:
735 """
736 Suspend all the Redis clients for the specified amount of time.
739 For more information, see https://redis.io/commands/client-pause
741 Args:
742 timeout: milliseconds to pause clients
743 all: If true (default) all client commands are blocked.
744 otherwise, clients are only blocked if they attempt to execute
745 a write command.
747 For the WRITE mode, some commands have special behavior:
749 * EVAL/EVALSHA: Will block client for all scripts.
750 * PUBLISH: Will block client.
751 * PFCOUNT: Will block client.
752 * WAIT: Acknowledgments will be delayed, so this command will
753 appear blocked.
754 """
755 args = ["CLIENT PAUSE", str(timeout)]
756 if not isinstance(timeout, int):
757 raise DataError("CLIENT PAUSE timeout must be an integer")
758 if not all:
759 args.append("WRITE")
760 return self.execute_command(*args, **kwargs)
762 def client_unpause(self, **kwargs) -> ResponseT:
763 """
764 Unpause all redis clients
766 For more information, see https://redis.io/commands/client-unpause
767 """
768 return self.execute_command("CLIENT UNPAUSE", **kwargs)
770 def client_no_evict(self, mode: str) -> Union[Awaitable[str], str]:
771 """
772 Sets the client eviction mode for the current connection.
774 For more information, see https://redis.io/commands/client-no-evict
775 """
776 return self.execute_command("CLIENT NO-EVICT", mode)
778 def client_no_touch(self, mode: str) -> Union[Awaitable[str], str]:
779 """
780 # The command controls whether commands sent by the client will alter
781 # the LRU/LFU of the keys they access.
782 # When turned on, the current client will not change LFU/LRU stats,
783 # unless it sends the TOUCH command.
785 For more information, see https://redis.io/commands/client-no-touch
786 """
787 return self.execute_command("CLIENT NO-TOUCH", mode)
789 def command(self, **kwargs):
790 """
791 Returns dict reply of details about all Redis commands.
793 For more information, see https://redis.io/commands/command
794 """
795 return self.execute_command("COMMAND", **kwargs)
797 def command_info(self, **kwargs) -> None:
798 raise NotImplementedError(
799 "COMMAND INFO is intentionally not implemented in the client."
800 )
802 def command_count(self, **kwargs) -> ResponseT:
803 return self.execute_command("COMMAND COUNT", **kwargs)
805 def command_list(
806 self,
807 module: Optional[str] = None,
808 category: Optional[str] = None,
809 pattern: Optional[str] = None,
810 ) -> ResponseT:
811 """
812 Return an array of the server's command names.
813 You can use one of the following filters:
814 ``module``: get the commands that belong to the module
815 ``category``: get the commands in the ACL category
816 ``pattern``: get the commands that match the given pattern
818 For more information, see https://redis.io/commands/command-list/
819 """
820 pieces = []
821 if module is not None:
822 pieces.extend(["MODULE", module])
823 if category is not None:
824 pieces.extend(["ACLCAT", category])
825 if pattern is not None:
826 pieces.extend(["PATTERN", pattern])
828 if pieces:
829 pieces.insert(0, "FILTERBY")
831 return self.execute_command("COMMAND LIST", *pieces)
833 def command_getkeysandflags(self, *args: str) -> List[Union[str, List[str]]]:
834 """
835 Returns array of keys from a full Redis command and their usage flags.
837 For more information, see https://redis.io/commands/command-getkeysandflags
838 """
839 return self.execute_command("COMMAND GETKEYSANDFLAGS", *args)
841 def command_docs(self, *args):
842 """
843 This function throws a NotImplementedError since it is intentionally
844 not supported.
845 """
846 raise NotImplementedError(
847 "COMMAND DOCS is intentionally not implemented in the client."
848 )
850 def config_get(
851 self, pattern: PatternT = "*", *args: PatternT, **kwargs
852 ) -> ResponseT:
853 """
854 Return a dictionary of configuration based on the ``pattern``
856 For more information, see https://redis.io/commands/config-get
857 """
858 return self.execute_command("CONFIG GET", pattern, *args, **kwargs)
860 def config_set(
861 self,
862 name: KeyT,
863 value: EncodableT,
864 *args: Union[KeyT, EncodableT],
865 **kwargs,
866 ) -> ResponseT:
867 """Set config item ``name`` with ``value``
869 For more information, see https://redis.io/commands/config-set
870 """
871 return self.execute_command("CONFIG SET", name, value, *args, **kwargs)
873 def config_resetstat(self, **kwargs) -> ResponseT:
874 """
875 Reset runtime statistics
877 For more information, see https://redis.io/commands/config-resetstat
878 """
879 return self.execute_command("CONFIG RESETSTAT", **kwargs)
881 def config_rewrite(self, **kwargs) -> ResponseT:
882 """
883 Rewrite config file with the minimal change to reflect running config.
885 For more information, see https://redis.io/commands/config-rewrite
886 """
887 return self.execute_command("CONFIG REWRITE", **kwargs)
889 def dbsize(self, **kwargs) -> ResponseT:
890 """
891 Returns the number of keys in the current database
893 For more information, see https://redis.io/commands/dbsize
894 """
895 return self.execute_command("DBSIZE", **kwargs)
897 def debug_object(self, key: KeyT, **kwargs) -> ResponseT:
898 """
899 Returns version specific meta information about a given key
901 For more information, see https://redis.io/commands/debug-object
902 """
903 return self.execute_command("DEBUG OBJECT", key, **kwargs)
905 def debug_segfault(self, **kwargs) -> None:
906 raise NotImplementedError(
907 """
908 DEBUG SEGFAULT is intentionally not implemented in the client.
910 For more information, see https://redis.io/commands/debug-segfault
911 """
912 )
914 def echo(self, value: EncodableT, **kwargs) -> ResponseT:
915 """
916 Echo the string back from the server
918 For more information, see https://redis.io/commands/echo
919 """
920 return self.execute_command("ECHO", value, **kwargs)
922 def flushall(self, asynchronous: bool = False, **kwargs) -> ResponseT:
923 """
924 Delete all keys in all databases on the current host.
926 ``asynchronous`` indicates whether the operation is
927 executed asynchronously by the server.
929 For more information, see https://redis.io/commands/flushall
930 """
931 args = []
932 if asynchronous:
933 args.append(b"ASYNC")
934 return self.execute_command("FLUSHALL", *args, **kwargs)
936 def flushdb(self, asynchronous: bool = False, **kwargs) -> ResponseT:
937 """
938 Delete all keys in the current database.
940 ``asynchronous`` indicates whether the operation is
941 executed asynchronously by the server.
943 For more information, see https://redis.io/commands/flushdb
944 """
945 args = []
946 if asynchronous:
947 args.append(b"ASYNC")
948 return self.execute_command("FLUSHDB", *args, **kwargs)
950 def sync(self) -> ResponseT:
951 """
952 Initiates a replication stream from the master.
954 For more information, see https://redis.io/commands/sync
955 """
956 from redis.client import NEVER_DECODE
958 options = {}
959 options[NEVER_DECODE] = []
960 return self.execute_command("SYNC", **options)
962 def psync(self, replicationid: str, offset: int):
963 """
964 Initiates a replication stream from the master.
965 Newer version for `sync`.
967 For more information, see https://redis.io/commands/sync
968 """
969 from redis.client import NEVER_DECODE
971 options = {}
972 options[NEVER_DECODE] = []
973 return self.execute_command("PSYNC", replicationid, offset, **options)
975 def swapdb(self, first: int, second: int, **kwargs) -> ResponseT:
976 """
977 Swap two databases
979 For more information, see https://redis.io/commands/swapdb
980 """
981 return self.execute_command("SWAPDB", first, second, **kwargs)
983 def select(self, index: int, **kwargs) -> ResponseT:
984 """Select the Redis logical database at index.
986 See: https://redis.io/commands/select
987 """
988 return self.execute_command("SELECT", index, **kwargs)
990 def info(self, section: Optional[str] = None, *args: str, **kwargs) -> ResponseT:
991 """
992 Returns a dictionary containing information about the Redis server
994 The ``section`` option can be used to select a specific section
995 of information
997 The section option is not supported by older versions of Redis Server,
998 and will generate ResponseError
1000 For more information, see https://redis.io/commands/info
1001 """
1002 if section is None:
1003 return self.execute_command("INFO", **kwargs)
1004 else:
1005 return self.execute_command("INFO", section, *args, **kwargs)
1007 def lastsave(self, **kwargs) -> ResponseT:
1008 """
1009 Return a Python datetime object representing the last time the
1010 Redis database was saved to disk
1012 For more information, see https://redis.io/commands/lastsave
1013 """
1014 return self.execute_command("LASTSAVE", **kwargs)
1016 def latency_doctor(self):
1017 """Raise a NotImplementedError, as the client will not support LATENCY DOCTOR.
1018 This function is best used within the redis-cli.
1020 For more information, see https://redis.io/commands/latency-doctor
1021 """
1022 raise NotImplementedError(
1023 """
1024 LATENCY DOCTOR is intentionally not implemented in the client.
1026 For more information, see https://redis.io/commands/latency-doctor
1027 """
1028 )
1030 def latency_graph(self):
1031 """Raise a NotImplementedError, as the client will not support LATENCY GRAPH.
1032 This function is best used within the redis-cli.
1034 For more information, see https://redis.io/commands/latency-graph.
1035 """
1036 raise NotImplementedError(
1037 """
1038 LATENCY GRAPH is intentionally not implemented in the client.
1040 For more information, see https://redis.io/commands/latency-graph
1041 """
1042 )
1044 def lolwut(self, *version_numbers: Union[str, float], **kwargs) -> ResponseT:
1045 """
1046 Get the Redis version and a piece of generative computer art
1048 See: https://redis.io/commands/lolwut
1049 """
1050 if version_numbers:
1051 return self.execute_command("LOLWUT VERSION", *version_numbers, **kwargs)
1052 else:
1053 return self.execute_command("LOLWUT", **kwargs)
1055 def reset(self) -> ResponseT:
1056 """Perform a full reset on the connection's server-side context.
1058 See: https://redis.io/commands/reset
1059 """
1060 return self.execute_command("RESET")
1062 def migrate(
1063 self,
1064 host: str,
1065 port: int,
1066 keys: KeysT,
1067 destination_db: int,
1068 timeout: int,
1069 copy: bool = False,
1070 replace: bool = False,
1071 auth: Optional[str] = None,
1072 **kwargs,
1073 ) -> ResponseT:
1074 """
1075 Migrate 1 or more keys from the current Redis server to a different
1076 server specified by the ``host``, ``port`` and ``destination_db``.
1078 The ``timeout``, specified in milliseconds, indicates the maximum
1079 time the connection between the two servers can be idle before the
1080 command is interrupted.
1082 If ``copy`` is True, the specified ``keys`` are NOT deleted from
1083 the source server.
1085 If ``replace`` is True, this operation will overwrite the keys
1086 on the destination server if they exist.
1088 If ``auth`` is specified, authenticate to the destination server with
1089 the password provided.
1091 For more information, see https://redis.io/commands/migrate
1092 """
1093 keys = list_or_args(keys, [])
1094 if not keys:
1095 raise DataError("MIGRATE requires at least one key")
1096 pieces = []
1097 if copy:
1098 pieces.append(b"COPY")
1099 if replace:
1100 pieces.append(b"REPLACE")
1101 if auth:
1102 pieces.append(b"AUTH")
1103 pieces.append(auth)
1104 pieces.append(b"KEYS")
1105 pieces.extend(keys)
1106 return self.execute_command(
1107 "MIGRATE", host, port, "", destination_db, timeout, *pieces, **kwargs
1108 )
1110 def object(self, infotype: str, key: KeyT, **kwargs) -> ResponseT:
1111 """
1112 Return the encoding, idletime, or refcount about the key
1113 """
1114 return self.execute_command(
1115 "OBJECT", infotype, key, infotype=infotype, **kwargs
1116 )
1118 def memory_doctor(self, **kwargs) -> None:
1119 raise NotImplementedError(
1120 """
1121 MEMORY DOCTOR is intentionally not implemented in the client.
1123 For more information, see https://redis.io/commands/memory-doctor
1124 """
1125 )
1127 def memory_help(self, **kwargs) -> None:
1128 raise NotImplementedError(
1129 """
1130 MEMORY HELP is intentionally not implemented in the client.
1132 For more information, see https://redis.io/commands/memory-help
1133 """
1134 )
1136 def memory_stats(self, **kwargs) -> ResponseT:
1137 """
1138 Return a dictionary of memory stats
1140 For more information, see https://redis.io/commands/memory-stats
1141 """
1142 return self.execute_command("MEMORY STATS", **kwargs)
1144 def memory_malloc_stats(self, **kwargs) -> ResponseT:
1145 """
1146 Return an internal statistics report from the memory allocator.
1148 See: https://redis.io/commands/memory-malloc-stats
1149 """
1150 return self.execute_command("MEMORY MALLOC-STATS", **kwargs)
1152 def memory_usage(
1153 self, key: KeyT, samples: Optional[int] = None, **kwargs
1154 ) -> ResponseT:
1155 """
1156 Return the total memory usage for key, its value and associated
1157 administrative overheads.
1159 For nested data structures, ``samples`` is the number of elements to
1160 sample. If left unspecified, the server's default is 5. Use 0 to sample
1161 all elements.
1163 For more information, see https://redis.io/commands/memory-usage
1164 """
1165 args = []
1166 if isinstance(samples, int):
1167 args.extend([b"SAMPLES", samples])
1168 return self.execute_command("MEMORY USAGE", key, *args, **kwargs)
1170 def memory_purge(self, **kwargs) -> ResponseT:
1171 """
1172 Attempts to purge dirty pages for reclamation by allocator
1174 For more information, see https://redis.io/commands/memory-purge
1175 """
1176 return self.execute_command("MEMORY PURGE", **kwargs)
1178 def latency_histogram(self, *args):
1179 """
1180 This function throws a NotImplementedError since it is intentionally
1181 not supported.
1182 """
1183 raise NotImplementedError(
1184 "LATENCY HISTOGRAM is intentionally not implemented in the client."
1185 )
1187 def latency_history(self, event: str) -> ResponseT:
1188 """
1189 Returns the raw data of the ``event``'s latency spikes time series.
1191 For more information, see https://redis.io/commands/latency-history
1192 """
1193 return self.execute_command("LATENCY HISTORY", event)
1195 def latency_latest(self) -> ResponseT:
1196 """
1197 Reports the latest latency events logged.
1199 For more information, see https://redis.io/commands/latency-latest
1200 """
1201 return self.execute_command("LATENCY LATEST")
1203 def latency_reset(self, *events: str) -> ResponseT:
1204 """
1205 Resets the latency spikes time series of all, or only some, events.
1207 For more information, see https://redis.io/commands/latency-reset
1208 """
1209 return self.execute_command("LATENCY RESET", *events)
1211 def ping(self, **kwargs) -> Union[Awaitable[bool], bool]:
1212 """
1213 Ping the Redis server to test connectivity.
1215 Sends a PING command to the Redis server and returns True if the server
1216 responds with "PONG".
1218 This command is useful for:
1219 - Testing whether a connection is still alive
1220 - Verifying the server's ability to serve data
1222 For more information on the underlying ping command see https://redis.io/commands/ping
1223 """
1224 return self.execute_command("PING", **kwargs)
1226 def quit(self, **kwargs) -> ResponseT:
1227 """
1228 Ask the server to close the connection.
1230 For more information, see https://redis.io/commands/quit
1231 """
1232 return self.execute_command("QUIT", **kwargs)
1234 def replicaof(self, *args, **kwargs) -> ResponseT:
1235 """
1236 Update the replication settings of a redis replica, on the fly.
1238 Examples of valid arguments include:
1240 NO ONE (set no replication)
1241 host port (set to the host and port of a redis server)
1243 For more information, see https://redis.io/commands/replicaof
1244 """
1245 return self.execute_command("REPLICAOF", *args, **kwargs)
1247 def save(self, **kwargs) -> ResponseT:
1248 """
1249 Tell the Redis server to save its data to disk,
1250 blocking until the save is complete
1252 For more information, see https://redis.io/commands/save
1253 """
1254 return self.execute_command("SAVE", **kwargs)
1256 def shutdown(
1257 self,
1258 save: bool = False,
1259 nosave: bool = False,
1260 now: bool = False,
1261 force: bool = False,
1262 abort: bool = False,
1263 **kwargs,
1264 ) -> None:
1265 """Shutdown the Redis server. If Redis has persistence configured,
1266 data will be flushed before shutdown.
1267 It is possible to specify modifiers to alter the behavior of the command:
1268 ``save`` will force a DB saving operation even if no save points are configured.
1269 ``nosave`` will prevent a DB saving operation even if one or more save points
1270 are configured.
1271 ``now`` skips waiting for lagging replicas, i.e. it bypasses the first step in
1272 the shutdown sequence.
1273 ``force`` ignores any errors that would normally prevent the server from exiting
1274 ``abort`` cancels an ongoing shutdown and cannot be combined with other flags.
1276 For more information, see https://redis.io/commands/shutdown
1277 """
1278 if save and nosave:
1279 raise DataError("SHUTDOWN save and nosave cannot both be set")
1280 args = ["SHUTDOWN"]
1281 if save:
1282 args.append("SAVE")
1283 if nosave:
1284 args.append("NOSAVE")
1285 if now:
1286 args.append("NOW")
1287 if force:
1288 args.append("FORCE")
1289 if abort:
1290 args.append("ABORT")
1291 try:
1292 self.execute_command(*args, **kwargs)
1293 except ConnectionError:
1294 # a ConnectionError here is expected
1295 return
1296 raise RedisError("SHUTDOWN seems to have failed.")
1298 def slaveof(
1299 self, host: Optional[str] = None, port: Optional[int] = None, **kwargs
1300 ) -> ResponseT:
1301 """
1302 Set the server to be a replicated slave of the instance identified
1303 by the ``host`` and ``port``. If called without arguments, the
1304 instance is promoted to a master instead.
1306 For more information, see https://redis.io/commands/slaveof
1307 """
1308 if host is None and port is None:
1309 return self.execute_command("SLAVEOF", b"NO", b"ONE", **kwargs)
1310 return self.execute_command("SLAVEOF", host, port, **kwargs)
1312 def slowlog_get(self, num: Optional[int] = None, **kwargs) -> ResponseT:
1313 """
1314 Get the entries from the slowlog. If ``num`` is specified, get the
1315 most recent ``num`` items.
1317 For more information, see https://redis.io/commands/slowlog-get
1318 """
1319 from redis.client import NEVER_DECODE
1321 args = ["SLOWLOG GET"]
1322 if num is not None:
1323 args.append(num)
1324 decode_responses = self.get_connection_kwargs().get("decode_responses", False)
1325 if decode_responses is True:
1326 kwargs[NEVER_DECODE] = []
1327 return self.execute_command(*args, **kwargs)
1329 def slowlog_len(self, **kwargs) -> ResponseT:
1330 """
1331 Get the number of items in the slowlog
1333 For more information, see https://redis.io/commands/slowlog-len
1334 """
1335 return self.execute_command("SLOWLOG LEN", **kwargs)
1337 def slowlog_reset(self, **kwargs) -> ResponseT:
1338 """
1339 Remove all items in the slowlog
1341 For more information, see https://redis.io/commands/slowlog-reset
1342 """
1343 return self.execute_command("SLOWLOG RESET", **kwargs)
1345 def time(self, **kwargs) -> ResponseT:
1346 """
1347 Returns the server time as a 2-item tuple of ints:
1348 (seconds since epoch, microseconds into this second).
1350 For more information, see https://redis.io/commands/time
1351 """
1352 return self.execute_command("TIME", **kwargs)
1354 def wait(self, num_replicas: int, timeout: int, **kwargs) -> ResponseT:
1355 """
1356 Redis synchronous replication
1357 That returns the number of replicas that processed the query when
1358 we finally have at least ``num_replicas``, or when the ``timeout`` was
1359 reached.
1361 For more information, see https://redis.io/commands/wait
1362 """
1363 return self.execute_command("WAIT", num_replicas, timeout, **kwargs)
1365 def waitaof(
1366 self, num_local: int, num_replicas: int, timeout: int, **kwargs
1367 ) -> ResponseT:
1368 """
1369 This command blocks the current client until all previous write
1370 commands by that client are acknowledged as having been fsynced
1371 to the AOF of the local Redis and/or at least the specified number
1372 of replicas.
1374 For more information, see https://redis.io/commands/waitaof
1375 """
1376 return self.execute_command(
1377 "WAITAOF", num_local, num_replicas, timeout, **kwargs
1378 )
1380 def hello(self):
1381 """
1382 This function throws a NotImplementedError since it is intentionally
1383 not supported.
1384 """
1385 raise NotImplementedError(
1386 "HELLO is intentionally not implemented in the client."
1387 )
1389 def failover(self):
1390 """
1391 This function throws a NotImplementedError since it is intentionally
1392 not supported.
1393 """
1394 raise NotImplementedError(
1395 "FAILOVER is intentionally not implemented in the client."
1396 )
1399class AsyncManagementCommands(ManagementCommands):
1400 async def command_info(self, **kwargs) -> None:
1401 return super().command_info(**kwargs)
1403 async def debug_segfault(self, **kwargs) -> None:
1404 return super().debug_segfault(**kwargs)
1406 async def memory_doctor(self, **kwargs) -> None:
1407 return super().memory_doctor(**kwargs)
1409 async def memory_help(self, **kwargs) -> None:
1410 return super().memory_help(**kwargs)
1412 async def shutdown(
1413 self,
1414 save: bool = False,
1415 nosave: bool = False,
1416 now: bool = False,
1417 force: bool = False,
1418 abort: bool = False,
1419 **kwargs,
1420 ) -> None:
1421 """Shutdown the Redis server. If Redis has persistence configured,
1422 data will be flushed before shutdown. If the "save" option is set,
1423 a data flush will be attempted even if there is no persistence
1424 configured. If the "nosave" option is set, no data flush will be
1425 attempted. The "save" and "nosave" options cannot both be set.
1427 For more information, see https://redis.io/commands/shutdown
1428 """
1429 if save and nosave:
1430 raise DataError("SHUTDOWN save and nosave cannot both be set")
1431 args = ["SHUTDOWN"]
1432 if save:
1433 args.append("SAVE")
1434 if nosave:
1435 args.append("NOSAVE")
1436 if now:
1437 args.append("NOW")
1438 if force:
1439 args.append("FORCE")
1440 if abort:
1441 args.append("ABORT")
1442 try:
1443 await self.execute_command(*args, **kwargs)
1444 except ConnectionError:
1445 # a ConnectionError here is expected
1446 return
1447 raise RedisError("SHUTDOWN seems to have failed.")
1450class BitFieldOperation:
1451 """
1452 Command builder for BITFIELD commands.
1453 """
1455 def __init__(
1456 self,
1457 client: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
1458 key: str,
1459 default_overflow: Optional[str] = None,
1460 ):
1461 self.client = client
1462 self.key = key
1463 self._default_overflow = default_overflow
1464 # for typing purposes, run the following in constructor and in reset()
1465 self.operations: list[tuple[EncodableT, ...]] = []
1466 self._last_overflow = "WRAP"
1467 self.reset()
1469 def reset(self):
1470 """
1471 Reset the state of the instance to when it was constructed
1472 """
1473 self.operations = []
1474 self._last_overflow = "WRAP"
1475 self.overflow(self._default_overflow or self._last_overflow)
1477 def overflow(self, overflow: str):
1478 """
1479 Update the overflow algorithm of successive INCRBY operations
1480 :param overflow: Overflow algorithm, one of WRAP, SAT, FAIL. See the
1481 Redis docs for descriptions of these algorithmsself.
1482 :returns: a :py:class:`BitFieldOperation` instance.
1483 """
1484 overflow = overflow.upper()
1485 if overflow != self._last_overflow:
1486 self._last_overflow = overflow
1487 self.operations.append(("OVERFLOW", overflow))
1488 return self
1490 def incrby(
1491 self,
1492 fmt: str,
1493 offset: BitfieldOffsetT,
1494 increment: int,
1495 overflow: Optional[str] = None,
1496 ):
1497 """
1498 Increment a bitfield by a given amount.
1499 :param fmt: format-string for the bitfield being updated, e.g. 'u8'
1500 for an unsigned 8-bit integer.
1501 :param offset: offset (in number of bits). If prefixed with a
1502 '#', this is an offset multiplier, e.g. given the arguments
1503 fmt='u8', offset='#2', the offset will be 16.
1504 :param int increment: value to increment the bitfield by.
1505 :param str overflow: overflow algorithm. Defaults to WRAP, but other
1506 acceptable values are SAT and FAIL. See the Redis docs for
1507 descriptions of these algorithms.
1508 :returns: a :py:class:`BitFieldOperation` instance.
1509 """
1510 if overflow is not None:
1511 self.overflow(overflow)
1513 self.operations.append(("INCRBY", fmt, offset, increment))
1514 return self
1516 def get(self, fmt: str, offset: BitfieldOffsetT):
1517 """
1518 Get the value of a given bitfield.
1519 :param fmt: format-string for the bitfield being read, e.g. 'u8' for
1520 an unsigned 8-bit integer.
1521 :param offset: offset (in number of bits). If prefixed with a
1522 '#', this is an offset multiplier, e.g. given the arguments
1523 fmt='u8', offset='#2', the offset will be 16.
1524 :returns: a :py:class:`BitFieldOperation` instance.
1525 """
1526 self.operations.append(("GET", fmt, offset))
1527 return self
1529 def set(self, fmt: str, offset: BitfieldOffsetT, value: int):
1530 """
1531 Set the value of a given bitfield.
1532 :param fmt: format-string for the bitfield being read, e.g. 'u8' for
1533 an unsigned 8-bit integer.
1534 :param offset: offset (in number of bits). If prefixed with a
1535 '#', this is an offset multiplier, e.g. given the arguments
1536 fmt='u8', offset='#2', the offset will be 16.
1537 :param int value: value to set at the given position.
1538 :returns: a :py:class:`BitFieldOperation` instance.
1539 """
1540 self.operations.append(("SET", fmt, offset, value))
1541 return self
1543 @property
1544 def command(self):
1545 cmd = ["BITFIELD", self.key]
1546 for ops in self.operations:
1547 cmd.extend(ops)
1548 return cmd
1550 def execute(self) -> ResponseT:
1551 """
1552 Execute the operation(s) in a single BITFIELD command. The return value
1553 is a list of values corresponding to each operation. If the client
1554 used to create this instance was a pipeline, the list of values
1555 will be present within the pipeline's execute.
1556 """
1557 command = self.command
1558 self.reset()
1559 return self.client.execute_command(*command)
1562class DataPersistOptions(Enum):
1563 # set the value for each provided key to each
1564 # provided value only if all do not already exist.
1565 NX = "NX"
1567 # set the value for each provided key to each
1568 # provided value only if all already exist.
1569 XX = "XX"
1572class BasicKeyCommands(CommandsProtocol):
1573 """
1574 Redis basic key-based commands
1575 """
1577 def append(self, key: KeyT, value: EncodableT) -> ResponseT:
1578 """
1579 Appends the string ``value`` to the value at ``key``. If ``key``
1580 doesn't already exist, create it with a value of ``value``.
1581 Returns the new length of the value at ``key``.
1583 For more information, see https://redis.io/commands/append
1584 """
1585 return self.execute_command("APPEND", key, value)
1587 def bitcount(
1588 self,
1589 key: KeyT,
1590 start: Optional[int] = None,
1591 end: Optional[int] = None,
1592 mode: Optional[str] = None,
1593 ) -> ResponseT:
1594 """
1595 Returns the count of set bits in the value of ``key``. Optional
1596 ``start`` and ``end`` parameters indicate which bytes to consider
1598 For more information, see https://redis.io/commands/bitcount
1599 """
1600 params = [key]
1601 if start is not None and end is not None:
1602 params.append(start)
1603 params.append(end)
1604 elif (start is not None and end is None) or (end is not None and start is None):
1605 raise DataError("Both start and end must be specified")
1606 if mode is not None:
1607 params.append(mode)
1608 return self.execute_command("BITCOUNT", *params, keys=[key])
1610 def bitfield(
1611 self: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
1612 key: KeyT,
1613 default_overflow: Optional[str] = None,
1614 ) -> BitFieldOperation:
1615 """
1616 Return a BitFieldOperation instance to conveniently construct one or
1617 more bitfield operations on ``key``.
1619 For more information, see https://redis.io/commands/bitfield
1620 """
1621 return BitFieldOperation(self, key, default_overflow=default_overflow)
1623 def bitfield_ro(
1624 self: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
1625 key: KeyT,
1626 encoding: str,
1627 offset: BitfieldOffsetT,
1628 items: Optional[list] = None,
1629 ) -> ResponseT:
1630 """
1631 Return an array of the specified bitfield values
1632 where the first value is found using ``encoding`` and ``offset``
1633 parameters and remaining values are result of corresponding
1634 encoding/offset pairs in optional list ``items``
1635 Read-only variant of the BITFIELD command.
1637 For more information, see https://redis.io/commands/bitfield_ro
1638 """
1639 params = [key, "GET", encoding, offset]
1641 items = items or []
1642 for encoding, offset in items:
1643 params.extend(["GET", encoding, offset])
1644 return self.execute_command("BITFIELD_RO", *params, keys=[key])
1646 def bitop(self, operation: str, dest: KeyT, *keys: KeyT) -> ResponseT:
1647 """
1648 Perform a bitwise operation using ``operation`` between ``keys`` and
1649 store the result in ``dest``.
1651 For more information, see https://redis.io/commands/bitop
1652 """
1653 return self.execute_command("BITOP", operation, dest, *keys)
1655 def bitpos(
1656 self,
1657 key: KeyT,
1658 bit: int,
1659 start: Optional[int] = None,
1660 end: Optional[int] = None,
1661 mode: Optional[str] = None,
1662 ) -> ResponseT:
1663 """
1664 Return the position of the first bit set to 1 or 0 in a string.
1665 ``start`` and ``end`` defines search range. The range is interpreted
1666 as a range of bytes and not a range of bits, so start=0 and end=2
1667 means to look at the first three bytes.
1669 For more information, see https://redis.io/commands/bitpos
1670 """
1671 if bit not in (0, 1):
1672 raise DataError("bit must be 0 or 1")
1673 params = [key, bit]
1675 start is not None and params.append(start)
1677 if start is not None and end is not None:
1678 params.append(end)
1679 elif start is None and end is not None:
1680 raise DataError("start argument is not set, when end is specified")
1682 if mode is not None:
1683 params.append(mode)
1684 return self.execute_command("BITPOS", *params, keys=[key])
1686 def copy(
1687 self,
1688 source: str,
1689 destination: str,
1690 destination_db: Optional[str] = None,
1691 replace: bool = False,
1692 ) -> ResponseT:
1693 """
1694 Copy the value stored in the ``source`` key to the ``destination`` key.
1696 ``destination_db`` an alternative destination database. By default,
1697 the ``destination`` key is created in the source Redis database.
1699 ``replace`` whether the ``destination`` key should be removed before
1700 copying the value to it. By default, the value is not copied if
1701 the ``destination`` key already exists.
1703 For more information, see https://redis.io/commands/copy
1704 """
1705 params = [source, destination]
1706 if destination_db is not None:
1707 params.extend(["DB", destination_db])
1708 if replace:
1709 params.append("REPLACE")
1710 return self.execute_command("COPY", *params)
1712 def decrby(self, name: KeyT, amount: int = 1) -> ResponseT:
1713 """
1714 Decrements the value of ``key`` by ``amount``. If no key exists,
1715 the value will be initialized as 0 - ``amount``
1717 For more information, see https://redis.io/commands/decrby
1718 """
1719 return self.execute_command("DECRBY", name, amount)
1721 decr = decrby
1723 def delete(self, *names: KeyT) -> ResponseT:
1724 """
1725 Delete one or more keys specified by ``names``
1726 """
1727 return self.execute_command("DEL", *names)
1729 def __delitem__(self, name: KeyT):
1730 self.delete(name)
1732 def dump(self, name: KeyT) -> ResponseT:
1733 """
1734 Return a serialized version of the value stored at the specified key.
1735 If key does not exist a nil bulk reply is returned.
1737 For more information, see https://redis.io/commands/dump
1738 """
1739 from redis.client import NEVER_DECODE
1741 options = {}
1742 options[NEVER_DECODE] = []
1743 return self.execute_command("DUMP", name, **options)
1745 def exists(self, *names: KeyT) -> ResponseT:
1746 """
1747 Returns the number of ``names`` that exist
1749 For more information, see https://redis.io/commands/exists
1750 """
1751 return self.execute_command("EXISTS", *names, keys=names)
1753 __contains__ = exists
1755 def expire(
1756 self,
1757 name: KeyT,
1758 time: ExpiryT,
1759 nx: bool = False,
1760 xx: bool = False,
1761 gt: bool = False,
1762 lt: bool = False,
1763 ) -> ResponseT:
1764 """
1765 Set an expire flag on key ``name`` for ``time`` seconds with given
1766 ``option``. ``time`` can be represented by an integer or a Python timedelta
1767 object.
1769 Valid options are:
1770 NX -> Set expiry only when the key has no expiry
1771 XX -> Set expiry only when the key has an existing expiry
1772 GT -> Set expiry only when the new expiry is greater than current one
1773 LT -> Set expiry only when the new expiry is less than current one
1775 For more information, see https://redis.io/commands/expire
1776 """
1777 if isinstance(time, datetime.timedelta):
1778 time = int(time.total_seconds())
1780 exp_option = list()
1781 if nx:
1782 exp_option.append("NX")
1783 if xx:
1784 exp_option.append("XX")
1785 if gt:
1786 exp_option.append("GT")
1787 if lt:
1788 exp_option.append("LT")
1790 return self.execute_command("EXPIRE", name, time, *exp_option)
1792 def expireat(
1793 self,
1794 name: KeyT,
1795 when: AbsExpiryT,
1796 nx: bool = False,
1797 xx: bool = False,
1798 gt: bool = False,
1799 lt: bool = False,
1800 ) -> ResponseT:
1801 """
1802 Set an expire flag on key ``name`` with given ``option``. ``when``
1803 can be represented as an integer indicating unix time or a Python
1804 datetime object.
1806 Valid options are:
1807 -> NX -- Set expiry only when the key has no expiry
1808 -> XX -- Set expiry only when the key has an existing expiry
1809 -> GT -- Set expiry only when the new expiry is greater than current one
1810 -> LT -- Set expiry only when the new expiry is less than current one
1812 For more information, see https://redis.io/commands/expireat
1813 """
1814 if isinstance(when, datetime.datetime):
1815 when = int(when.timestamp())
1817 exp_option = list()
1818 if nx:
1819 exp_option.append("NX")
1820 if xx:
1821 exp_option.append("XX")
1822 if gt:
1823 exp_option.append("GT")
1824 if lt:
1825 exp_option.append("LT")
1827 return self.execute_command("EXPIREAT", name, when, *exp_option)
1829 def expiretime(self, key: str) -> int:
1830 """
1831 Returns the absolute Unix timestamp (since January 1, 1970) in seconds
1832 at which the given key will expire.
1834 For more information, see https://redis.io/commands/expiretime
1835 """
1836 return self.execute_command("EXPIRETIME", key)
1838 def get(self, name: KeyT) -> ResponseT:
1839 """
1840 Return the value at key ``name``, or None if the key doesn't exist
1842 For more information, see https://redis.io/commands/get
1843 """
1844 return self.execute_command("GET", name, keys=[name])
1846 def getdel(self, name: KeyT) -> ResponseT:
1847 """
1848 Get the value at key ``name`` and delete the key. This command
1849 is similar to GET, except for the fact that it also deletes
1850 the key on success (if and only if the key's value type
1851 is a string).
1853 For more information, see https://redis.io/commands/getdel
1854 """
1855 return self.execute_command("GETDEL", name)
1857 def getex(
1858 self,
1859 name: KeyT,
1860 ex: Optional[ExpiryT] = None,
1861 px: Optional[ExpiryT] = None,
1862 exat: Optional[AbsExpiryT] = None,
1863 pxat: Optional[AbsExpiryT] = None,
1864 persist: bool = False,
1865 ) -> ResponseT:
1866 """
1867 Get the value of key and optionally set its expiration.
1868 GETEX is similar to GET, but is a write command with
1869 additional options. All time parameters can be given as
1870 datetime.timedelta or integers.
1872 ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
1874 ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
1876 ``exat`` sets an expire flag on key ``name`` for ``ex`` seconds,
1877 specified in unix time.
1879 ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,
1880 specified in unix time.
1882 ``persist`` remove the time to live associated with ``name``.
1884 For more information, see https://redis.io/commands/getex
1885 """
1886 opset = {ex, px, exat, pxat}
1887 if len(opset) > 2 or len(opset) > 1 and persist:
1888 raise DataError(
1889 "``ex``, ``px``, ``exat``, ``pxat``, "
1890 "and ``persist`` are mutually exclusive."
1891 )
1893 exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
1895 if persist:
1896 exp_options.append("PERSIST")
1898 return self.execute_command("GETEX", name, *exp_options)
1900 def __getitem__(self, name: KeyT):
1901 """
1902 Return the value at key ``name``, raises a KeyError if the key
1903 doesn't exist.
1904 """
1905 value = self.get(name)
1906 if value is not None:
1907 return value
1908 raise KeyError(name)
1910 def getbit(self, name: KeyT, offset: int) -> ResponseT:
1911 """
1912 Returns an integer indicating the value of ``offset`` in ``name``
1914 For more information, see https://redis.io/commands/getbit
1915 """
1916 return self.execute_command("GETBIT", name, offset, keys=[name])
1918 def getrange(self, key: KeyT, start: int, end: int) -> ResponseT:
1919 """
1920 Returns the substring of the string value stored at ``key``,
1921 determined by the offsets ``start`` and ``end`` (both are inclusive)
1923 For more information, see https://redis.io/commands/getrange
1924 """
1925 return self.execute_command("GETRANGE", key, start, end, keys=[key])
1927 def getset(self, name: KeyT, value: EncodableT) -> ResponseT:
1928 """
1929 Sets the value at key ``name`` to ``value``
1930 and returns the old value at key ``name`` atomically.
1932 As per Redis 6.2, GETSET is considered deprecated.
1933 Please use SET with GET parameter in new code.
1935 For more information, see https://redis.io/commands/getset
1936 """
1937 return self.execute_command("GETSET", name, value)
1939 def incrby(self, name: KeyT, amount: int = 1) -> ResponseT:
1940 """
1941 Increments the value of ``key`` by ``amount``. If no key exists,
1942 the value will be initialized as ``amount``
1944 For more information, see https://redis.io/commands/incrby
1945 """
1946 return self.execute_command("INCRBY", name, amount)
1948 incr = incrby
1950 def incrbyfloat(self, name: KeyT, amount: float = 1.0) -> ResponseT:
1951 """
1952 Increments the value at key ``name`` by floating ``amount``.
1953 If no key exists, the value will be initialized as ``amount``
1955 For more information, see https://redis.io/commands/incrbyfloat
1956 """
1957 return self.execute_command("INCRBYFLOAT", name, amount)
1959 def keys(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
1960 """
1961 Returns a list of keys matching ``pattern``
1963 For more information, see https://redis.io/commands/keys
1964 """
1965 return self.execute_command("KEYS", pattern, **kwargs)
1967 def lmove(
1968 self, first_list: str, second_list: str, src: str = "LEFT", dest: str = "RIGHT"
1969 ) -> ResponseT:
1970 """
1971 Atomically returns and removes the first/last element of a list,
1972 pushing it as the first/last element on the destination list.
1973 Returns the element being popped and pushed.
1975 For more information, see https://redis.io/commands/lmove
1976 """
1977 params = [first_list, second_list, src, dest]
1978 return self.execute_command("LMOVE", *params)
1980 def blmove(
1981 self,
1982 first_list: str,
1983 second_list: str,
1984 timeout: int,
1985 src: str = "LEFT",
1986 dest: str = "RIGHT",
1987 ) -> ResponseT:
1988 """
1989 Blocking version of lmove.
1991 For more information, see https://redis.io/commands/blmove
1992 """
1993 params = [first_list, second_list, src, dest, timeout]
1994 return self.execute_command("BLMOVE", *params)
1996 def mget(self, keys: KeysT, *args: EncodableT) -> ResponseT:
1997 """
1998 Returns a list of values ordered identically to ``keys``
2000 ** Important ** When this method is used with Cluster clients, all keys
2001 must be in the same hash slot, otherwise a RedisClusterException
2002 will be raised.
2004 For more information, see https://redis.io/commands/mget
2005 """
2006 from redis.client import EMPTY_RESPONSE
2008 args = list_or_args(keys, args)
2009 options = {}
2010 if not args:
2011 options[EMPTY_RESPONSE] = []
2012 options["keys"] = args
2013 return self.execute_command("MGET", *args, **options)
2015 def mset(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
2016 """
2017 Sets key/values based on a mapping. Mapping is a dictionary of
2018 key/value pairs. Both keys and values should be strings or types that
2019 can be cast to a string via str().
2021 ** Important ** When this method is used with Cluster clients, all keys
2022 must be in the same hash slot, otherwise a RedisClusterException
2023 will be raised.
2025 For more information, see https://redis.io/commands/mset
2026 """
2027 items = []
2028 for pair in mapping.items():
2029 items.extend(pair)
2030 return self.execute_command("MSET", *items)
2032 def msetex(
2033 self,
2034 mapping: Mapping[AnyKeyT, EncodableT],
2035 data_persist_option: Optional[DataPersistOptions] = None,
2036 ex: Optional[ExpiryT] = None,
2037 px: Optional[ExpiryT] = None,
2038 exat: Optional[AbsExpiryT] = None,
2039 pxat: Optional[AbsExpiryT] = None,
2040 keepttl: bool = False,
2041 ) -> Union[Awaitable[int], int]:
2042 """
2043 Sets key/values based on the provided ``mapping`` items.
2045 ** Important ** When this method is used with Cluster clients, all keys
2046 must be in the same hash slot, otherwise a RedisClusterException
2047 will be raised.
2049 ``mapping`` accepts a dict of key/value pairs that will be added to the database.
2051 ``data_persist_option`` can be set to ``NX`` or ``XX`` to control the
2052 behavior of the command.
2053 ``NX`` will set the value for each provided key to each
2054 provided value only if all do not already exist.
2055 ``XX`` will set the value for each provided key to each
2056 provided value only if all already exist.
2058 ``ex`` sets an expire flag on the keys in ``mapping`` for ``ex`` seconds.
2060 ``px`` sets an expire flag on the keys in ``mapping`` for ``px`` milliseconds.
2062 ``exat`` sets an expire flag on the keys in ``mapping`` for ``exat`` seconds,
2063 specified in unix time.
2065 ``pxat`` sets an expire flag on the keys in ``mapping`` for ``pxat`` milliseconds,
2066 specified in unix time.
2068 ``keepttl`` if True, retain the time to live associated with the keys.
2070 Returns the number of fields that were added.
2072 Available since Redis 8.4
2073 For more information, see https://redis.io/commands/msetex
2074 """
2075 opset = {ex, px, exat, pxat}
2076 if len(opset) > 2 or len(opset) > 1 and keepttl:
2077 raise DataError(
2078 "``ex``, ``px``, ``exat``, ``pxat``, "
2079 "and ``keepttl`` are mutually exclusive."
2080 )
2082 exp_options: list[EncodableT] = []
2083 if data_persist_option:
2084 exp_options.append(data_persist_option.value)
2086 exp_options.extend(extract_expire_flags(ex, px, exat, pxat))
2088 if keepttl:
2089 exp_options.append("KEEPTTL")
2091 pieces = ["MSETEX", len(mapping)]
2093 for pair in mapping.items():
2094 pieces.extend(pair)
2096 return self.execute_command(*pieces, *exp_options)
2098 def msetnx(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
2099 """
2100 Sets key/values based on a mapping if none of the keys are already set.
2101 Mapping is a dictionary of key/value pairs. Both keys and values
2102 should be strings or types that can be cast to a string via str().
2103 Returns a boolean indicating if the operation was successful.
2105 ** Important ** When this method is used with Cluster clients, all keys
2106 must be in the same hash slot, otherwise a RedisClusterException
2107 will be raised.
2109 For more information, see https://redis.io/commands/msetnx
2110 """
2111 items = []
2112 for pair in mapping.items():
2113 items.extend(pair)
2114 return self.execute_command("MSETNX", *items)
2116 def move(self, name: KeyT, db: int) -> ResponseT:
2117 """
2118 Moves the key ``name`` to a different Redis database ``db``
2120 For more information, see https://redis.io/commands/move
2121 """
2122 return self.execute_command("MOVE", name, db)
2124 def persist(self, name: KeyT) -> ResponseT:
2125 """
2126 Removes an expiration on ``name``
2128 For more information, see https://redis.io/commands/persist
2129 """
2130 return self.execute_command("PERSIST", name)
2132 def pexpire(
2133 self,
2134 name: KeyT,
2135 time: ExpiryT,
2136 nx: bool = False,
2137 xx: bool = False,
2138 gt: bool = False,
2139 lt: bool = False,
2140 ) -> ResponseT:
2141 """
2142 Set an expire flag on key ``name`` for ``time`` milliseconds
2143 with given ``option``. ``time`` can be represented by an
2144 integer or a Python timedelta object.
2146 Valid options are:
2147 NX -> Set expiry only when the key has no expiry
2148 XX -> Set expiry only when the key has an existing expiry
2149 GT -> Set expiry only when the new expiry is greater than current one
2150 LT -> Set expiry only when the new expiry is less than current one
2152 For more information, see https://redis.io/commands/pexpire
2153 """
2154 if isinstance(time, datetime.timedelta):
2155 time = int(time.total_seconds() * 1000)
2157 exp_option = list()
2158 if nx:
2159 exp_option.append("NX")
2160 if xx:
2161 exp_option.append("XX")
2162 if gt:
2163 exp_option.append("GT")
2164 if lt:
2165 exp_option.append("LT")
2166 return self.execute_command("PEXPIRE", name, time, *exp_option)
2168 def pexpireat(
2169 self,
2170 name: KeyT,
2171 when: AbsExpiryT,
2172 nx: bool = False,
2173 xx: bool = False,
2174 gt: bool = False,
2175 lt: bool = False,
2176 ) -> ResponseT:
2177 """
2178 Set an expire flag on key ``name`` with given ``option``. ``when``
2179 can be represented as an integer representing unix time in
2180 milliseconds (unix time * 1000) or a Python datetime object.
2182 Valid options are:
2183 NX -> Set expiry only when the key has no expiry
2184 XX -> Set expiry only when the key has an existing expiry
2185 GT -> Set expiry only when the new expiry is greater than current one
2186 LT -> Set expiry only when the new expiry is less than current one
2188 For more information, see https://redis.io/commands/pexpireat
2189 """
2190 if isinstance(when, datetime.datetime):
2191 when = int(when.timestamp() * 1000)
2192 exp_option = list()
2193 if nx:
2194 exp_option.append("NX")
2195 if xx:
2196 exp_option.append("XX")
2197 if gt:
2198 exp_option.append("GT")
2199 if lt:
2200 exp_option.append("LT")
2201 return self.execute_command("PEXPIREAT", name, when, *exp_option)
2203 def pexpiretime(self, key: str) -> int:
2204 """
2205 Returns the absolute Unix timestamp (since January 1, 1970) in milliseconds
2206 at which the given key will expire.
2208 For more information, see https://redis.io/commands/pexpiretime
2209 """
2210 return self.execute_command("PEXPIRETIME", key)
2212 def psetex(self, name: KeyT, time_ms: ExpiryT, value: EncodableT):
2213 """
2214 Set the value of key ``name`` to ``value`` that expires in ``time_ms``
2215 milliseconds. ``time_ms`` can be represented by an integer or a Python
2216 timedelta object
2218 For more information, see https://redis.io/commands/psetex
2219 """
2220 if isinstance(time_ms, datetime.timedelta):
2221 time_ms = int(time_ms.total_seconds() * 1000)
2222 return self.execute_command("PSETEX", name, time_ms, value)
2224 def pttl(self, name: KeyT) -> ResponseT:
2225 """
2226 Returns the number of milliseconds until the key ``name`` will expire
2228 For more information, see https://redis.io/commands/pttl
2229 """
2230 return self.execute_command("PTTL", name)
2232 def hrandfield(
2233 self, key: str, count: Optional[int] = None, withvalues: bool = False
2234 ) -> ResponseT:
2235 """
2236 Return a random field from the hash value stored at key.
2238 count: if the argument is positive, return an array of distinct fields.
2239 If called with a negative count, the behavior changes and the command
2240 is allowed to return the same field multiple times. In this case,
2241 the number of returned fields is the absolute value of the
2242 specified count.
2243 withvalues: The optional WITHVALUES modifier changes the reply so it
2244 includes the respective values of the randomly selected hash fields.
2246 For more information, see https://redis.io/commands/hrandfield
2247 """
2248 params = []
2249 if count is not None:
2250 params.append(count)
2251 if withvalues:
2252 params.append("WITHVALUES")
2254 return self.execute_command("HRANDFIELD", key, *params)
2256 def randomkey(self, **kwargs) -> ResponseT:
2257 """
2258 Returns the name of a random key
2260 For more information, see https://redis.io/commands/randomkey
2261 """
2262 return self.execute_command("RANDOMKEY", **kwargs)
2264 def rename(self, src: KeyT, dst: KeyT) -> ResponseT:
2265 """
2266 Rename key ``src`` to ``dst``
2268 For more information, see https://redis.io/commands/rename
2269 """
2270 return self.execute_command("RENAME", src, dst)
2272 def renamenx(self, src: KeyT, dst: KeyT):
2273 """
2274 Rename key ``src`` to ``dst`` if ``dst`` doesn't already exist
2276 For more information, see https://redis.io/commands/renamenx
2277 """
2278 return self.execute_command("RENAMENX", src, dst)
2280 def restore(
2281 self,
2282 name: KeyT,
2283 ttl: float,
2284 value: EncodableT,
2285 replace: bool = False,
2286 absttl: bool = False,
2287 idletime: Optional[int] = None,
2288 frequency: Optional[int] = None,
2289 ) -> ResponseT:
2290 """
2291 Create a key using the provided serialized value, previously obtained
2292 using DUMP.
2294 ``replace`` allows an existing key on ``name`` to be overridden. If
2295 it's not specified an error is raised on collision.
2297 ``absttl`` if True, specified ``ttl`` should represent an absolute Unix
2298 timestamp in milliseconds in which the key will expire. (Redis 5.0 or
2299 greater).
2301 ``idletime`` Used for eviction, this is the number of seconds the
2302 key must be idle, prior to execution.
2304 ``frequency`` Used for eviction, this is the frequency counter of
2305 the object stored at the key, prior to execution.
2307 For more information, see https://redis.io/commands/restore
2308 """
2309 params = [name, ttl, value]
2310 if replace:
2311 params.append("REPLACE")
2312 if absttl:
2313 params.append("ABSTTL")
2314 if idletime is not None:
2315 params.append("IDLETIME")
2316 try:
2317 params.append(int(idletime))
2318 except ValueError:
2319 raise DataError("idletimemust be an integer")
2321 if frequency is not None:
2322 params.append("FREQ")
2323 try:
2324 params.append(int(frequency))
2325 except ValueError:
2326 raise DataError("frequency must be an integer")
2328 return self.execute_command("RESTORE", *params)
2330 def set(
2331 self,
2332 name: KeyT,
2333 value: EncodableT,
2334 ex: Optional[ExpiryT] = None,
2335 px: Optional[ExpiryT] = None,
2336 nx: bool = False,
2337 xx: bool = False,
2338 keepttl: bool = False,
2339 get: bool = False,
2340 exat: Optional[AbsExpiryT] = None,
2341 pxat: Optional[AbsExpiryT] = None,
2342 ) -> ResponseT:
2343 """
2344 Set the value at key ``name`` to ``value``
2346 ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
2348 ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
2350 ``nx`` if set to True, set the value at key ``name`` to ``value`` only
2351 if it does not exist.
2353 ``xx`` if set to True, set the value at key ``name`` to ``value`` only
2354 if it already exists.
2356 ``keepttl`` if True, retain the time to live associated with the key.
2357 (Available since Redis 6.0)
2359 ``get`` if True, set the value at key ``name`` to ``value`` and return
2360 the old value stored at key, or None if the key did not exist.
2361 (Available since Redis 6.2)
2363 ``exat`` sets an expire flag on key ``name`` for ``ex`` seconds,
2364 specified in unix time.
2366 ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,
2367 specified in unix time.
2369 For more information, see https://redis.io/commands/set
2370 """
2371 opset = {ex, px, exat, pxat}
2372 if len(opset) > 2 or len(opset) > 1 and keepttl:
2373 raise DataError(
2374 "``ex``, ``px``, ``exat``, ``pxat``, "
2375 "and ``keepttl`` are mutually exclusive."
2376 )
2378 if nx and xx:
2379 raise DataError("``nx`` and ``xx`` are mutually exclusive.")
2381 pieces: list[EncodableT] = [name, value]
2382 options = {}
2384 pieces.extend(extract_expire_flags(ex, px, exat, pxat))
2386 if keepttl:
2387 pieces.append("KEEPTTL")
2389 if nx:
2390 pieces.append("NX")
2391 if xx:
2392 pieces.append("XX")
2394 if get:
2395 pieces.append("GET")
2396 options["get"] = True
2398 return self.execute_command("SET", *pieces, **options)
2400 def __setitem__(self, name: KeyT, value: EncodableT):
2401 self.set(name, value)
2403 def setbit(self, name: KeyT, offset: int, value: int) -> ResponseT:
2404 """
2405 Flag the ``offset`` in ``name`` as ``value``. Returns an integer
2406 indicating the previous value of ``offset``.
2408 For more information, see https://redis.io/commands/setbit
2409 """
2410 value = value and 1 or 0
2411 return self.execute_command("SETBIT", name, offset, value)
2413 def setex(self, name: KeyT, time: ExpiryT, value: EncodableT) -> ResponseT:
2414 """
2415 Set the value of key ``name`` to ``value`` that expires in ``time``
2416 seconds. ``time`` can be represented by an integer or a Python
2417 timedelta object.
2419 For more information, see https://redis.io/commands/setex
2420 """
2421 if isinstance(time, datetime.timedelta):
2422 time = int(time.total_seconds())
2423 return self.execute_command("SETEX", name, time, value)
2425 def setnx(self, name: KeyT, value: EncodableT) -> ResponseT:
2426 """
2427 Set the value of key ``name`` to ``value`` if key doesn't exist
2429 For more information, see https://redis.io/commands/setnx
2430 """
2431 return self.execute_command("SETNX", name, value)
2433 def setrange(self, name: KeyT, offset: int, value: EncodableT) -> ResponseT:
2434 """
2435 Overwrite bytes in the value of ``name`` starting at ``offset`` with
2436 ``value``. If ``offset`` plus the length of ``value`` exceeds the
2437 length of the original value, the new value will be larger than before.
2438 If ``offset`` exceeds the length of the original value, null bytes
2439 will be used to pad between the end of the previous value and the start
2440 of what's being injected.
2442 Returns the length of the new string.
2444 For more information, see https://redis.io/commands/setrange
2445 """
2446 return self.execute_command("SETRANGE", name, offset, value)
2448 def stralgo(
2449 self,
2450 algo: Literal["LCS"],
2451 value1: KeyT,
2452 value2: KeyT,
2453 specific_argument: Union[Literal["strings"], Literal["keys"]] = "strings",
2454 len: bool = False,
2455 idx: bool = False,
2456 minmatchlen: Optional[int] = None,
2457 withmatchlen: bool = False,
2458 **kwargs,
2459 ) -> ResponseT:
2460 """
2461 Implements complex algorithms that operate on strings.
2462 Right now the only algorithm implemented is the LCS algorithm
2463 (longest common substring). However new algorithms could be
2464 implemented in the future.
2466 ``algo`` Right now must be LCS
2467 ``value1`` and ``value2`` Can be two strings or two keys
2468 ``specific_argument`` Specifying if the arguments to the algorithm
2469 will be keys or strings. strings is the default.
2470 ``len`` Returns just the len of the match.
2471 ``idx`` Returns the match positions in each string.
2472 ``minmatchlen`` Restrict the list of matches to the ones of a given
2473 minimal length. Can be provided only when ``idx`` set to True.
2474 ``withmatchlen`` Returns the matches with the len of the match.
2475 Can be provided only when ``idx`` set to True.
2477 For more information, see https://redis.io/commands/stralgo
2478 """
2479 # check validity
2480 supported_algo = ["LCS"]
2481 if algo not in supported_algo:
2482 supported_algos_str = ", ".join(supported_algo)
2483 raise DataError(f"The supported algorithms are: {supported_algos_str}")
2484 if specific_argument not in ["keys", "strings"]:
2485 raise DataError("specific_argument can be only keys or strings")
2486 if len and idx:
2487 raise DataError("len and idx cannot be provided together.")
2489 pieces: list[EncodableT] = [algo, specific_argument.upper(), value1, value2]
2490 if len:
2491 pieces.append(b"LEN")
2492 if idx:
2493 pieces.append(b"IDX")
2494 try:
2495 int(minmatchlen)
2496 pieces.extend([b"MINMATCHLEN", minmatchlen])
2497 except TypeError:
2498 pass
2499 if withmatchlen:
2500 pieces.append(b"WITHMATCHLEN")
2502 return self.execute_command(
2503 "STRALGO",
2504 *pieces,
2505 len=len,
2506 idx=idx,
2507 minmatchlen=minmatchlen,
2508 withmatchlen=withmatchlen,
2509 **kwargs,
2510 )
2512 def strlen(self, name: KeyT) -> ResponseT:
2513 """
2514 Return the number of bytes stored in the value of ``name``
2516 For more information, see https://redis.io/commands/strlen
2517 """
2518 return self.execute_command("STRLEN", name, keys=[name])
2520 def substr(self, name: KeyT, start: int, end: int = -1) -> ResponseT:
2521 """
2522 Return a substring of the string at key ``name``. ``start`` and ``end``
2523 are 0-based integers specifying the portion of the string to return.
2524 """
2525 return self.execute_command("SUBSTR", name, start, end, keys=[name])
2527 def touch(self, *args: KeyT) -> ResponseT:
2528 """
2529 Alters the last access time of a key(s) ``*args``. A key is ignored
2530 if it does not exist.
2532 For more information, see https://redis.io/commands/touch
2533 """
2534 return self.execute_command("TOUCH", *args)
2536 def ttl(self, name: KeyT) -> ResponseT:
2537 """
2538 Returns the number of seconds until the key ``name`` will expire
2540 For more information, see https://redis.io/commands/ttl
2541 """
2542 return self.execute_command("TTL", name)
2544 def type(self, name: KeyT) -> ResponseT:
2545 """
2546 Returns the type of key ``name``
2548 For more information, see https://redis.io/commands/type
2549 """
2550 return self.execute_command("TYPE", name, keys=[name])
2552 def watch(self, *names: KeyT) -> None:
2553 """
2554 Watches the values at keys ``names``, or None if the key doesn't exist
2556 For more information, see https://redis.io/commands/watch
2557 """
2558 warnings.warn(DeprecationWarning("Call WATCH from a Pipeline object"))
2560 def unwatch(self) -> None:
2561 """
2562 Unwatches all previously watched keys for a transaction
2564 For more information, see https://redis.io/commands/unwatch
2565 """
2566 warnings.warn(DeprecationWarning("Call UNWATCH from a Pipeline object"))
2568 def unlink(self, *names: KeyT) -> ResponseT:
2569 """
2570 Unlink one or more keys specified by ``names``
2572 For more information, see https://redis.io/commands/unlink
2573 """
2574 return self.execute_command("UNLINK", *names)
2576 def lcs(
2577 self,
2578 key1: str,
2579 key2: str,
2580 len: Optional[bool] = False,
2581 idx: Optional[bool] = False,
2582 minmatchlen: Optional[int] = 0,
2583 withmatchlen: Optional[bool] = False,
2584 ) -> Union[str, int, list]:
2585 """
2586 Find the longest common subsequence between ``key1`` and ``key2``.
2587 If ``len`` is true the length of the match will will be returned.
2588 If ``idx`` is true the match position in each strings will be returned.
2589 ``minmatchlen`` restrict the list of matches to the ones of
2590 the given ``minmatchlen``.
2591 If ``withmatchlen`` the length of the match also will be returned.
2592 For more information, see https://redis.io/commands/lcs
2593 """
2594 pieces = [key1, key2]
2595 if len:
2596 pieces.append("LEN")
2597 if idx:
2598 pieces.append("IDX")
2599 if minmatchlen != 0:
2600 pieces.extend(["MINMATCHLEN", minmatchlen])
2601 if withmatchlen:
2602 pieces.append("WITHMATCHLEN")
2603 return self.execute_command("LCS", *pieces, keys=[key1, key2])
2606class AsyncBasicKeyCommands(BasicKeyCommands):
2607 def __delitem__(self, name: KeyT):
2608 raise TypeError("Async Redis client does not support class deletion")
2610 def __contains__(self, name: KeyT):
2611 raise TypeError("Async Redis client does not support class inclusion")
2613 def __getitem__(self, name: KeyT):
2614 raise TypeError("Async Redis client does not support class retrieval")
2616 def __setitem__(self, name: KeyT, value: EncodableT):
2617 raise TypeError("Async Redis client does not support class assignment")
2619 async def watch(self, *names: KeyT) -> None:
2620 return super().watch(*names)
2622 async def unwatch(self) -> None:
2623 return super().unwatch()
2626class ListCommands(CommandsProtocol):
2627 """
2628 Redis commands for List data type.
2629 see: https://redis.io/topics/data-types#lists
2630 """
2632 def blpop(
2633 self, keys: List, timeout: Optional[Number] = 0
2634 ) -> Union[Awaitable[list], list]:
2635 """
2636 LPOP a value off of the first non-empty list
2637 named in the ``keys`` list.
2639 If none of the lists in ``keys`` has a value to LPOP, then block
2640 for ``timeout`` seconds, or until a value gets pushed on to one
2641 of the lists.
2643 If timeout is 0, then block indefinitely.
2645 For more information, see https://redis.io/commands/blpop
2646 """
2647 if timeout is None:
2648 timeout = 0
2649 keys = list_or_args(keys, None)
2650 keys.append(timeout)
2651 return self.execute_command("BLPOP", *keys)
2653 def brpop(
2654 self, keys: List, timeout: Optional[Number] = 0
2655 ) -> Union[Awaitable[list], list]:
2656 """
2657 RPOP a value off of the first non-empty list
2658 named in the ``keys`` list.
2660 If none of the lists in ``keys`` has a value to RPOP, then block
2661 for ``timeout`` seconds, or until a value gets pushed on to one
2662 of the lists.
2664 If timeout is 0, then block indefinitely.
2666 For more information, see https://redis.io/commands/brpop
2667 """
2668 if timeout is None:
2669 timeout = 0
2670 keys = list_or_args(keys, None)
2671 keys.append(timeout)
2672 return self.execute_command("BRPOP", *keys)
2674 def brpoplpush(
2675 self, src: str, dst: str, timeout: Optional[Number] = 0
2676 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
2677 """
2678 Pop a value off the tail of ``src``, push it on the head of ``dst``
2679 and then return it.
2681 This command blocks until a value is in ``src`` or until ``timeout``
2682 seconds elapse, whichever is first. A ``timeout`` value of 0 blocks
2683 forever.
2685 For more information, see https://redis.io/commands/brpoplpush
2686 """
2687 if timeout is None:
2688 timeout = 0
2689 return self.execute_command("BRPOPLPUSH", src, dst, timeout)
2691 def blmpop(
2692 self,
2693 timeout: float,
2694 numkeys: int,
2695 *args: str,
2696 direction: str,
2697 count: Optional[int] = 1,
2698 ) -> Optional[list]:
2699 """
2700 Pop ``count`` values (default 1) from first non-empty in the list
2701 of provided key names.
2703 When all lists are empty this command blocks the connection until another
2704 client pushes to it or until the timeout, timeout of 0 blocks indefinitely
2706 For more information, see https://redis.io/commands/blmpop
2707 """
2708 cmd_args = [timeout, numkeys, *args, direction, "COUNT", count]
2710 return self.execute_command("BLMPOP", *cmd_args)
2712 def lmpop(
2713 self,
2714 num_keys: int,
2715 *args: str,
2716 direction: str,
2717 count: Optional[int] = 1,
2718 ) -> Union[Awaitable[list], list]:
2719 """
2720 Pop ``count`` values (default 1) first non-empty list key from the list
2721 of args provided key names.
2723 For more information, see https://redis.io/commands/lmpop
2724 """
2725 cmd_args = [num_keys] + list(args) + [direction]
2726 if count != 1:
2727 cmd_args.extend(["COUNT", count])
2729 return self.execute_command("LMPOP", *cmd_args)
2731 def lindex(
2732 self, name: str, index: int
2733 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
2734 """
2735 Return the item from list ``name`` at position ``index``
2737 Negative indexes are supported and will return an item at the
2738 end of the list
2740 For more information, see https://redis.io/commands/lindex
2741 """
2742 return self.execute_command("LINDEX", name, index, keys=[name])
2744 def linsert(
2745 self, name: str, where: str, refvalue: str, value: str
2746 ) -> Union[Awaitable[int], int]:
2747 """
2748 Insert ``value`` in list ``name`` either immediately before or after
2749 [``where``] ``refvalue``
2751 Returns the new length of the list on success or -1 if ``refvalue``
2752 is not in the list.
2754 For more information, see https://redis.io/commands/linsert
2755 """
2756 return self.execute_command("LINSERT", name, where, refvalue, value)
2758 def llen(self, name: str) -> Union[Awaitable[int], int]:
2759 """
2760 Return the length of the list ``name``
2762 For more information, see https://redis.io/commands/llen
2763 """
2764 return self.execute_command("LLEN", name, keys=[name])
2766 def lpop(
2767 self,
2768 name: str,
2769 count: Optional[int] = None,
2770 ) -> Union[Awaitable[Union[str, List, None]], Union[str, List, None]]:
2771 """
2772 Removes and returns the first elements of the list ``name``.
2774 By default, the command pops a single element from the beginning of
2775 the list. When provided with the optional ``count`` argument, the reply
2776 will consist of up to count elements, depending on the list's length.
2778 For more information, see https://redis.io/commands/lpop
2779 """
2780 if count is not None:
2781 return self.execute_command("LPOP", name, count)
2782 else:
2783 return self.execute_command("LPOP", name)
2785 def lpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2786 """
2787 Push ``values`` onto the head of the list ``name``
2789 For more information, see https://redis.io/commands/lpush
2790 """
2791 return self.execute_command("LPUSH", name, *values)
2793 def lpushx(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2794 """
2795 Push ``value`` onto the head of the list ``name`` if ``name`` exists
2797 For more information, see https://redis.io/commands/lpushx
2798 """
2799 return self.execute_command("LPUSHX", name, *values)
2801 def lrange(self, name: str, start: int, end: int) -> Union[Awaitable[list], list]:
2802 """
2803 Return a slice of the list ``name`` between
2804 position ``start`` and ``end``
2806 ``start`` and ``end`` can be negative numbers just like
2807 Python slicing notation
2809 For more information, see https://redis.io/commands/lrange
2810 """
2811 return self.execute_command("LRANGE", name, start, end, keys=[name])
2813 def lrem(self, name: str, count: int, value: str) -> Union[Awaitable[int], int]:
2814 """
2815 Remove the first ``count`` occurrences of elements equal to ``value``
2816 from the list stored at ``name``.
2818 The count argument influences the operation in the following ways:
2819 count > 0: Remove elements equal to value moving from head to tail.
2820 count < 0: Remove elements equal to value moving from tail to head.
2821 count = 0: Remove all elements equal to value.
2823 For more information, see https://redis.io/commands/lrem
2824 """
2825 return self.execute_command("LREM", name, count, value)
2827 def lset(self, name: str, index: int, value: str) -> Union[Awaitable[str], str]:
2828 """
2829 Set element at ``index`` of list ``name`` to ``value``
2831 For more information, see https://redis.io/commands/lset
2832 """
2833 return self.execute_command("LSET", name, index, value)
2835 def ltrim(self, name: str, start: int, end: int) -> Union[Awaitable[str], str]:
2836 """
2837 Trim the list ``name``, removing all values not within the slice
2838 between ``start`` and ``end``
2840 ``start`` and ``end`` can be negative numbers just like
2841 Python slicing notation
2843 For more information, see https://redis.io/commands/ltrim
2844 """
2845 return self.execute_command("LTRIM", name, start, end)
2847 def rpop(
2848 self,
2849 name: str,
2850 count: Optional[int] = None,
2851 ) -> Union[Awaitable[Union[str, List, None]], Union[str, List, None]]:
2852 """
2853 Removes and returns the last elements of the list ``name``.
2855 By default, the command pops a single element from the end of the list.
2856 When provided with the optional ``count`` argument, the reply will
2857 consist of up to count elements, depending on the list's length.
2859 For more information, see https://redis.io/commands/rpop
2860 """
2861 if count is not None:
2862 return self.execute_command("RPOP", name, count)
2863 else:
2864 return self.execute_command("RPOP", name)
2866 def rpoplpush(self, src: str, dst: str) -> Union[Awaitable[str], str]:
2867 """
2868 RPOP a value off of the ``src`` list and atomically LPUSH it
2869 on to the ``dst`` list. Returns the value.
2871 For more information, see https://redis.io/commands/rpoplpush
2872 """
2873 return self.execute_command("RPOPLPUSH", src, dst)
2875 def rpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2876 """
2877 Push ``values`` onto the tail of the list ``name``
2879 For more information, see https://redis.io/commands/rpush
2880 """
2881 return self.execute_command("RPUSH", name, *values)
2883 def rpushx(self, name: str, *values: str) -> Union[Awaitable[int], int]:
2884 """
2885 Push ``value`` onto the tail of the list ``name`` if ``name`` exists
2887 For more information, see https://redis.io/commands/rpushx
2888 """
2889 return self.execute_command("RPUSHX", name, *values)
2891 def lpos(
2892 self,
2893 name: str,
2894 value: str,
2895 rank: Optional[int] = None,
2896 count: Optional[int] = None,
2897 maxlen: Optional[int] = None,
2898 ) -> Union[str, List, None]:
2899 """
2900 Get position of ``value`` within the list ``name``
2902 If specified, ``rank`` indicates the "rank" of the first element to
2903 return in case there are multiple copies of ``value`` in the list.
2904 By default, LPOS returns the position of the first occurrence of
2905 ``value`` in the list. When ``rank`` 2, LPOS returns the position of
2906 the second ``value`` in the list. If ``rank`` is negative, LPOS
2907 searches the list in reverse. For example, -1 would return the
2908 position of the last occurrence of ``value`` and -2 would return the
2909 position of the next to last occurrence of ``value``.
2911 If specified, ``count`` indicates that LPOS should return a list of
2912 up to ``count`` positions. A ``count`` of 2 would return a list of
2913 up to 2 positions. A ``count`` of 0 returns a list of all positions
2914 matching ``value``. When ``count`` is specified and but ``value``
2915 does not exist in the list, an empty list is returned.
2917 If specified, ``maxlen`` indicates the maximum number of list
2918 elements to scan. A ``maxlen`` of 1000 will only return the
2919 position(s) of items within the first 1000 entries in the list.
2920 A ``maxlen`` of 0 (the default) will scan the entire list.
2922 For more information, see https://redis.io/commands/lpos
2923 """
2924 pieces: list[EncodableT] = [name, value]
2925 if rank is not None:
2926 pieces.extend(["RANK", rank])
2928 if count is not None:
2929 pieces.extend(["COUNT", count])
2931 if maxlen is not None:
2932 pieces.extend(["MAXLEN", maxlen])
2934 return self.execute_command("LPOS", *pieces, keys=[name])
2936 def sort(
2937 self,
2938 name: str,
2939 start: Optional[int] = None,
2940 num: Optional[int] = None,
2941 by: Optional[str] = None,
2942 get: Optional[List[str]] = None,
2943 desc: bool = False,
2944 alpha: bool = False,
2945 store: Optional[str] = None,
2946 groups: Optional[bool] = False,
2947 ) -> Union[List, int]:
2948 """
2949 Sort and return the list, set or sorted set at ``name``.
2951 ``start`` and ``num`` allow for paging through the sorted data
2953 ``by`` allows using an external key to weight and sort the items.
2954 Use an "*" to indicate where in the key the item value is located
2956 ``get`` allows for returning items from external keys rather than the
2957 sorted data itself. Use an "*" to indicate where in the key
2958 the item value is located
2960 ``desc`` allows for reversing the sort
2962 ``alpha`` allows for sorting lexicographically rather than numerically
2964 ``store`` allows for storing the result of the sort into
2965 the key ``store``
2967 ``groups`` if set to True and if ``get`` contains at least two
2968 elements, sort will return a list of tuples, each containing the
2969 values fetched from the arguments to ``get``.
2971 For more information, see https://redis.io/commands/sort
2972 """
2973 if (start is not None and num is None) or (num is not None and start is None):
2974 raise DataError("``start`` and ``num`` must both be specified")
2976 pieces: list[EncodableT] = [name]
2977 if by is not None:
2978 pieces.extend([b"BY", by])
2979 if start is not None and num is not None:
2980 pieces.extend([b"LIMIT", start, num])
2981 if get is not None:
2982 # If get is a string assume we want to get a single value.
2983 # Otherwise assume it's an interable and we want to get multiple
2984 # values. We can't just iterate blindly because strings are
2985 # iterable.
2986 if isinstance(get, (bytes, str)):
2987 pieces.extend([b"GET", get])
2988 else:
2989 for g in get:
2990 pieces.extend([b"GET", g])
2991 if desc:
2992 pieces.append(b"DESC")
2993 if alpha:
2994 pieces.append(b"ALPHA")
2995 if store is not None:
2996 pieces.extend([b"STORE", store])
2997 if groups:
2998 if not get or isinstance(get, (bytes, str)) or len(get) < 2:
2999 raise DataError(
3000 'when using "groups" the "get" argument '
3001 "must be specified and contain at least "
3002 "two keys"
3003 )
3005 options = {"groups": len(get) if groups else None}
3006 options["keys"] = [name]
3007 return self.execute_command("SORT", *pieces, **options)
3009 def sort_ro(
3010 self,
3011 key: str,
3012 start: Optional[int] = None,
3013 num: Optional[int] = None,
3014 by: Optional[str] = None,
3015 get: Optional[List[str]] = None,
3016 desc: bool = False,
3017 alpha: bool = False,
3018 ) -> list:
3019 """
3020 Returns the elements contained in the list, set or sorted set at key.
3021 (read-only variant of the SORT command)
3023 ``start`` and ``num`` allow for paging through the sorted data
3025 ``by`` allows using an external key to weight and sort the items.
3026 Use an "*" to indicate where in the key the item value is located
3028 ``get`` allows for returning items from external keys rather than the
3029 sorted data itself. Use an "*" to indicate where in the key
3030 the item value is located
3032 ``desc`` allows for reversing the sort
3034 ``alpha`` allows for sorting lexicographically rather than numerically
3036 For more information, see https://redis.io/commands/sort_ro
3037 """
3038 return self.sort(
3039 key, start=start, num=num, by=by, get=get, desc=desc, alpha=alpha
3040 )
3043AsyncListCommands = ListCommands
3046class ScanCommands(CommandsProtocol):
3047 """
3048 Redis SCAN commands.
3049 see: https://redis.io/commands/scan
3050 """
3052 def scan(
3053 self,
3054 cursor: int = 0,
3055 match: Union[PatternT, None] = None,
3056 count: Optional[int] = None,
3057 _type: Optional[str] = None,
3058 **kwargs,
3059 ) -> ResponseT:
3060 """
3061 Incrementally return lists of key names. Also return a cursor
3062 indicating the scan position.
3064 ``match`` allows for filtering the keys by pattern
3066 ``count`` provides a hint to Redis about the number of keys to
3067 return per batch.
3069 ``_type`` filters the returned values by a particular Redis type.
3070 Stock Redis instances allow for the following types:
3071 HASH, LIST, SET, STREAM, STRING, ZSET
3072 Additionally, Redis modules can expose other types as well.
3074 For more information, see https://redis.io/commands/scan
3075 """
3076 pieces: list[EncodableT] = [cursor]
3077 if match is not None:
3078 pieces.extend([b"MATCH", match])
3079 if count is not None:
3080 pieces.extend([b"COUNT", count])
3081 if _type is not None:
3082 pieces.extend([b"TYPE", _type])
3083 return self.execute_command("SCAN", *pieces, **kwargs)
3085 def scan_iter(
3086 self,
3087 match: Union[PatternT, None] = None,
3088 count: Optional[int] = None,
3089 _type: Optional[str] = None,
3090 **kwargs,
3091 ) -> Iterator:
3092 """
3093 Make an iterator using the SCAN command so that the client doesn't
3094 need to remember the cursor position.
3096 ``match`` allows for filtering the keys by pattern
3098 ``count`` provides a hint to Redis about the number of keys to
3099 return per batch.
3101 ``_type`` filters the returned values by a particular Redis type.
3102 Stock Redis instances allow for the following types:
3103 HASH, LIST, SET, STREAM, STRING, ZSET
3104 Additionally, Redis modules can expose other types as well.
3105 """
3106 cursor = "0"
3107 while cursor != 0:
3108 cursor, data = self.scan(
3109 cursor=cursor, match=match, count=count, _type=_type, **kwargs
3110 )
3111 yield from data
3113 def sscan(
3114 self,
3115 name: KeyT,
3116 cursor: int = 0,
3117 match: Union[PatternT, None] = None,
3118 count: Optional[int] = None,
3119 ) -> ResponseT:
3120 """
3121 Incrementally return lists of elements in a set. Also return a cursor
3122 indicating the scan position.
3124 ``match`` allows for filtering the keys by pattern
3126 ``count`` allows for hint the minimum number of returns
3128 For more information, see https://redis.io/commands/sscan
3129 """
3130 pieces: list[EncodableT] = [name, cursor]
3131 if match is not None:
3132 pieces.extend([b"MATCH", match])
3133 if count is not None:
3134 pieces.extend([b"COUNT", count])
3135 return self.execute_command("SSCAN", *pieces)
3137 def sscan_iter(
3138 self,
3139 name: KeyT,
3140 match: Union[PatternT, None] = None,
3141 count: Optional[int] = None,
3142 ) -> Iterator:
3143 """
3144 Make an iterator using the SSCAN command so that the client doesn't
3145 need to remember the cursor position.
3147 ``match`` allows for filtering the keys by pattern
3149 ``count`` allows for hint the minimum number of returns
3150 """
3151 cursor = "0"
3152 while cursor != 0:
3153 cursor, data = self.sscan(name, cursor=cursor, match=match, count=count)
3154 yield from data
3156 def hscan(
3157 self,
3158 name: KeyT,
3159 cursor: int = 0,
3160 match: Union[PatternT, None] = None,
3161 count: Optional[int] = None,
3162 no_values: Union[bool, None] = None,
3163 ) -> ResponseT:
3164 """
3165 Incrementally return key/value slices in a hash. Also return a cursor
3166 indicating the scan position.
3168 ``match`` allows for filtering the keys by pattern
3170 ``count`` allows for hint the minimum number of returns
3172 ``no_values`` indicates to return only the keys, without values.
3174 For more information, see https://redis.io/commands/hscan
3175 """
3176 pieces: list[EncodableT] = [name, cursor]
3177 if match is not None:
3178 pieces.extend([b"MATCH", match])
3179 if count is not None:
3180 pieces.extend([b"COUNT", count])
3181 if no_values is not None:
3182 pieces.extend([b"NOVALUES"])
3183 return self.execute_command("HSCAN", *pieces, no_values=no_values)
3185 def hscan_iter(
3186 self,
3187 name: str,
3188 match: Union[PatternT, None] = None,
3189 count: Optional[int] = None,
3190 no_values: Union[bool, None] = None,
3191 ) -> Iterator:
3192 """
3193 Make an iterator using the HSCAN command so that the client doesn't
3194 need to remember the cursor position.
3196 ``match`` allows for filtering the keys by pattern
3198 ``count`` allows for hint the minimum number of returns
3200 ``no_values`` indicates to return only the keys, without values
3201 """
3202 cursor = "0"
3203 while cursor != 0:
3204 cursor, data = self.hscan(
3205 name, cursor=cursor, match=match, count=count, no_values=no_values
3206 )
3207 if no_values:
3208 yield from data
3209 else:
3210 yield from data.items()
3212 def zscan(
3213 self,
3214 name: KeyT,
3215 cursor: int = 0,
3216 match: Union[PatternT, None] = None,
3217 count: Optional[int] = None,
3218 score_cast_func: Union[type, Callable] = float,
3219 ) -> ResponseT:
3220 """
3221 Incrementally return lists of elements in a sorted set. Also return a
3222 cursor indicating the scan position.
3224 ``match`` allows for filtering the keys by pattern
3226 ``count`` allows for hint the minimum number of returns
3228 ``score_cast_func`` a callable used to cast the score return value
3230 For more information, see https://redis.io/commands/zscan
3231 """
3232 pieces = [name, cursor]
3233 if match is not None:
3234 pieces.extend([b"MATCH", match])
3235 if count is not None:
3236 pieces.extend([b"COUNT", count])
3237 options = {"score_cast_func": score_cast_func}
3238 return self.execute_command("ZSCAN", *pieces, **options)
3240 def zscan_iter(
3241 self,
3242 name: KeyT,
3243 match: Union[PatternT, None] = None,
3244 count: Optional[int] = None,
3245 score_cast_func: Union[type, Callable] = float,
3246 ) -> Iterator:
3247 """
3248 Make an iterator using the ZSCAN command so that the client doesn't
3249 need to remember the cursor position.
3251 ``match`` allows for filtering the keys by pattern
3253 ``count`` allows for hint the minimum number of returns
3255 ``score_cast_func`` a callable used to cast the score return value
3256 """
3257 cursor = "0"
3258 while cursor != 0:
3259 cursor, data = self.zscan(
3260 name,
3261 cursor=cursor,
3262 match=match,
3263 count=count,
3264 score_cast_func=score_cast_func,
3265 )
3266 yield from data
3269class AsyncScanCommands(ScanCommands):
3270 async def scan_iter(
3271 self,
3272 match: Union[PatternT, None] = None,
3273 count: Optional[int] = None,
3274 _type: Optional[str] = None,
3275 **kwargs,
3276 ) -> AsyncIterator:
3277 """
3278 Make an iterator using the SCAN command so that the client doesn't
3279 need to remember the cursor position.
3281 ``match`` allows for filtering the keys by pattern
3283 ``count`` provides a hint to Redis about the number of keys to
3284 return per batch.
3286 ``_type`` filters the returned values by a particular Redis type.
3287 Stock Redis instances allow for the following types:
3288 HASH, LIST, SET, STREAM, STRING, ZSET
3289 Additionally, Redis modules can expose other types as well.
3290 """
3291 cursor = "0"
3292 while cursor != 0:
3293 cursor, data = await self.scan(
3294 cursor=cursor, match=match, count=count, _type=_type, **kwargs
3295 )
3296 for d in data:
3297 yield d
3299 async def sscan_iter(
3300 self,
3301 name: KeyT,
3302 match: Union[PatternT, None] = None,
3303 count: Optional[int] = None,
3304 ) -> AsyncIterator:
3305 """
3306 Make an iterator using the SSCAN command so that the client doesn't
3307 need to remember the cursor position.
3309 ``match`` allows for filtering the keys by pattern
3311 ``count`` allows for hint the minimum number of returns
3312 """
3313 cursor = "0"
3314 while cursor != 0:
3315 cursor, data = await self.sscan(
3316 name, cursor=cursor, match=match, count=count
3317 )
3318 for d in data:
3319 yield d
3321 async def hscan_iter(
3322 self,
3323 name: str,
3324 match: Union[PatternT, None] = None,
3325 count: Optional[int] = None,
3326 no_values: Union[bool, None] = None,
3327 ) -> AsyncIterator:
3328 """
3329 Make an iterator using the HSCAN command so that the client doesn't
3330 need to remember the cursor position.
3332 ``match`` allows for filtering the keys by pattern
3334 ``count`` allows for hint the minimum number of returns
3336 ``no_values`` indicates to return only the keys, without values
3337 """
3338 cursor = "0"
3339 while cursor != 0:
3340 cursor, data = await self.hscan(
3341 name, cursor=cursor, match=match, count=count, no_values=no_values
3342 )
3343 if no_values:
3344 for it in data:
3345 yield it
3346 else:
3347 for it in data.items():
3348 yield it
3350 async def zscan_iter(
3351 self,
3352 name: KeyT,
3353 match: Union[PatternT, None] = None,
3354 count: Optional[int] = None,
3355 score_cast_func: Union[type, Callable] = float,
3356 ) -> AsyncIterator:
3357 """
3358 Make an iterator using the ZSCAN command so that the client doesn't
3359 need to remember the cursor position.
3361 ``match`` allows for filtering the keys by pattern
3363 ``count`` allows for hint the minimum number of returns
3365 ``score_cast_func`` a callable used to cast the score return value
3366 """
3367 cursor = "0"
3368 while cursor != 0:
3369 cursor, data = await self.zscan(
3370 name,
3371 cursor=cursor,
3372 match=match,
3373 count=count,
3374 score_cast_func=score_cast_func,
3375 )
3376 for d in data:
3377 yield d
3380class SetCommands(CommandsProtocol):
3381 """
3382 Redis commands for Set data type.
3383 see: https://redis.io/topics/data-types#sets
3384 """
3386 def sadd(self, name: KeyT, *values: FieldT) -> Union[Awaitable[int], int]:
3387 """
3388 Add ``value(s)`` to set ``name``
3390 For more information, see https://redis.io/commands/sadd
3391 """
3392 return self.execute_command("SADD", name, *values)
3394 def scard(self, name: KeyT) -> Union[Awaitable[int], int]:
3395 """
3396 Return the number of elements in set ``name``
3398 For more information, see https://redis.io/commands/scard
3399 """
3400 return self.execute_command("SCARD", name, keys=[name])
3402 def sdiff(self, keys: List, *args: List) -> Union[Awaitable[list], list]:
3403 """
3404 Return the difference of sets specified by ``keys``
3406 For more information, see https://redis.io/commands/sdiff
3407 """
3408 args = list_or_args(keys, args)
3409 return self.execute_command("SDIFF", *args, keys=args)
3411 def sdiffstore(
3412 self, dest: str, keys: List, *args: List
3413 ) -> Union[Awaitable[int], int]:
3414 """
3415 Store the difference of sets specified by ``keys`` into a new
3416 set named ``dest``. Returns the number of keys in the new set.
3418 For more information, see https://redis.io/commands/sdiffstore
3419 """
3420 args = list_or_args(keys, args)
3421 return self.execute_command("SDIFFSTORE", dest, *args)
3423 def sinter(self, keys: List, *args: List) -> Union[Awaitable[list], list]:
3424 """
3425 Return the intersection of sets specified by ``keys``
3427 For more information, see https://redis.io/commands/sinter
3428 """
3429 args = list_or_args(keys, args)
3430 return self.execute_command("SINTER", *args, keys=args)
3432 def sintercard(
3433 self, numkeys: int, keys: List[KeyT], limit: int = 0
3434 ) -> Union[Awaitable[int], int]:
3435 """
3436 Return the cardinality of the intersect of multiple sets specified by ``keys``.
3438 When LIMIT provided (defaults to 0 and means unlimited), if the intersection
3439 cardinality reaches limit partway through the computation, the algorithm will
3440 exit and yield limit as the cardinality
3442 For more information, see https://redis.io/commands/sintercard
3443 """
3444 args = [numkeys, *keys, "LIMIT", limit]
3445 return self.execute_command("SINTERCARD", *args, keys=keys)
3447 def sinterstore(
3448 self, dest: KeyT, keys: List, *args: List
3449 ) -> Union[Awaitable[int], int]:
3450 """
3451 Store the intersection of sets specified by ``keys`` into a new
3452 set named ``dest``. Returns the number of keys in the new set.
3454 For more information, see https://redis.io/commands/sinterstore
3455 """
3456 args = list_or_args(keys, args)
3457 return self.execute_command("SINTERSTORE", dest, *args)
3459 def sismember(
3460 self, name: KeyT, value: str
3461 ) -> Union[Awaitable[Union[Literal[0], Literal[1]]], Union[Literal[0], Literal[1]]]:
3462 """
3463 Return whether ``value`` is a member of set ``name``:
3464 - 1 if the value is a member of the set.
3465 - 0 if the value is not a member of the set or if key does not exist.
3467 For more information, see https://redis.io/commands/sismember
3468 """
3469 return self.execute_command("SISMEMBER", name, value, keys=[name])
3471 def smembers(self, name: KeyT) -> Union[Awaitable[Set], Set]:
3472 """
3473 Return all members of the set ``name``
3475 For more information, see https://redis.io/commands/smembers
3476 """
3477 return self.execute_command("SMEMBERS", name, keys=[name])
3479 def smismember(
3480 self, name: KeyT, values: List, *args: List
3481 ) -> Union[
3482 Awaitable[List[Union[Literal[0], Literal[1]]]],
3483 List[Union[Literal[0], Literal[1]]],
3484 ]:
3485 """
3486 Return whether each value in ``values`` is a member of the set ``name``
3487 as a list of ``int`` in the order of ``values``:
3488 - 1 if the value is a member of the set.
3489 - 0 if the value is not a member of the set or if key does not exist.
3491 For more information, see https://redis.io/commands/smismember
3492 """
3493 args = list_or_args(values, args)
3494 return self.execute_command("SMISMEMBER", name, *args, keys=[name])
3496 def smove(self, src: KeyT, dst: KeyT, value: str) -> Union[Awaitable[bool], bool]:
3497 """
3498 Move ``value`` from set ``src`` to set ``dst`` atomically
3500 For more information, see https://redis.io/commands/smove
3501 """
3502 return self.execute_command("SMOVE", src, dst, value)
3504 def spop(self, name: KeyT, count: Optional[int] = None) -> Union[str, List, None]:
3505 """
3506 Remove and return a random member of set ``name``
3508 For more information, see https://redis.io/commands/spop
3509 """
3510 args = (count is not None) and [count] or []
3511 return self.execute_command("SPOP", name, *args)
3513 def srandmember(
3514 self, name: KeyT, number: Optional[int] = None
3515 ) -> Union[str, List, None]:
3516 """
3517 If ``number`` is None, returns a random member of set ``name``.
3519 If ``number`` is supplied, returns a list of ``number`` random
3520 members of set ``name``. Note this is only available when running
3521 Redis 2.6+.
3523 For more information, see https://redis.io/commands/srandmember
3524 """
3525 args = (number is not None) and [number] or []
3526 return self.execute_command("SRANDMEMBER", name, *args)
3528 def srem(self, name: KeyT, *values: FieldT) -> Union[Awaitable[int], int]:
3529 """
3530 Remove ``values`` from set ``name``
3532 For more information, see https://redis.io/commands/srem
3533 """
3534 return self.execute_command("SREM", name, *values)
3536 def sunion(self, keys: List, *args: List) -> Union[Awaitable[List], List]:
3537 """
3538 Return the union of sets specified by ``keys``
3540 For more information, see https://redis.io/commands/sunion
3541 """
3542 args = list_or_args(keys, args)
3543 return self.execute_command("SUNION", *args, keys=args)
3545 def sunionstore(
3546 self, dest: KeyT, keys: List, *args: List
3547 ) -> Union[Awaitable[int], int]:
3548 """
3549 Store the union of sets specified by ``keys`` into a new
3550 set named ``dest``. Returns the number of keys in the new set.
3552 For more information, see https://redis.io/commands/sunionstore
3553 """
3554 args = list_or_args(keys, args)
3555 return self.execute_command("SUNIONSTORE", dest, *args)
3558AsyncSetCommands = SetCommands
3561class StreamCommands(CommandsProtocol):
3562 """
3563 Redis commands for Stream data type.
3564 see: https://redis.io/topics/streams-intro
3565 """
3567 def xack(self, name: KeyT, groupname: GroupT, *ids: StreamIdT) -> ResponseT:
3568 """
3569 Acknowledges the successful processing of one or more messages.
3571 Args:
3572 name: name of the stream.
3573 groupname: name of the consumer group.
3574 *ids: message ids to acknowledge.
3576 For more information, see https://redis.io/commands/xack
3577 """
3578 return self.execute_command("XACK", name, groupname, *ids)
3580 def xackdel(
3581 self,
3582 name: KeyT,
3583 groupname: GroupT,
3584 *ids: StreamIdT,
3585 ref_policy: Literal["KEEPREF", "DELREF", "ACKED"] = "KEEPREF",
3586 ) -> ResponseT:
3587 """
3588 Combines the functionality of XACK and XDEL. Acknowledges the specified
3589 message IDs in the given consumer group and simultaneously attempts to
3590 delete the corresponding entries from the stream.
3591 """
3592 if not ids:
3593 raise DataError("XACKDEL requires at least one message ID")
3595 if ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
3596 raise DataError("XACKDEL ref_policy must be one of: KEEPREF, DELREF, ACKED")
3598 pieces = [name, groupname, ref_policy, "IDS", len(ids)]
3599 pieces.extend(ids)
3600 return self.execute_command("XACKDEL", *pieces)
3602 def xadd(
3603 self,
3604 name: KeyT,
3605 fields: Dict[FieldT, EncodableT],
3606 id: StreamIdT = "*",
3607 maxlen: Optional[int] = None,
3608 approximate: bool = True,
3609 nomkstream: bool = False,
3610 minid: Union[StreamIdT, None] = None,
3611 limit: Optional[int] = None,
3612 ref_policy: Optional[Literal["KEEPREF", "DELREF", "ACKED"]] = None,
3613 ) -> ResponseT:
3614 """
3615 Add to a stream.
3616 name: name of the stream
3617 fields: dict of field/value pairs to insert into the stream
3618 id: Location to insert this record. By default it is appended.
3619 maxlen: truncate old stream members beyond this size.
3620 Can't be specified with minid.
3621 approximate: actual stream length may be slightly more than maxlen
3622 nomkstream: When set to true, do not make a stream
3623 minid: the minimum id in the stream to query.
3624 Can't be specified with maxlen.
3625 limit: specifies the maximum number of entries to retrieve
3626 ref_policy: optional reference policy for consumer groups when trimming:
3627 - KEEPREF (default): When trimming, preserves references in consumer groups' PEL
3628 - DELREF: When trimming, removes all references from consumer groups' PEL
3629 - ACKED: When trimming, only removes entries acknowledged by all consumer groups
3631 For more information, see https://redis.io/commands/xadd
3632 """
3633 pieces: list[EncodableT] = []
3634 if maxlen is not None and minid is not None:
3635 raise DataError("Only one of ```maxlen``` or ```minid``` may be specified")
3637 if ref_policy is not None and ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
3638 raise DataError("XADD ref_policy must be one of: KEEPREF, DELREF, ACKED")
3640 if maxlen is not None:
3641 if not isinstance(maxlen, int) or maxlen < 0:
3642 raise DataError("XADD maxlen must be non-negative integer")
3643 pieces.append(b"MAXLEN")
3644 if approximate:
3645 pieces.append(b"~")
3646 pieces.append(str(maxlen))
3647 if minid is not None:
3648 pieces.append(b"MINID")
3649 if approximate:
3650 pieces.append(b"~")
3651 pieces.append(minid)
3652 if limit is not None:
3653 pieces.extend([b"LIMIT", limit])
3654 if nomkstream:
3655 pieces.append(b"NOMKSTREAM")
3656 if ref_policy is not None:
3657 pieces.append(ref_policy)
3658 pieces.append(id)
3659 if not isinstance(fields, dict) or len(fields) == 0:
3660 raise DataError("XADD fields must be a non-empty dict")
3661 for pair in fields.items():
3662 pieces.extend(pair)
3663 return self.execute_command("XADD", name, *pieces)
3665 def xautoclaim(
3666 self,
3667 name: KeyT,
3668 groupname: GroupT,
3669 consumername: ConsumerT,
3670 min_idle_time: int,
3671 start_id: StreamIdT = "0-0",
3672 count: Optional[int] = None,
3673 justid: bool = False,
3674 ) -> ResponseT:
3675 """
3676 Transfers ownership of pending stream entries that match the specified
3677 criteria. Conceptually, equivalent to calling XPENDING and then XCLAIM,
3678 but provides a more straightforward way to deal with message delivery
3679 failures via SCAN-like semantics.
3680 name: name of the stream.
3681 groupname: name of the consumer group.
3682 consumername: name of a consumer that claims the message.
3683 min_idle_time: filter messages that were idle less than this amount of
3684 milliseconds.
3685 start_id: filter messages with equal or greater ID.
3686 count: optional integer, upper limit of the number of entries that the
3687 command attempts to claim. Set to 100 by default.
3688 justid: optional boolean, false by default. Return just an array of IDs
3689 of messages successfully claimed, without returning the actual message
3691 For more information, see https://redis.io/commands/xautoclaim
3692 """
3693 try:
3694 if int(min_idle_time) < 0:
3695 raise DataError(
3696 "XAUTOCLAIM min_idle_time must be a nonnegative integer"
3697 )
3698 except TypeError:
3699 pass
3701 kwargs = {}
3702 pieces = [name, groupname, consumername, min_idle_time, start_id]
3704 try:
3705 if int(count) < 0:
3706 raise DataError("XPENDING count must be a integer >= 0")
3707 pieces.extend([b"COUNT", count])
3708 except TypeError:
3709 pass
3710 if justid:
3711 pieces.append(b"JUSTID")
3712 kwargs["parse_justid"] = True
3714 return self.execute_command("XAUTOCLAIM", *pieces, **kwargs)
3716 def xclaim(
3717 self,
3718 name: KeyT,
3719 groupname: GroupT,
3720 consumername: ConsumerT,
3721 min_idle_time: int,
3722 message_ids: Union[List[StreamIdT], Tuple[StreamIdT]],
3723 idle: Optional[int] = None,
3724 time: Optional[int] = None,
3725 retrycount: Optional[int] = None,
3726 force: bool = False,
3727 justid: bool = False,
3728 ) -> ResponseT:
3729 """
3730 Changes the ownership of a pending message.
3732 name: name of the stream.
3734 groupname: name of the consumer group.
3736 consumername: name of a consumer that claims the message.
3738 min_idle_time: filter messages that were idle less than this amount of
3739 milliseconds
3741 message_ids: non-empty list or tuple of message IDs to claim
3743 idle: optional. Set the idle time (last time it was delivered) of the
3744 message in ms
3746 time: optional integer. This is the same as idle but instead of a
3747 relative amount of milliseconds, it sets the idle time to a specific
3748 Unix time (in milliseconds).
3750 retrycount: optional integer. set the retry counter to the specified
3751 value. This counter is incremented every time a message is delivered
3752 again.
3754 force: optional boolean, false by default. Creates the pending message
3755 entry in the PEL even if certain specified IDs are not already in the
3756 PEL assigned to a different client.
3758 justid: optional boolean, false by default. Return just an array of IDs
3759 of messages successfully claimed, without returning the actual message
3761 For more information, see https://redis.io/commands/xclaim
3762 """
3763 if not isinstance(min_idle_time, int) or min_idle_time < 0:
3764 raise DataError("XCLAIM min_idle_time must be a non negative integer")
3765 if not isinstance(message_ids, (list, tuple)) or not message_ids:
3766 raise DataError(
3767 "XCLAIM message_ids must be a non empty list or "
3768 "tuple of message IDs to claim"
3769 )
3771 kwargs = {}
3772 pieces: list[EncodableT] = [name, groupname, consumername, str(min_idle_time)]
3773 pieces.extend(list(message_ids))
3775 if idle is not None:
3776 if not isinstance(idle, int):
3777 raise DataError("XCLAIM idle must be an integer")
3778 pieces.extend((b"IDLE", str(idle)))
3779 if time is not None:
3780 if not isinstance(time, int):
3781 raise DataError("XCLAIM time must be an integer")
3782 pieces.extend((b"TIME", str(time)))
3783 if retrycount is not None:
3784 if not isinstance(retrycount, int):
3785 raise DataError("XCLAIM retrycount must be an integer")
3786 pieces.extend((b"RETRYCOUNT", str(retrycount)))
3788 if force:
3789 if not isinstance(force, bool):
3790 raise DataError("XCLAIM force must be a boolean")
3791 pieces.append(b"FORCE")
3792 if justid:
3793 if not isinstance(justid, bool):
3794 raise DataError("XCLAIM justid must be a boolean")
3795 pieces.append(b"JUSTID")
3796 kwargs["parse_justid"] = True
3797 return self.execute_command("XCLAIM", *pieces, **kwargs)
3799 def xdel(self, name: KeyT, *ids: StreamIdT) -> ResponseT:
3800 """
3801 Deletes one or more messages from a stream.
3803 Args:
3804 name: name of the stream.
3805 *ids: message ids to delete.
3807 For more information, see https://redis.io/commands/xdel
3808 """
3809 return self.execute_command("XDEL", name, *ids)
3811 def xdelex(
3812 self,
3813 name: KeyT,
3814 *ids: StreamIdT,
3815 ref_policy: Literal["KEEPREF", "DELREF", "ACKED"] = "KEEPREF",
3816 ) -> ResponseT:
3817 """
3818 Extended version of XDEL that provides more control over how message entries
3819 are deleted concerning consumer groups.
3820 """
3821 if not ids:
3822 raise DataError("XDELEX requires at least one message ID")
3824 if ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
3825 raise DataError("XDELEX ref_policy must be one of: KEEPREF, DELREF, ACKED")
3827 pieces = [name, ref_policy, "IDS", len(ids)]
3828 pieces.extend(ids)
3829 return self.execute_command("XDELEX", *pieces)
3831 def xgroup_create(
3832 self,
3833 name: KeyT,
3834 groupname: GroupT,
3835 id: StreamIdT = "$",
3836 mkstream: bool = False,
3837 entries_read: Optional[int] = None,
3838 ) -> ResponseT:
3839 """
3840 Create a new consumer group associated with a stream.
3841 name: name of the stream.
3842 groupname: name of the consumer group.
3843 id: ID of the last item in the stream to consider already delivered.
3845 For more information, see https://redis.io/commands/xgroup-create
3846 """
3847 pieces: list[EncodableT] = ["XGROUP CREATE", name, groupname, id]
3848 if mkstream:
3849 pieces.append(b"MKSTREAM")
3850 if entries_read is not None:
3851 pieces.extend(["ENTRIESREAD", entries_read])
3853 return self.execute_command(*pieces)
3855 def xgroup_delconsumer(
3856 self, name: KeyT, groupname: GroupT, consumername: ConsumerT
3857 ) -> ResponseT:
3858 """
3859 Remove a specific consumer from a consumer group.
3860 Returns the number of pending messages that the consumer had before it
3861 was deleted.
3862 name: name of the stream.
3863 groupname: name of the consumer group.
3864 consumername: name of consumer to delete
3866 For more information, see https://redis.io/commands/xgroup-delconsumer
3867 """
3868 return self.execute_command("XGROUP DELCONSUMER", name, groupname, consumername)
3870 def xgroup_destroy(self, name: KeyT, groupname: GroupT) -> ResponseT:
3871 """
3872 Destroy a consumer group.
3873 name: name of the stream.
3874 groupname: name of the consumer group.
3876 For more information, see https://redis.io/commands/xgroup-destroy
3877 """
3878 return self.execute_command("XGROUP DESTROY", name, groupname)
3880 def xgroup_createconsumer(
3881 self, name: KeyT, groupname: GroupT, consumername: ConsumerT
3882 ) -> ResponseT:
3883 """
3884 Consumers in a consumer group are auto-created every time a new
3885 consumer name is mentioned by some command.
3886 They can be explicitly created by using this command.
3887 name: name of the stream.
3888 groupname: name of the consumer group.
3889 consumername: name of consumer to create.
3891 See: https://redis.io/commands/xgroup-createconsumer
3892 """
3893 return self.execute_command(
3894 "XGROUP CREATECONSUMER", name, groupname, consumername
3895 )
3897 def xgroup_setid(
3898 self,
3899 name: KeyT,
3900 groupname: GroupT,
3901 id: StreamIdT,
3902 entries_read: Optional[int] = None,
3903 ) -> ResponseT:
3904 """
3905 Set the consumer group last delivered ID to something else.
3906 name: name of the stream.
3907 groupname: name of the consumer group.
3908 id: ID of the last item in the stream to consider already delivered.
3910 For more information, see https://redis.io/commands/xgroup-setid
3911 """
3912 pieces = [name, groupname, id]
3913 if entries_read is not None:
3914 pieces.extend(["ENTRIESREAD", entries_read])
3915 return self.execute_command("XGROUP SETID", *pieces)
3917 def xinfo_consumers(self, name: KeyT, groupname: GroupT) -> ResponseT:
3918 """
3919 Returns general information about the consumers in the group.
3920 name: name of the stream.
3921 groupname: name of the consumer group.
3923 For more information, see https://redis.io/commands/xinfo-consumers
3924 """
3925 return self.execute_command("XINFO CONSUMERS", name, groupname)
3927 def xinfo_groups(self, name: KeyT) -> ResponseT:
3928 """
3929 Returns general information about the consumer groups of the stream.
3930 name: name of the stream.
3932 For more information, see https://redis.io/commands/xinfo-groups
3933 """
3934 return self.execute_command("XINFO GROUPS", name)
3936 def xinfo_stream(self, name: KeyT, full: bool = False) -> ResponseT:
3937 """
3938 Returns general information about the stream.
3939 name: name of the stream.
3940 full: optional boolean, false by default. Return full summary
3942 For more information, see https://redis.io/commands/xinfo-stream
3943 """
3944 pieces = [name]
3945 options = {}
3946 if full:
3947 pieces.append(b"FULL")
3948 options = {"full": full}
3949 return self.execute_command("XINFO STREAM", *pieces, **options)
3951 def xlen(self, name: KeyT) -> ResponseT:
3952 """
3953 Returns the number of elements in a given stream.
3955 For more information, see https://redis.io/commands/xlen
3956 """
3957 return self.execute_command("XLEN", name, keys=[name])
3959 def xpending(self, name: KeyT, groupname: GroupT) -> ResponseT:
3960 """
3961 Returns information about pending messages of a group.
3962 name: name of the stream.
3963 groupname: name of the consumer group.
3965 For more information, see https://redis.io/commands/xpending
3966 """
3967 return self.execute_command("XPENDING", name, groupname, keys=[name])
3969 def xpending_range(
3970 self,
3971 name: KeyT,
3972 groupname: GroupT,
3973 min: StreamIdT,
3974 max: StreamIdT,
3975 count: int,
3976 consumername: Union[ConsumerT, None] = None,
3977 idle: Optional[int] = None,
3978 ) -> ResponseT:
3979 """
3980 Returns information about pending messages, in a range.
3982 name: name of the stream.
3983 groupname: name of the consumer group.
3984 idle: available from version 6.2. filter entries by their
3985 idle-time, given in milliseconds (optional).
3986 min: minimum stream ID.
3987 max: maximum stream ID.
3988 count: number of messages to return
3989 consumername: name of a consumer to filter by (optional).
3990 """
3991 if {min, max, count} == {None}:
3992 if idle is not None or consumername is not None:
3993 raise DataError(
3994 "if XPENDING is provided with idle time"
3995 " or consumername, it must be provided"
3996 " with min, max and count parameters"
3997 )
3998 return self.xpending(name, groupname)
4000 pieces = [name, groupname]
4001 if min is None or max is None or count is None:
4002 raise DataError(
4003 "XPENDING must be provided with min, max "
4004 "and count parameters, or none of them."
4005 )
4006 # idle
4007 try:
4008 if int(idle) < 0:
4009 raise DataError("XPENDING idle must be a integer >= 0")
4010 pieces.extend(["IDLE", idle])
4011 except TypeError:
4012 pass
4013 # count
4014 try:
4015 if int(count) < 0:
4016 raise DataError("XPENDING count must be a integer >= 0")
4017 pieces.extend([min, max, count])
4018 except TypeError:
4019 pass
4020 # consumername
4021 if consumername:
4022 pieces.append(consumername)
4024 return self.execute_command("XPENDING", *pieces, parse_detail=True)
4026 def xrange(
4027 self,
4028 name: KeyT,
4029 min: StreamIdT = "-",
4030 max: StreamIdT = "+",
4031 count: Optional[int] = None,
4032 ) -> ResponseT:
4033 """
4034 Read stream values within an interval.
4036 name: name of the stream.
4038 start: first stream ID. defaults to '-',
4039 meaning the earliest available.
4041 finish: last stream ID. defaults to '+',
4042 meaning the latest available.
4044 count: if set, only return this many items, beginning with the
4045 earliest available.
4047 For more information, see https://redis.io/commands/xrange
4048 """
4049 pieces = [min, max]
4050 if count is not None:
4051 if not isinstance(count, int) or count < 1:
4052 raise DataError("XRANGE count must be a positive integer")
4053 pieces.append(b"COUNT")
4054 pieces.append(str(count))
4056 return self.execute_command("XRANGE", name, *pieces, keys=[name])
4058 def xread(
4059 self,
4060 streams: Dict[KeyT, StreamIdT],
4061 count: Optional[int] = None,
4062 block: Optional[int] = None,
4063 ) -> ResponseT:
4064 """
4065 Block and monitor multiple streams for new data.
4067 streams: a dict of stream names to stream IDs, where
4068 IDs indicate the last ID already seen.
4070 count: if set, only return this many items, beginning with the
4071 earliest available.
4073 block: number of milliseconds to wait, if nothing already present.
4075 For more information, see https://redis.io/commands/xread
4076 """
4077 pieces = []
4078 if block is not None:
4079 if not isinstance(block, int) or block < 0:
4080 raise DataError("XREAD block must be a non-negative integer")
4081 pieces.append(b"BLOCK")
4082 pieces.append(str(block))
4083 if count is not None:
4084 if not isinstance(count, int) or count < 1:
4085 raise DataError("XREAD count must be a positive integer")
4086 pieces.append(b"COUNT")
4087 pieces.append(str(count))
4088 if not isinstance(streams, dict) or len(streams) == 0:
4089 raise DataError("XREAD streams must be a non empty dict")
4090 pieces.append(b"STREAMS")
4091 keys, values = zip(*streams.items())
4092 pieces.extend(keys)
4093 pieces.extend(values)
4094 return self.execute_command("XREAD", *pieces, keys=keys)
4096 def xreadgroup(
4097 self,
4098 groupname: str,
4099 consumername: str,
4100 streams: Dict[KeyT, StreamIdT],
4101 count: Optional[int] = None,
4102 block: Optional[int] = None,
4103 noack: bool = False,
4104 claim_min_idle_time: Optional[int] = None,
4105 ) -> ResponseT:
4106 """
4107 Read from a stream via a consumer group.
4109 groupname: name of the consumer group.
4111 consumername: name of the requesting consumer.
4113 streams: a dict of stream names to stream IDs, where
4114 IDs indicate the last ID already seen.
4116 count: if set, only return this many items, beginning with the
4117 earliest available.
4119 block: number of milliseconds to wait, if nothing already present.
4120 noack: do not add messages to the PEL
4122 claim_min_idle_time: accepts an integer type and represents a
4123 time interval in milliseconds
4125 For more information, see https://redis.io/commands/xreadgroup
4126 """
4127 options = {}
4128 pieces: list[EncodableT] = [b"GROUP", groupname, consumername]
4129 if count is not None:
4130 if not isinstance(count, int) or count < 1:
4131 raise DataError("XREADGROUP count must be a positive integer")
4132 pieces.append(b"COUNT")
4133 pieces.append(str(count))
4134 if block is not None:
4135 if not isinstance(block, int) or block < 0:
4136 raise DataError("XREADGROUP block must be a non-negative integer")
4137 pieces.append(b"BLOCK")
4138 pieces.append(str(block))
4139 if noack:
4140 pieces.append(b"NOACK")
4141 if claim_min_idle_time is not None:
4142 if not isinstance(claim_min_idle_time, int) or claim_min_idle_time < 0:
4143 raise DataError(
4144 "XREADGROUP claim_min_idle_time must be a non-negative integer"
4145 )
4146 pieces.append(b"CLAIM")
4147 pieces.append(claim_min_idle_time)
4148 options["claim_min_idle_time"] = claim_min_idle_time
4149 if not isinstance(streams, dict) or len(streams) == 0:
4150 raise DataError("XREADGROUP streams must be a non empty dict")
4151 pieces.append(b"STREAMS")
4152 pieces.extend(streams.keys())
4153 pieces.extend(streams.values())
4154 return self.execute_command("XREADGROUP", *pieces, **options)
4156 def xrevrange(
4157 self,
4158 name: KeyT,
4159 max: StreamIdT = "+",
4160 min: StreamIdT = "-",
4161 count: Optional[int] = None,
4162 ) -> ResponseT:
4163 """
4164 Read stream values within an interval, in reverse order.
4166 name: name of the stream
4168 start: first stream ID. defaults to '+',
4169 meaning the latest available.
4171 finish: last stream ID. defaults to '-',
4172 meaning the earliest available.
4174 count: if set, only return this many items, beginning with the
4175 latest available.
4177 For more information, see https://redis.io/commands/xrevrange
4178 """
4179 pieces: list[EncodableT] = [max, min]
4180 if count is not None:
4181 if not isinstance(count, int) or count < 1:
4182 raise DataError("XREVRANGE count must be a positive integer")
4183 pieces.append(b"COUNT")
4184 pieces.append(str(count))
4186 return self.execute_command("XREVRANGE", name, *pieces, keys=[name])
4188 def xtrim(
4189 self,
4190 name: KeyT,
4191 maxlen: Optional[int] = None,
4192 approximate: bool = True,
4193 minid: Union[StreamIdT, None] = None,
4194 limit: Optional[int] = None,
4195 ref_policy: Optional[Literal["KEEPREF", "DELREF", "ACKED"]] = None,
4196 ) -> ResponseT:
4197 """
4198 Trims old messages from a stream.
4199 name: name of the stream.
4200 maxlen: truncate old stream messages beyond this size
4201 Can't be specified with minid.
4202 approximate: actual stream length may be slightly more than maxlen
4203 minid: the minimum id in the stream to query
4204 Can't be specified with maxlen.
4205 limit: specifies the maximum number of entries to retrieve
4206 ref_policy: optional reference policy for consumer groups:
4207 - KEEPREF (default): Trims entries but preserves references in consumer groups' PEL
4208 - DELREF: Trims entries and removes all references from consumer groups' PEL
4209 - ACKED: Only trims entries that were read and acknowledged by all consumer groups
4211 For more information, see https://redis.io/commands/xtrim
4212 """
4213 pieces: list[EncodableT] = []
4214 if maxlen is not None and minid is not None:
4215 raise DataError("Only one of ``maxlen`` or ``minid`` may be specified")
4217 if maxlen is None and minid is None:
4218 raise DataError("One of ``maxlen`` or ``minid`` must be specified")
4220 if ref_policy is not None and ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
4221 raise DataError("XTRIM ref_policy must be one of: KEEPREF, DELREF, ACKED")
4223 if maxlen is not None:
4224 pieces.append(b"MAXLEN")
4225 if minid is not None:
4226 pieces.append(b"MINID")
4227 if approximate:
4228 pieces.append(b"~")
4229 if maxlen is not None:
4230 pieces.append(maxlen)
4231 if minid is not None:
4232 pieces.append(minid)
4233 if limit is not None:
4234 pieces.append(b"LIMIT")
4235 pieces.append(limit)
4236 if ref_policy is not None:
4237 pieces.append(ref_policy)
4239 return self.execute_command("XTRIM", name, *pieces)
4242AsyncStreamCommands = StreamCommands
4245class SortedSetCommands(CommandsProtocol):
4246 """
4247 Redis commands for Sorted Sets data type.
4248 see: https://redis.io/topics/data-types-intro#redis-sorted-sets
4249 """
4251 def zadd(
4252 self,
4253 name: KeyT,
4254 mapping: Mapping[AnyKeyT, EncodableT],
4255 nx: bool = False,
4256 xx: bool = False,
4257 ch: bool = False,
4258 incr: bool = False,
4259 gt: bool = False,
4260 lt: bool = False,
4261 ) -> ResponseT:
4262 """
4263 Set any number of element-name, score pairs to the key ``name``. Pairs
4264 are specified as a dict of element-names keys to score values.
4266 ``nx`` forces ZADD to only create new elements and not to update
4267 scores for elements that already exist.
4269 ``xx`` forces ZADD to only update scores of elements that already
4270 exist. New elements will not be added.
4272 ``ch`` modifies the return value to be the numbers of elements changed.
4273 Changed elements include new elements that were added and elements
4274 whose scores changed.
4276 ``incr`` modifies ZADD to behave like ZINCRBY. In this mode only a
4277 single element/score pair can be specified and the score is the amount
4278 the existing score will be incremented by. When using this mode the
4279 return value of ZADD will be the new score of the element.
4281 ``lt`` only updates existing elements if the new score is less than
4282 the current score. This flag doesn't prevent adding new elements.
4284 ``gt`` only updates existing elements if the new score is greater than
4285 the current score. This flag doesn't prevent adding new elements.
4287 The return value of ZADD varies based on the mode specified. With no
4288 options, ZADD returns the number of new elements added to the sorted
4289 set.
4291 ``nx``, ``lt``, and ``gt`` are mutually exclusive options.
4293 See: https://redis.io/commands/ZADD
4294 """
4295 if not mapping:
4296 raise DataError("ZADD requires at least one element/score pair")
4297 if nx and xx:
4298 raise DataError("ZADD allows either 'nx' or 'xx', not both")
4299 if gt and lt:
4300 raise DataError("ZADD allows either 'gt' or 'lt', not both")
4301 if incr and len(mapping) != 1:
4302 raise DataError(
4303 "ZADD option 'incr' only works when passing a single element/score pair"
4304 )
4305 if nx and (gt or lt):
4306 raise DataError("Only one of 'nx', 'lt', or 'gr' may be defined.")
4308 pieces: list[EncodableT] = []
4309 options = {}
4310 if nx:
4311 pieces.append(b"NX")
4312 if xx:
4313 pieces.append(b"XX")
4314 if ch:
4315 pieces.append(b"CH")
4316 if incr:
4317 pieces.append(b"INCR")
4318 options["as_score"] = True
4319 if gt:
4320 pieces.append(b"GT")
4321 if lt:
4322 pieces.append(b"LT")
4323 for pair in mapping.items():
4324 pieces.append(pair[1])
4325 pieces.append(pair[0])
4326 return self.execute_command("ZADD", name, *pieces, **options)
4328 def zcard(self, name: KeyT) -> ResponseT:
4329 """
4330 Return the number of elements in the sorted set ``name``
4332 For more information, see https://redis.io/commands/zcard
4333 """
4334 return self.execute_command("ZCARD", name, keys=[name])
4336 def zcount(self, name: KeyT, min: ZScoreBoundT, max: ZScoreBoundT) -> ResponseT:
4337 """
4338 Returns the number of elements in the sorted set at key ``name`` with
4339 a score between ``min`` and ``max``.
4341 For more information, see https://redis.io/commands/zcount
4342 """
4343 return self.execute_command("ZCOUNT", name, min, max, keys=[name])
4345 def zdiff(self, keys: KeysT, withscores: bool = False) -> ResponseT:
4346 """
4347 Returns the difference between the first and all successive input
4348 sorted sets provided in ``keys``.
4350 For more information, see https://redis.io/commands/zdiff
4351 """
4352 pieces = [len(keys), *keys]
4353 if withscores:
4354 pieces.append("WITHSCORES")
4355 return self.execute_command("ZDIFF", *pieces, keys=keys)
4357 def zdiffstore(self, dest: KeyT, keys: KeysT) -> ResponseT:
4358 """
4359 Computes the difference between the first and all successive input
4360 sorted sets provided in ``keys`` and stores the result in ``dest``.
4362 For more information, see https://redis.io/commands/zdiffstore
4363 """
4364 pieces = [len(keys), *keys]
4365 return self.execute_command("ZDIFFSTORE", dest, *pieces)
4367 def zincrby(self, name: KeyT, amount: float, value: EncodableT) -> ResponseT:
4368 """
4369 Increment the score of ``value`` in sorted set ``name`` by ``amount``
4371 For more information, see https://redis.io/commands/zincrby
4372 """
4373 return self.execute_command("ZINCRBY", name, amount, value)
4375 def zinter(
4376 self, keys: KeysT, aggregate: Optional[str] = None, withscores: bool = False
4377 ) -> ResponseT:
4378 """
4379 Return the intersect of multiple sorted sets specified by ``keys``.
4380 With the ``aggregate`` option, it is possible to specify how the
4381 results of the union are aggregated. This option defaults to SUM,
4382 where the score of an element is summed across the inputs where it
4383 exists. When this option is set to either MIN or MAX, the resulting
4384 set will contain the minimum or maximum score of an element across
4385 the inputs where it exists.
4387 For more information, see https://redis.io/commands/zinter
4388 """
4389 return self._zaggregate("ZINTER", None, keys, aggregate, withscores=withscores)
4391 def zinterstore(
4392 self,
4393 dest: KeyT,
4394 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4395 aggregate: Optional[str] = None,
4396 ) -> ResponseT:
4397 """
4398 Intersect multiple sorted sets specified by ``keys`` into a new
4399 sorted set, ``dest``. Scores in the destination will be aggregated
4400 based on the ``aggregate``. This option defaults to SUM, where the
4401 score of an element is summed across the inputs where it exists.
4402 When this option is set to either MIN or MAX, the resulting set will
4403 contain the minimum or maximum score of an element across the inputs
4404 where it exists.
4406 For more information, see https://redis.io/commands/zinterstore
4407 """
4408 return self._zaggregate("ZINTERSTORE", dest, keys, aggregate)
4410 def zintercard(
4411 self, numkeys: int, keys: List[str], limit: int = 0
4412 ) -> Union[Awaitable[int], int]:
4413 """
4414 Return the cardinality of the intersect of multiple sorted sets
4415 specified by ``keys``.
4416 When LIMIT provided (defaults to 0 and means unlimited), if the intersection
4417 cardinality reaches limit partway through the computation, the algorithm will
4418 exit and yield limit as the cardinality
4420 For more information, see https://redis.io/commands/zintercard
4421 """
4422 args = [numkeys, *keys, "LIMIT", limit]
4423 return self.execute_command("ZINTERCARD", *args, keys=keys)
4425 def zlexcount(self, name, min, max):
4426 """
4427 Return the number of items in the sorted set ``name`` between the
4428 lexicographical range ``min`` and ``max``.
4430 For more information, see https://redis.io/commands/zlexcount
4431 """
4432 return self.execute_command("ZLEXCOUNT", name, min, max, keys=[name])
4434 def zpopmax(self, name: KeyT, count: Optional[int] = None) -> ResponseT:
4435 """
4436 Remove and return up to ``count`` members with the highest scores
4437 from the sorted set ``name``.
4439 For more information, see https://redis.io/commands/zpopmax
4440 """
4441 args = (count is not None) and [count] or []
4442 options = {"withscores": True}
4443 return self.execute_command("ZPOPMAX", name, *args, **options)
4445 def zpopmin(self, name: KeyT, count: Optional[int] = None) -> ResponseT:
4446 """
4447 Remove and return up to ``count`` members with the lowest scores
4448 from the sorted set ``name``.
4450 For more information, see https://redis.io/commands/zpopmin
4451 """
4452 args = (count is not None) and [count] or []
4453 options = {"withscores": True}
4454 return self.execute_command("ZPOPMIN", name, *args, **options)
4456 def zrandmember(
4457 self, key: KeyT, count: Optional[int] = None, withscores: bool = False
4458 ) -> ResponseT:
4459 """
4460 Return a random element from the sorted set value stored at key.
4462 ``count`` if the argument is positive, return an array of distinct
4463 fields. If called with a negative count, the behavior changes and
4464 the command is allowed to return the same field multiple times.
4465 In this case, the number of returned fields is the absolute value
4466 of the specified count.
4468 ``withscores`` The optional WITHSCORES modifier changes the reply so it
4469 includes the respective scores of the randomly selected elements from
4470 the sorted set.
4472 For more information, see https://redis.io/commands/zrandmember
4473 """
4474 params = []
4475 if count is not None:
4476 params.append(count)
4477 if withscores:
4478 params.append("WITHSCORES")
4480 return self.execute_command("ZRANDMEMBER", key, *params)
4482 def bzpopmax(self, keys: KeysT, timeout: TimeoutSecT = 0) -> ResponseT:
4483 """
4484 ZPOPMAX a value off of the first non-empty sorted set
4485 named in the ``keys`` list.
4487 If none of the sorted sets in ``keys`` has a value to ZPOPMAX,
4488 then block for ``timeout`` seconds, or until a member gets added
4489 to one of the sorted sets.
4491 If timeout is 0, then block indefinitely.
4493 For more information, see https://redis.io/commands/bzpopmax
4494 """
4495 if timeout is None:
4496 timeout = 0
4497 keys = list_or_args(keys, None)
4498 keys.append(timeout)
4499 return self.execute_command("BZPOPMAX", *keys)
4501 def bzpopmin(self, keys: KeysT, timeout: TimeoutSecT = 0) -> ResponseT:
4502 """
4503 ZPOPMIN a value off of the first non-empty sorted set
4504 named in the ``keys`` list.
4506 If none of the sorted sets in ``keys`` has a value to ZPOPMIN,
4507 then block for ``timeout`` seconds, or until a member gets added
4508 to one of the sorted sets.
4510 If timeout is 0, then block indefinitely.
4512 For more information, see https://redis.io/commands/bzpopmin
4513 """
4514 if timeout is None:
4515 timeout = 0
4516 keys: list[EncodableT] = list_or_args(keys, None)
4517 keys.append(timeout)
4518 return self.execute_command("BZPOPMIN", *keys)
4520 def zmpop(
4521 self,
4522 num_keys: int,
4523 keys: List[str],
4524 min: Optional[bool] = False,
4525 max: Optional[bool] = False,
4526 count: Optional[int] = 1,
4527 ) -> Union[Awaitable[list], list]:
4528 """
4529 Pop ``count`` values (default 1) off of the first non-empty sorted set
4530 named in the ``keys`` list.
4531 For more information, see https://redis.io/commands/zmpop
4532 """
4533 args = [num_keys] + keys
4534 if (min and max) or (not min and not max):
4535 raise DataError
4536 elif min:
4537 args.append("MIN")
4538 else:
4539 args.append("MAX")
4540 if count != 1:
4541 args.extend(["COUNT", count])
4543 return self.execute_command("ZMPOP", *args)
4545 def bzmpop(
4546 self,
4547 timeout: float,
4548 numkeys: int,
4549 keys: List[str],
4550 min: Optional[bool] = False,
4551 max: Optional[bool] = False,
4552 count: Optional[int] = 1,
4553 ) -> Optional[list]:
4554 """
4555 Pop ``count`` values (default 1) off of the first non-empty sorted set
4556 named in the ``keys`` list.
4558 If none of the sorted sets in ``keys`` has a value to pop,
4559 then block for ``timeout`` seconds, or until a member gets added
4560 to one of the sorted sets.
4562 If timeout is 0, then block indefinitely.
4564 For more information, see https://redis.io/commands/bzmpop
4565 """
4566 args = [timeout, numkeys, *keys]
4567 if (min and max) or (not min and not max):
4568 raise DataError("Either min or max, but not both must be set")
4569 elif min:
4570 args.append("MIN")
4571 else:
4572 args.append("MAX")
4573 args.extend(["COUNT", count])
4575 return self.execute_command("BZMPOP", *args)
4577 def _zrange(
4578 self,
4579 command,
4580 dest: Union[KeyT, None],
4581 name: KeyT,
4582 start: int,
4583 end: int,
4584 desc: bool = False,
4585 byscore: bool = False,
4586 bylex: bool = False,
4587 withscores: bool = False,
4588 score_cast_func: Union[type, Callable, None] = float,
4589 offset: Optional[int] = None,
4590 num: Optional[int] = None,
4591 ) -> ResponseT:
4592 if byscore and bylex:
4593 raise DataError("``byscore`` and ``bylex`` can not be specified together.")
4594 if (offset is not None and num is None) or (num is not None and offset is None):
4595 raise DataError("``offset`` and ``num`` must both be specified.")
4596 if bylex and withscores:
4597 raise DataError(
4598 "``withscores`` not supported in combination with ``bylex``."
4599 )
4600 pieces = [command]
4601 if dest:
4602 pieces.append(dest)
4603 pieces.extend([name, start, end])
4604 if byscore:
4605 pieces.append("BYSCORE")
4606 if bylex:
4607 pieces.append("BYLEX")
4608 if desc:
4609 pieces.append("REV")
4610 if offset is not None and num is not None:
4611 pieces.extend(["LIMIT", offset, num])
4612 if withscores:
4613 pieces.append("WITHSCORES")
4614 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4615 options["keys"] = [name]
4616 return self.execute_command(*pieces, **options)
4618 def zrange(
4619 self,
4620 name: KeyT,
4621 start: int,
4622 end: int,
4623 desc: bool = False,
4624 withscores: bool = False,
4625 score_cast_func: Union[type, Callable] = float,
4626 byscore: bool = False,
4627 bylex: bool = False,
4628 offset: Optional[int] = None,
4629 num: Optional[int] = None,
4630 ) -> ResponseT:
4631 """
4632 Return a range of values from sorted set ``name`` between
4633 ``start`` and ``end`` sorted in ascending order.
4635 ``start`` and ``end`` can be negative, indicating the end of the range.
4637 ``desc`` a boolean indicating whether to sort the results in reversed
4638 order.
4640 ``withscores`` indicates to return the scores along with the values.
4641 The return type is a list of (value, score) pairs.
4643 ``score_cast_func`` a callable used to cast the score return value.
4645 ``byscore`` when set to True, returns the range of elements from the
4646 sorted set having scores equal or between ``start`` and ``end``.
4648 ``bylex`` when set to True, returns the range of elements from the
4649 sorted set between the ``start`` and ``end`` lexicographical closed
4650 range intervals.
4651 Valid ``start`` and ``end`` must start with ( or [, in order to specify
4652 whether the range interval is exclusive or inclusive, respectively.
4654 ``offset`` and ``num`` are specified, then return a slice of the range.
4655 Can't be provided when using ``bylex``.
4657 For more information, see https://redis.io/commands/zrange
4658 """
4659 # Need to support ``desc`` also when using old redis version
4660 # because it was supported in 3.5.3 (of redis-py)
4661 if not byscore and not bylex and (offset is None and num is None) and desc:
4662 return self.zrevrange(name, start, end, withscores, score_cast_func)
4664 return self._zrange(
4665 "ZRANGE",
4666 None,
4667 name,
4668 start,
4669 end,
4670 desc,
4671 byscore,
4672 bylex,
4673 withscores,
4674 score_cast_func,
4675 offset,
4676 num,
4677 )
4679 def zrevrange(
4680 self,
4681 name: KeyT,
4682 start: int,
4683 end: int,
4684 withscores: bool = False,
4685 score_cast_func: Union[type, Callable] = float,
4686 ) -> ResponseT:
4687 """
4688 Return a range of values from sorted set ``name`` between
4689 ``start`` and ``end`` sorted in descending order.
4691 ``start`` and ``end`` can be negative, indicating the end of the range.
4693 ``withscores`` indicates to return the scores along with the values
4694 The return type is a list of (value, score) pairs
4696 ``score_cast_func`` a callable used to cast the score return value
4698 For more information, see https://redis.io/commands/zrevrange
4699 """
4700 pieces = ["ZREVRANGE", name, start, end]
4701 if withscores:
4702 pieces.append(b"WITHSCORES")
4703 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4704 options["keys"] = name
4705 return self.execute_command(*pieces, **options)
4707 def zrangestore(
4708 self,
4709 dest: KeyT,
4710 name: KeyT,
4711 start: int,
4712 end: int,
4713 byscore: bool = False,
4714 bylex: bool = False,
4715 desc: bool = False,
4716 offset: Optional[int] = None,
4717 num: Optional[int] = None,
4718 ) -> ResponseT:
4719 """
4720 Stores in ``dest`` the result of a range of values from sorted set
4721 ``name`` between ``start`` and ``end`` sorted in ascending order.
4723 ``start`` and ``end`` can be negative, indicating the end of the range.
4725 ``byscore`` when set to True, returns the range of elements from the
4726 sorted set having scores equal or between ``start`` and ``end``.
4728 ``bylex`` when set to True, returns the range of elements from the
4729 sorted set between the ``start`` and ``end`` lexicographical closed
4730 range intervals.
4731 Valid ``start`` and ``end`` must start with ( or [, in order to specify
4732 whether the range interval is exclusive or inclusive, respectively.
4734 ``desc`` a boolean indicating whether to sort the results in reversed
4735 order.
4737 ``offset`` and ``num`` are specified, then return a slice of the range.
4738 Can't be provided when using ``bylex``.
4740 For more information, see https://redis.io/commands/zrangestore
4741 """
4742 return self._zrange(
4743 "ZRANGESTORE",
4744 dest,
4745 name,
4746 start,
4747 end,
4748 desc,
4749 byscore,
4750 bylex,
4751 False,
4752 None,
4753 offset,
4754 num,
4755 )
4757 def zrangebylex(
4758 self,
4759 name: KeyT,
4760 min: EncodableT,
4761 max: EncodableT,
4762 start: Optional[int] = None,
4763 num: Optional[int] = None,
4764 ) -> ResponseT:
4765 """
4766 Return the lexicographical range of values from sorted set ``name``
4767 between ``min`` and ``max``.
4769 If ``start`` and ``num`` are specified, then return a slice of the
4770 range.
4772 For more information, see https://redis.io/commands/zrangebylex
4773 """
4774 if (start is not None and num is None) or (num is not None and start is None):
4775 raise DataError("``start`` and ``num`` must both be specified")
4776 pieces = ["ZRANGEBYLEX", name, min, max]
4777 if start is not None and num is not None:
4778 pieces.extend([b"LIMIT", start, num])
4779 return self.execute_command(*pieces, keys=[name])
4781 def zrevrangebylex(
4782 self,
4783 name: KeyT,
4784 max: EncodableT,
4785 min: EncodableT,
4786 start: Optional[int] = None,
4787 num: Optional[int] = None,
4788 ) -> ResponseT:
4789 """
4790 Return the reversed lexicographical range of values from sorted set
4791 ``name`` between ``max`` and ``min``.
4793 If ``start`` and ``num`` are specified, then return a slice of the
4794 range.
4796 For more information, see https://redis.io/commands/zrevrangebylex
4797 """
4798 if (start is not None and num is None) or (num is not None and start is None):
4799 raise DataError("``start`` and ``num`` must both be specified")
4800 pieces = ["ZREVRANGEBYLEX", name, max, min]
4801 if start is not None and num is not None:
4802 pieces.extend(["LIMIT", start, num])
4803 return self.execute_command(*pieces, keys=[name])
4805 def zrangebyscore(
4806 self,
4807 name: KeyT,
4808 min: ZScoreBoundT,
4809 max: ZScoreBoundT,
4810 start: Optional[int] = None,
4811 num: Optional[int] = None,
4812 withscores: bool = False,
4813 score_cast_func: Union[type, Callable] = float,
4814 ) -> ResponseT:
4815 """
4816 Return a range of values from the sorted set ``name`` with scores
4817 between ``min`` and ``max``.
4819 If ``start`` and ``num`` are specified, then return a slice
4820 of the range.
4822 ``withscores`` indicates to return the scores along with the values.
4823 The return type is a list of (value, score) pairs
4825 `score_cast_func`` a callable used to cast the score return value
4827 For more information, see https://redis.io/commands/zrangebyscore
4828 """
4829 if (start is not None and num is None) or (num is not None and start is None):
4830 raise DataError("``start`` and ``num`` must both be specified")
4831 pieces = ["ZRANGEBYSCORE", name, min, max]
4832 if start is not None and num is not None:
4833 pieces.extend(["LIMIT", start, num])
4834 if withscores:
4835 pieces.append("WITHSCORES")
4836 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4837 options["keys"] = [name]
4838 return self.execute_command(*pieces, **options)
4840 def zrevrangebyscore(
4841 self,
4842 name: KeyT,
4843 max: ZScoreBoundT,
4844 min: ZScoreBoundT,
4845 start: Optional[int] = None,
4846 num: Optional[int] = None,
4847 withscores: bool = False,
4848 score_cast_func: Union[type, Callable] = float,
4849 ):
4850 """
4851 Return a range of values from the sorted set ``name`` with scores
4852 between ``min`` and ``max`` in descending order.
4854 If ``start`` and ``num`` are specified, then return a slice
4855 of the range.
4857 ``withscores`` indicates to return the scores along with the values.
4858 The return type is a list of (value, score) pairs
4860 ``score_cast_func`` a callable used to cast the score return value
4862 For more information, see https://redis.io/commands/zrevrangebyscore
4863 """
4864 if (start is not None and num is None) or (num is not None and start is None):
4865 raise DataError("``start`` and ``num`` must both be specified")
4866 pieces = ["ZREVRANGEBYSCORE", name, max, min]
4867 if start is not None and num is not None:
4868 pieces.extend(["LIMIT", start, num])
4869 if withscores:
4870 pieces.append("WITHSCORES")
4871 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4872 options["keys"] = [name]
4873 return self.execute_command(*pieces, **options)
4875 def zrank(
4876 self,
4877 name: KeyT,
4878 value: EncodableT,
4879 withscore: bool = False,
4880 score_cast_func: Union[type, Callable] = float,
4881 ) -> ResponseT:
4882 """
4883 Returns a 0-based value indicating the rank of ``value`` in sorted set
4884 ``name``.
4885 The optional WITHSCORE argument supplements the command's
4886 reply with the score of the element returned.
4888 ``score_cast_func`` a callable used to cast the score return value
4890 For more information, see https://redis.io/commands/zrank
4891 """
4892 pieces = ["ZRANK", name, value]
4893 if withscore:
4894 pieces.append("WITHSCORE")
4896 options = {"withscore": withscore, "score_cast_func": score_cast_func}
4898 return self.execute_command(*pieces, **options)
4900 def zrem(self, name: KeyT, *values: FieldT) -> ResponseT:
4901 """
4902 Remove member ``values`` from sorted set ``name``
4904 For more information, see https://redis.io/commands/zrem
4905 """
4906 return self.execute_command("ZREM", name, *values)
4908 def zremrangebylex(self, name: KeyT, min: EncodableT, max: EncodableT) -> ResponseT:
4909 """
4910 Remove all elements in the sorted set ``name`` between the
4911 lexicographical range specified by ``min`` and ``max``.
4913 Returns the number of elements removed.
4915 For more information, see https://redis.io/commands/zremrangebylex
4916 """
4917 return self.execute_command("ZREMRANGEBYLEX", name, min, max)
4919 def zremrangebyrank(self, name: KeyT, min: int, max: int) -> ResponseT:
4920 """
4921 Remove all elements in the sorted set ``name`` with ranks between
4922 ``min`` and ``max``. Values are 0-based, ordered from smallest score
4923 to largest. Values can be negative indicating the highest scores.
4924 Returns the number of elements removed
4926 For more information, see https://redis.io/commands/zremrangebyrank
4927 """
4928 return self.execute_command("ZREMRANGEBYRANK", name, min, max)
4930 def zremrangebyscore(
4931 self, name: KeyT, min: ZScoreBoundT, max: ZScoreBoundT
4932 ) -> ResponseT:
4933 """
4934 Remove all elements in the sorted set ``name`` with scores
4935 between ``min`` and ``max``. Returns the number of elements removed.
4937 For more information, see https://redis.io/commands/zremrangebyscore
4938 """
4939 return self.execute_command("ZREMRANGEBYSCORE", name, min, max)
4941 def zrevrank(
4942 self,
4943 name: KeyT,
4944 value: EncodableT,
4945 withscore: bool = False,
4946 score_cast_func: Union[type, Callable] = float,
4947 ) -> ResponseT:
4948 """
4949 Returns a 0-based value indicating the descending rank of
4950 ``value`` in sorted set ``name``.
4951 The optional ``withscore`` argument supplements the command's
4952 reply with the score of the element returned.
4954 ``score_cast_func`` a callable used to cast the score return value
4956 For more information, see https://redis.io/commands/zrevrank
4957 """
4958 pieces = ["ZREVRANK", name, value]
4959 if withscore:
4960 pieces.append("WITHSCORE")
4962 options = {"withscore": withscore, "score_cast_func": score_cast_func}
4964 return self.execute_command(*pieces, **options)
4966 def zscore(self, name: KeyT, value: EncodableT) -> ResponseT:
4967 """
4968 Return the score of element ``value`` in sorted set ``name``
4970 For more information, see https://redis.io/commands/zscore
4971 """
4972 return self.execute_command("ZSCORE", name, value, keys=[name])
4974 def zunion(
4975 self,
4976 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4977 aggregate: Optional[str] = None,
4978 withscores: bool = False,
4979 score_cast_func: Union[type, Callable] = float,
4980 ) -> ResponseT:
4981 """
4982 Return the union of multiple sorted sets specified by ``keys``.
4983 ``keys`` can be provided as dictionary of keys and their weights.
4984 Scores will be aggregated based on the ``aggregate``, or SUM if
4985 none is provided.
4987 ``score_cast_func`` a callable used to cast the score return value
4989 For more information, see https://redis.io/commands/zunion
4990 """
4991 return self._zaggregate(
4992 "ZUNION",
4993 None,
4994 keys,
4995 aggregate,
4996 withscores=withscores,
4997 score_cast_func=score_cast_func,
4998 )
5000 def zunionstore(
5001 self,
5002 dest: KeyT,
5003 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
5004 aggregate: Optional[str] = None,
5005 ) -> ResponseT:
5006 """
5007 Union multiple sorted sets specified by ``keys`` into
5008 a new sorted set, ``dest``. Scores in the destination will be
5009 aggregated based on the ``aggregate``, or SUM if none is provided.
5011 For more information, see https://redis.io/commands/zunionstore
5012 """
5013 return self._zaggregate("ZUNIONSTORE", dest, keys, aggregate)
5015 def zmscore(self, key: KeyT, members: List[str]) -> ResponseT:
5016 """
5017 Returns the scores associated with the specified members
5018 in the sorted set stored at key.
5019 ``members`` should be a list of the member name.
5020 Return type is a list of score.
5021 If the member does not exist, a None will be returned
5022 in corresponding position.
5024 For more information, see https://redis.io/commands/zmscore
5025 """
5026 if not members:
5027 raise DataError("ZMSCORE members must be a non-empty list")
5028 pieces = [key] + members
5029 return self.execute_command("ZMSCORE", *pieces, keys=[key])
5031 def _zaggregate(
5032 self,
5033 command: str,
5034 dest: Union[KeyT, None],
5035 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
5036 aggregate: Optional[str] = None,
5037 **options,
5038 ) -> ResponseT:
5039 pieces: list[EncodableT] = [command]
5040 if dest is not None:
5041 pieces.append(dest)
5042 pieces.append(len(keys))
5043 if isinstance(keys, dict):
5044 keys, weights = keys.keys(), keys.values()
5045 else:
5046 weights = None
5047 pieces.extend(keys)
5048 if weights:
5049 pieces.append(b"WEIGHTS")
5050 pieces.extend(weights)
5051 if aggregate:
5052 if aggregate.upper() in ["SUM", "MIN", "MAX"]:
5053 pieces.append(b"AGGREGATE")
5054 pieces.append(aggregate)
5055 else:
5056 raise DataError("aggregate can be sum, min or max.")
5057 if options.get("withscores", False):
5058 pieces.append(b"WITHSCORES")
5059 options["keys"] = keys
5060 return self.execute_command(*pieces, **options)
5063AsyncSortedSetCommands = SortedSetCommands
5066class HyperlogCommands(CommandsProtocol):
5067 """
5068 Redis commands of HyperLogLogs data type.
5069 see: https://redis.io/topics/data-types-intro#hyperloglogs
5070 """
5072 def pfadd(self, name: KeyT, *values: FieldT) -> ResponseT:
5073 """
5074 Adds the specified elements to the specified HyperLogLog.
5076 For more information, see https://redis.io/commands/pfadd
5077 """
5078 return self.execute_command("PFADD", name, *values)
5080 def pfcount(self, *sources: KeyT) -> ResponseT:
5081 """
5082 Return the approximated cardinality of
5083 the set observed by the HyperLogLog at key(s).
5085 For more information, see https://redis.io/commands/pfcount
5086 """
5087 return self.execute_command("PFCOUNT", *sources)
5089 def pfmerge(self, dest: KeyT, *sources: KeyT) -> ResponseT:
5090 """
5091 Merge N different HyperLogLogs into a single one.
5093 For more information, see https://redis.io/commands/pfmerge
5094 """
5095 return self.execute_command("PFMERGE", dest, *sources)
5098AsyncHyperlogCommands = HyperlogCommands
5101class HashDataPersistOptions(Enum):
5102 # set the value for each provided key to each
5103 # provided value only if all do not already exist.
5104 FNX = "FNX"
5106 # set the value for each provided key to each
5107 # provided value only if all already exist.
5108 FXX = "FXX"
5111class HashCommands(CommandsProtocol):
5112 """
5113 Redis commands for Hash data type.
5114 see: https://redis.io/topics/data-types-intro#redis-hashes
5115 """
5117 def hdel(self, name: str, *keys: str) -> Union[Awaitable[int], int]:
5118 """
5119 Delete ``keys`` from hash ``name``
5121 For more information, see https://redis.io/commands/hdel
5122 """
5123 return self.execute_command("HDEL", name, *keys)
5125 def hexists(self, name: str, key: str) -> Union[Awaitable[bool], bool]:
5126 """
5127 Returns a boolean indicating if ``key`` exists within hash ``name``
5129 For more information, see https://redis.io/commands/hexists
5130 """
5131 return self.execute_command("HEXISTS", name, key, keys=[name])
5133 def hget(
5134 self, name: str, key: str
5135 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
5136 """
5137 Return the value of ``key`` within the hash ``name``
5139 For more information, see https://redis.io/commands/hget
5140 """
5141 return self.execute_command("HGET", name, key, keys=[name])
5143 def hgetall(self, name: str) -> Union[Awaitable[dict], dict]:
5144 """
5145 Return a Python dict of the hash's name/value pairs
5147 For more information, see https://redis.io/commands/hgetall
5148 """
5149 return self.execute_command("HGETALL", name, keys=[name])
5151 def hgetdel(
5152 self, name: str, *keys: str
5153 ) -> Union[
5154 Awaitable[Optional[List[Union[str, bytes]]]], Optional[List[Union[str, bytes]]]
5155 ]:
5156 """
5157 Return the value of ``key`` within the hash ``name`` and
5158 delete the field in the hash.
5159 This command is similar to HGET, except for the fact that it also deletes
5160 the key on success from the hash with the provided ```name```.
5162 Available since Redis 8.0
5163 For more information, see https://redis.io/commands/hgetdel
5164 """
5165 if len(keys) == 0:
5166 raise DataError("'hgetdel' should have at least one key provided")
5168 return self.execute_command("HGETDEL", name, "FIELDS", len(keys), *keys)
5170 def hgetex(
5171 self,
5172 name: KeyT,
5173 *keys: str,
5174 ex: Optional[ExpiryT] = None,
5175 px: Optional[ExpiryT] = None,
5176 exat: Optional[AbsExpiryT] = None,
5177 pxat: Optional[AbsExpiryT] = None,
5178 persist: bool = False,
5179 ) -> Union[
5180 Awaitable[Optional[List[Union[str, bytes]]]], Optional[List[Union[str, bytes]]]
5181 ]:
5182 """
5183 Return the values of ``key`` and ``keys`` within the hash ``name``
5184 and optionally set their expiration.
5186 ``ex`` sets an expire flag on ``kyes`` for ``ex`` seconds.
5188 ``px`` sets an expire flag on ``keys`` for ``px`` milliseconds.
5190 ``exat`` sets an expire flag on ``keys`` for ``ex`` seconds,
5191 specified in unix time.
5193 ``pxat`` sets an expire flag on ``keys`` for ``ex`` milliseconds,
5194 specified in unix time.
5196 ``persist`` remove the time to live associated with the ``keys``.
5198 Available since Redis 8.0
5199 For more information, see https://redis.io/commands/hgetex
5200 """
5201 if not keys:
5202 raise DataError("'hgetex' should have at least one key provided")
5204 opset = {ex, px, exat, pxat}
5205 if len(opset) > 2 or len(opset) > 1 and persist:
5206 raise DataError(
5207 "``ex``, ``px``, ``exat``, ``pxat``, "
5208 "and ``persist`` are mutually exclusive."
5209 )
5211 exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
5213 if persist:
5214 exp_options.append("PERSIST")
5216 return self.execute_command(
5217 "HGETEX",
5218 name,
5219 *exp_options,
5220 "FIELDS",
5221 len(keys),
5222 *keys,
5223 )
5225 def hincrby(
5226 self, name: str, key: str, amount: int = 1
5227 ) -> Union[Awaitable[int], int]:
5228 """
5229 Increment the value of ``key`` in hash ``name`` by ``amount``
5231 For more information, see https://redis.io/commands/hincrby
5232 """
5233 return self.execute_command("HINCRBY", name, key, amount)
5235 def hincrbyfloat(
5236 self, name: str, key: str, amount: float = 1.0
5237 ) -> Union[Awaitable[float], float]:
5238 """
5239 Increment the value of ``key`` in hash ``name`` by floating ``amount``
5241 For more information, see https://redis.io/commands/hincrbyfloat
5242 """
5243 return self.execute_command("HINCRBYFLOAT", name, key, amount)
5245 def hkeys(self, name: str) -> Union[Awaitable[List], List]:
5246 """
5247 Return the list of keys within hash ``name``
5249 For more information, see https://redis.io/commands/hkeys
5250 """
5251 return self.execute_command("HKEYS", name, keys=[name])
5253 def hlen(self, name: str) -> Union[Awaitable[int], int]:
5254 """
5255 Return the number of elements in hash ``name``
5257 For more information, see https://redis.io/commands/hlen
5258 """
5259 return self.execute_command("HLEN", name, keys=[name])
5261 def hset(
5262 self,
5263 name: str,
5264 key: Optional[str] = None,
5265 value: Optional[str] = None,
5266 mapping: Optional[dict] = None,
5267 items: Optional[list] = None,
5268 ) -> Union[Awaitable[int], int]:
5269 """
5270 Set ``key`` to ``value`` within hash ``name``,
5271 ``mapping`` accepts a dict of key/value pairs that will be
5272 added to hash ``name``.
5273 ``items`` accepts a list of key/value pairs that will be
5274 added to hash ``name``.
5275 Returns the number of fields that were added.
5277 For more information, see https://redis.io/commands/hset
5278 """
5280 if key is None and not mapping and not items:
5281 raise DataError("'hset' with no key value pairs")
5283 pieces = []
5284 if items:
5285 pieces.extend(items)
5286 if key is not None:
5287 pieces.extend((key, value))
5288 if mapping:
5289 for pair in mapping.items():
5290 pieces.extend(pair)
5292 return self.execute_command("HSET", name, *pieces)
5294 def hsetex(
5295 self,
5296 name: str,
5297 key: Optional[str] = None,
5298 value: Optional[str] = None,
5299 mapping: Optional[dict] = None,
5300 items: Optional[list] = None,
5301 ex: Optional[ExpiryT] = None,
5302 px: Optional[ExpiryT] = None,
5303 exat: Optional[AbsExpiryT] = None,
5304 pxat: Optional[AbsExpiryT] = None,
5305 data_persist_option: Optional[HashDataPersistOptions] = None,
5306 keepttl: bool = False,
5307 ) -> Union[Awaitable[int], int]:
5308 """
5309 Set ``key`` to ``value`` within hash ``name``
5311 ``mapping`` accepts a dict of key/value pairs that will be
5312 added to hash ``name``.
5314 ``items`` accepts a list of key/value pairs that will be
5315 added to hash ``name``.
5317 ``ex`` sets an expire flag on ``keys`` for ``ex`` seconds.
5319 ``px`` sets an expire flag on ``keys`` for ``px`` milliseconds.
5321 ``exat`` sets an expire flag on ``keys`` for ``ex`` seconds,
5322 specified in unix time.
5324 ``pxat`` sets an expire flag on ``keys`` for ``ex`` milliseconds,
5325 specified in unix time.
5327 ``data_persist_option`` can be set to ``FNX`` or ``FXX`` to control the
5328 behavior of the command.
5329 ``FNX`` will set the value for each provided key to each
5330 provided value only if all do not already exist.
5331 ``FXX`` will set the value for each provided key to each
5332 provided value only if all already exist.
5334 ``keepttl`` if True, retain the time to live associated with the keys.
5336 Returns the number of fields that were added.
5338 Available since Redis 8.0
5339 For more information, see https://redis.io/commands/hsetex
5340 """
5341 if key is None and not mapping and not items:
5342 raise DataError("'hsetex' with no key value pairs")
5344 if items and len(items) % 2 != 0:
5345 raise DataError(
5346 "'hsetex' with odd number of items. "
5347 "'items' must contain a list of key/value pairs."
5348 )
5350 opset = {ex, px, exat, pxat}
5351 if len(opset) > 2 or len(opset) > 1 and keepttl:
5352 raise DataError(
5353 "``ex``, ``px``, ``exat``, ``pxat``, "
5354 "and ``keepttl`` are mutually exclusive."
5355 )
5357 exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
5358 if data_persist_option:
5359 exp_options.append(data_persist_option.value)
5361 if keepttl:
5362 exp_options.append("KEEPTTL")
5364 pieces = []
5365 if items:
5366 pieces.extend(items)
5367 if key is not None:
5368 pieces.extend((key, value))
5369 if mapping:
5370 for pair in mapping.items():
5371 pieces.extend(pair)
5373 return self.execute_command(
5374 "HSETEX", name, *exp_options, "FIELDS", int(len(pieces) / 2), *pieces
5375 )
5377 def hsetnx(self, name: str, key: str, value: str) -> Union[Awaitable[bool], bool]:
5378 """
5379 Set ``key`` to ``value`` within hash ``name`` if ``key`` does not
5380 exist. Returns 1 if HSETNX created a field, otherwise 0.
5382 For more information, see https://redis.io/commands/hsetnx
5383 """
5384 return self.execute_command("HSETNX", name, key, value)
5386 @deprecated_function(
5387 version="4.0.0",
5388 reason="Use 'hset' instead.",
5389 name="hmset",
5390 )
5391 def hmset(self, name: str, mapping: dict) -> Union[Awaitable[str], str]:
5392 """
5393 Set key to value within hash ``name`` for each corresponding
5394 key and value from the ``mapping`` dict.
5396 For more information, see https://redis.io/commands/hmset
5397 """
5398 if not mapping:
5399 raise DataError("'hmset' with 'mapping' of length 0")
5400 items = []
5401 for pair in mapping.items():
5402 items.extend(pair)
5403 return self.execute_command("HMSET", name, *items)
5405 def hmget(self, name: str, keys: List, *args: List) -> Union[Awaitable[List], List]:
5406 """
5407 Returns a list of values ordered identically to ``keys``
5409 For more information, see https://redis.io/commands/hmget
5410 """
5411 args = list_or_args(keys, args)
5412 return self.execute_command("HMGET", name, *args, keys=[name])
5414 def hvals(self, name: str) -> Union[Awaitable[List], List]:
5415 """
5416 Return the list of values within hash ``name``
5418 For more information, see https://redis.io/commands/hvals
5419 """
5420 return self.execute_command("HVALS", name, keys=[name])
5422 def hstrlen(self, name: str, key: str) -> Union[Awaitable[int], int]:
5423 """
5424 Return the number of bytes stored in the value of ``key``
5425 within hash ``name``
5427 For more information, see https://redis.io/commands/hstrlen
5428 """
5429 return self.execute_command("HSTRLEN", name, key, keys=[name])
5431 def hexpire(
5432 self,
5433 name: KeyT,
5434 seconds: ExpiryT,
5435 *fields: str,
5436 nx: bool = False,
5437 xx: bool = False,
5438 gt: bool = False,
5439 lt: bool = False,
5440 ) -> ResponseT:
5441 """
5442 Sets or updates the expiration time for fields within a hash key, using relative
5443 time in seconds.
5445 If a field already has an expiration time, the behavior of the update can be
5446 controlled using the `nx`, `xx`, `gt`, and `lt` parameters.
5448 The return value provides detailed information about the outcome for each field.
5450 For more information, see https://redis.io/commands/hexpire
5452 Args:
5453 name: The name of the hash key.
5454 seconds: Expiration time in seconds, relative. Can be an integer, or a
5455 Python `timedelta` object.
5456 fields: List of fields within the hash to apply the expiration time to.
5457 nx: Set expiry only when the field has no expiry.
5458 xx: Set expiry only when the field has an existing expiry.
5459 gt: Set expiry only when the new expiry is greater than the current one.
5460 lt: Set expiry only when the new expiry is less than the current one.
5462 Returns:
5463 Returns a list which contains for each field in the request:
5464 - `-2` if the field does not exist, or if the key does not exist.
5465 - `0` if the specified NX | XX | GT | LT condition was not met.
5466 - `1` if the expiration time was set or updated.
5467 - `2` if the field was deleted because the specified expiration time is
5468 in the past.
5469 """
5470 conditions = [nx, xx, gt, lt]
5471 if sum(conditions) > 1:
5472 raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.")
5474 if isinstance(seconds, datetime.timedelta):
5475 seconds = int(seconds.total_seconds())
5477 options = []
5478 if nx:
5479 options.append("NX")
5480 if xx:
5481 options.append("XX")
5482 if gt:
5483 options.append("GT")
5484 if lt:
5485 options.append("LT")
5487 return self.execute_command(
5488 "HEXPIRE", name, seconds, *options, "FIELDS", len(fields), *fields
5489 )
5491 def hpexpire(
5492 self,
5493 name: KeyT,
5494 milliseconds: ExpiryT,
5495 *fields: str,
5496 nx: bool = False,
5497 xx: bool = False,
5498 gt: bool = False,
5499 lt: bool = False,
5500 ) -> ResponseT:
5501 """
5502 Sets or updates the expiration time for fields within a hash key, using relative
5503 time in milliseconds.
5505 If a field already has an expiration time, the behavior of the update can be
5506 controlled using the `nx`, `xx`, `gt`, and `lt` parameters.
5508 The return value provides detailed information about the outcome for each field.
5510 For more information, see https://redis.io/commands/hpexpire
5512 Args:
5513 name: The name of the hash key.
5514 milliseconds: Expiration time in milliseconds, relative. Can be an integer,
5515 or a Python `timedelta` object.
5516 fields: List of fields within the hash to apply the expiration time to.
5517 nx: Set expiry only when the field has no expiry.
5518 xx: Set expiry only when the field has an existing expiry.
5519 gt: Set expiry only when the new expiry is greater than the current one.
5520 lt: Set expiry only when the new expiry is less than the current one.
5522 Returns:
5523 Returns a list which contains for each field in the request:
5524 - `-2` if the field does not exist, or if the key does not exist.
5525 - `0` if the specified NX | XX | GT | LT condition was not met.
5526 - `1` if the expiration time was set or updated.
5527 - `2` if the field was deleted because the specified expiration time is
5528 in the past.
5529 """
5530 conditions = [nx, xx, gt, lt]
5531 if sum(conditions) > 1:
5532 raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.")
5534 if isinstance(milliseconds, datetime.timedelta):
5535 milliseconds = int(milliseconds.total_seconds() * 1000)
5537 options = []
5538 if nx:
5539 options.append("NX")
5540 if xx:
5541 options.append("XX")
5542 if gt:
5543 options.append("GT")
5544 if lt:
5545 options.append("LT")
5547 return self.execute_command(
5548 "HPEXPIRE", name, milliseconds, *options, "FIELDS", len(fields), *fields
5549 )
5551 def hexpireat(
5552 self,
5553 name: KeyT,
5554 unix_time_seconds: AbsExpiryT,
5555 *fields: str,
5556 nx: bool = False,
5557 xx: bool = False,
5558 gt: bool = False,
5559 lt: bool = False,
5560 ) -> ResponseT:
5561 """
5562 Sets or updates the expiration time for fields within a hash key, using an
5563 absolute Unix timestamp in seconds.
5565 If a field already has an expiration time, the behavior of the update can be
5566 controlled using the `nx`, `xx`, `gt`, and `lt` parameters.
5568 The return value provides detailed information about the outcome for each field.
5570 For more information, see https://redis.io/commands/hexpireat
5572 Args:
5573 name: The name of the hash key.
5574 unix_time_seconds: Expiration time as Unix timestamp in seconds. Can be an
5575 integer or a Python `datetime` object.
5576 fields: List of fields within the hash to apply the expiration time to.
5577 nx: Set expiry only when the field has no expiry.
5578 xx: Set expiry only when the field has an existing expiration time.
5579 gt: Set expiry only when the new expiry is greater than the current one.
5580 lt: Set expiry only when the new expiry is less than the current one.
5582 Returns:
5583 Returns a list which contains for each field in the request:
5584 - `-2` if the field does not exist, or if the key does not exist.
5585 - `0` if the specified NX | XX | GT | LT condition was not met.
5586 - `1` if the expiration time was set or updated.
5587 - `2` if the field was deleted because the specified expiration time is
5588 in the past.
5589 """
5590 conditions = [nx, xx, gt, lt]
5591 if sum(conditions) > 1:
5592 raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.")
5594 if isinstance(unix_time_seconds, datetime.datetime):
5595 unix_time_seconds = int(unix_time_seconds.timestamp())
5597 options = []
5598 if nx:
5599 options.append("NX")
5600 if xx:
5601 options.append("XX")
5602 if gt:
5603 options.append("GT")
5604 if lt:
5605 options.append("LT")
5607 return self.execute_command(
5608 "HEXPIREAT",
5609 name,
5610 unix_time_seconds,
5611 *options,
5612 "FIELDS",
5613 len(fields),
5614 *fields,
5615 )
5617 def hpexpireat(
5618 self,
5619 name: KeyT,
5620 unix_time_milliseconds: AbsExpiryT,
5621 *fields: str,
5622 nx: bool = False,
5623 xx: bool = False,
5624 gt: bool = False,
5625 lt: bool = False,
5626 ) -> ResponseT:
5627 """
5628 Sets or updates the expiration time for fields within a hash key, using an
5629 absolute Unix timestamp in milliseconds.
5631 If a field already has an expiration time, the behavior of the update can be
5632 controlled using the `nx`, `xx`, `gt`, and `lt` parameters.
5634 The return value provides detailed information about the outcome for each field.
5636 For more information, see https://redis.io/commands/hpexpireat
5638 Args:
5639 name: The name of the hash key.
5640 unix_time_milliseconds: Expiration time as Unix timestamp in milliseconds.
5641 Can be an integer or a Python `datetime` object.
5642 fields: List of fields within the hash to apply the expiry.
5643 nx: Set expiry only when the field has no expiry.
5644 xx: Set expiry only when the field has an existing expiry.
5645 gt: Set expiry only when the new expiry is greater than the current one.
5646 lt: Set expiry only when the new expiry is less than the current one.
5648 Returns:
5649 Returns a list which contains for each field in the request:
5650 - `-2` if the field does not exist, or if the key does not exist.
5651 - `0` if the specified NX | XX | GT | LT condition was not met.
5652 - `1` if the expiration time was set or updated.
5653 - `2` if the field was deleted because the specified expiration time is
5654 in the past.
5655 """
5656 conditions = [nx, xx, gt, lt]
5657 if sum(conditions) > 1:
5658 raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.")
5660 if isinstance(unix_time_milliseconds, datetime.datetime):
5661 unix_time_milliseconds = int(unix_time_milliseconds.timestamp() * 1000)
5663 options = []
5664 if nx:
5665 options.append("NX")
5666 if xx:
5667 options.append("XX")
5668 if gt:
5669 options.append("GT")
5670 if lt:
5671 options.append("LT")
5673 return self.execute_command(
5674 "HPEXPIREAT",
5675 name,
5676 unix_time_milliseconds,
5677 *options,
5678 "FIELDS",
5679 len(fields),
5680 *fields,
5681 )
5683 def hpersist(self, name: KeyT, *fields: str) -> ResponseT:
5684 """
5685 Removes the expiration time for each specified field in a hash.
5687 For more information, see https://redis.io/commands/hpersist
5689 Args:
5690 name: The name of the hash key.
5691 fields: A list of fields within the hash from which to remove the
5692 expiration time.
5694 Returns:
5695 Returns a list which contains for each field in the request:
5696 - `-2` if the field does not exist, or if the key does not exist.
5697 - `-1` if the field exists but has no associated expiration time.
5698 - `1` if the expiration time was successfully removed from the field.
5699 """
5700 return self.execute_command("HPERSIST", name, "FIELDS", len(fields), *fields)
5702 def hexpiretime(self, key: KeyT, *fields: str) -> ResponseT:
5703 """
5704 Returns the expiration times of hash fields as Unix timestamps in seconds.
5706 For more information, see https://redis.io/commands/hexpiretime
5708 Args:
5709 key: The hash key.
5710 fields: A list of fields within the hash for which to get the expiration
5711 time.
5713 Returns:
5714 Returns a list which contains for each field in the request:
5715 - `-2` if the field does not exist, or if the key does not exist.
5716 - `-1` if the field exists but has no associated expire time.
5717 - A positive integer representing the expiration Unix timestamp in
5718 seconds, if the field has an associated expiration time.
5719 """
5720 return self.execute_command(
5721 "HEXPIRETIME", key, "FIELDS", len(fields), *fields, keys=[key]
5722 )
5724 def hpexpiretime(self, key: KeyT, *fields: str) -> ResponseT:
5725 """
5726 Returns the expiration times of hash fields as Unix timestamps in milliseconds.
5728 For more information, see https://redis.io/commands/hpexpiretime
5730 Args:
5731 key: The hash key.
5732 fields: A list of fields within the hash for which to get the expiration
5733 time.
5735 Returns:
5736 Returns a list which contains for each field in the request:
5737 - `-2` if the field does not exist, or if the key does not exist.
5738 - `-1` if the field exists but has no associated expire time.
5739 - A positive integer representing the expiration Unix timestamp in
5740 milliseconds, if the field has an associated expiration time.
5741 """
5742 return self.execute_command(
5743 "HPEXPIRETIME", key, "FIELDS", len(fields), *fields, keys=[key]
5744 )
5746 def httl(self, key: KeyT, *fields: str) -> ResponseT:
5747 """
5748 Returns the TTL (Time To Live) in seconds for each specified field within a hash
5749 key.
5751 For more information, see https://redis.io/commands/httl
5753 Args:
5754 key: The hash key.
5755 fields: A list of fields within the hash for which to get the TTL.
5757 Returns:
5758 Returns a list which contains for each field in the request:
5759 - `-2` if the field does not exist, or if the key does not exist.
5760 - `-1` if the field exists but has no associated expire time.
5761 - A positive integer representing the TTL in seconds if the field has
5762 an associated expiration time.
5763 """
5764 return self.execute_command(
5765 "HTTL", key, "FIELDS", len(fields), *fields, keys=[key]
5766 )
5768 def hpttl(self, key: KeyT, *fields: str) -> ResponseT:
5769 """
5770 Returns the TTL (Time To Live) in milliseconds for each specified field within a
5771 hash key.
5773 For more information, see https://redis.io/commands/hpttl
5775 Args:
5776 key: The hash key.
5777 fields: A list of fields within the hash for which to get the TTL.
5779 Returns:
5780 Returns a list which contains for each field in the request:
5781 - `-2` if the field does not exist, or if the key does not exist.
5782 - `-1` if the field exists but has no associated expire time.
5783 - A positive integer representing the TTL in milliseconds if the field
5784 has an associated expiration time.
5785 """
5786 return self.execute_command(
5787 "HPTTL", key, "FIELDS", len(fields), *fields, keys=[key]
5788 )
5791AsyncHashCommands = HashCommands
5794class Script:
5795 """
5796 An executable Lua script object returned by ``register_script``
5797 """
5799 def __init__(self, registered_client: "redis.client.Redis", script: ScriptTextT):
5800 self.registered_client = registered_client
5801 self.script = script
5802 # Precalculate and store the SHA1 hex digest of the script.
5804 if isinstance(script, str):
5805 # We need the encoding from the client in order to generate an
5806 # accurate byte representation of the script
5807 encoder = self.get_encoder()
5808 script = encoder.encode(script)
5809 self.sha = hashlib.sha1(script).hexdigest()
5811 def __call__(
5812 self,
5813 keys: Union[Sequence[KeyT], None] = None,
5814 args: Union[Iterable[EncodableT], None] = None,
5815 client: Union["redis.client.Redis", None] = None,
5816 ):
5817 """Execute the script, passing any required ``args``"""
5818 keys = keys or []
5819 args = args or []
5820 if client is None:
5821 client = self.registered_client
5822 args = tuple(keys) + tuple(args)
5823 # make sure the Redis server knows about the script
5824 from redis.client import Pipeline
5826 if isinstance(client, Pipeline):
5827 # Make sure the pipeline can register the script before executing.
5828 client.scripts.add(self)
5829 try:
5830 return client.evalsha(self.sha, len(keys), *args)
5831 except NoScriptError:
5832 # Maybe the client is pointed to a different server than the client
5833 # that created this instance?
5834 # Overwrite the sha just in case there was a discrepancy.
5835 self.sha = client.script_load(self.script)
5836 return client.evalsha(self.sha, len(keys), *args)
5838 def get_encoder(self):
5839 """Get the encoder to encode string scripts into bytes."""
5840 try:
5841 return self.registered_client.get_encoder()
5842 except AttributeError:
5843 # DEPRECATED
5844 # In version <=4.1.2, this was the code we used to get the encoder.
5845 # However, after 4.1.2 we added support for scripting in clustered
5846 # redis. ClusteredRedis doesn't have a `.connection_pool` attribute
5847 # so we changed the Script class to use
5848 # `self.registered_client.get_encoder` (see above).
5849 # However, that is technically a breaking change, as consumers who
5850 # use Scripts directly might inject a `registered_client` that
5851 # doesn't have a `.get_encoder` field. This try/except prevents us
5852 # from breaking backward-compatibility. Ideally, it would be
5853 # removed in the next major release.
5854 return self.registered_client.connection_pool.get_encoder()
5857class AsyncScript:
5858 """
5859 An executable Lua script object returned by ``register_script``
5860 """
5862 def __init__(
5863 self,
5864 registered_client: "redis.asyncio.client.Redis",
5865 script: ScriptTextT,
5866 ):
5867 self.registered_client = registered_client
5868 self.script = script
5869 # Precalculate and store the SHA1 hex digest of the script.
5871 if isinstance(script, str):
5872 # We need the encoding from the client in order to generate an
5873 # accurate byte representation of the script
5874 try:
5875 encoder = registered_client.connection_pool.get_encoder()
5876 except AttributeError:
5877 # Cluster
5878 encoder = registered_client.get_encoder()
5879 script = encoder.encode(script)
5880 self.sha = hashlib.sha1(script).hexdigest()
5882 async def __call__(
5883 self,
5884 keys: Union[Sequence[KeyT], None] = None,
5885 args: Union[Iterable[EncodableT], None] = None,
5886 client: Union["redis.asyncio.client.Redis", None] = None,
5887 ):
5888 """Execute the script, passing any required ``args``"""
5889 keys = keys or []
5890 args = args or []
5891 if client is None:
5892 client = self.registered_client
5893 args = tuple(keys) + tuple(args)
5894 # make sure the Redis server knows about the script
5895 from redis.asyncio.client import Pipeline
5897 if isinstance(client, Pipeline):
5898 # Make sure the pipeline can register the script before executing.
5899 client.scripts.add(self)
5900 try:
5901 return await client.evalsha(self.sha, len(keys), *args)
5902 except NoScriptError:
5903 # Maybe the client is pointed to a different server than the client
5904 # that created this instance?
5905 # Overwrite the sha just in case there was a discrepancy.
5906 self.sha = await client.script_load(self.script)
5907 return await client.evalsha(self.sha, len(keys), *args)
5910class PubSubCommands(CommandsProtocol):
5911 """
5912 Redis PubSub commands.
5913 see https://redis.io/topics/pubsub
5914 """
5916 def publish(self, channel: ChannelT, message: EncodableT, **kwargs) -> ResponseT:
5917 """
5918 Publish ``message`` on ``channel``.
5919 Returns the number of subscribers the message was delivered to.
5921 For more information, see https://redis.io/commands/publish
5922 """
5923 return self.execute_command("PUBLISH", channel, message, **kwargs)
5925 def spublish(self, shard_channel: ChannelT, message: EncodableT) -> ResponseT:
5926 """
5927 Posts a message to the given shard channel.
5928 Returns the number of clients that received the message
5930 For more information, see https://redis.io/commands/spublish
5931 """
5932 return self.execute_command("SPUBLISH", shard_channel, message)
5934 def pubsub_channels(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
5935 """
5936 Return a list of channels that have at least one subscriber
5938 For more information, see https://redis.io/commands/pubsub-channels
5939 """
5940 return self.execute_command("PUBSUB CHANNELS", pattern, **kwargs)
5942 def pubsub_shardchannels(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
5943 """
5944 Return a list of shard_channels that have at least one subscriber
5946 For more information, see https://redis.io/commands/pubsub-shardchannels
5947 """
5948 return self.execute_command("PUBSUB SHARDCHANNELS", pattern, **kwargs)
5950 def pubsub_numpat(self, **kwargs) -> ResponseT:
5951 """
5952 Returns the number of subscriptions to patterns
5954 For more information, see https://redis.io/commands/pubsub-numpat
5955 """
5956 return self.execute_command("PUBSUB NUMPAT", **kwargs)
5958 def pubsub_numsub(self, *args: ChannelT, **kwargs) -> ResponseT:
5959 """
5960 Return a list of (channel, number of subscribers) tuples
5961 for each channel given in ``*args``
5963 For more information, see https://redis.io/commands/pubsub-numsub
5964 """
5965 return self.execute_command("PUBSUB NUMSUB", *args, **kwargs)
5967 def pubsub_shardnumsub(self, *args: ChannelT, **kwargs) -> ResponseT:
5968 """
5969 Return a list of (shard_channel, number of subscribers) tuples
5970 for each channel given in ``*args``
5972 For more information, see https://redis.io/commands/pubsub-shardnumsub
5973 """
5974 return self.execute_command("PUBSUB SHARDNUMSUB", *args, **kwargs)
5977AsyncPubSubCommands = PubSubCommands
5980class ScriptCommands(CommandsProtocol):
5981 """
5982 Redis Lua script commands. see:
5983 https://redis.io/ebook/part-3-next-steps/chapter-11-scripting-redis-with-lua/
5984 """
5986 def _eval(
5987 self,
5988 command: str,
5989 script: str,
5990 numkeys: int,
5991 *keys_and_args: Union[KeyT, EncodableT],
5992 ) -> Union[Awaitable[str], str]:
5993 return self.execute_command(command, script, numkeys, *keys_and_args)
5995 def eval(
5996 self, script: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5997 ) -> Union[Awaitable[str], str]:
5998 """
5999 Execute the Lua ``script``, specifying the ``numkeys`` the script
6000 will touch and the key names and argument values in ``keys_and_args``.
6001 Returns the result of the script.
6003 In practice, use the object returned by ``register_script``. This
6004 function exists purely for Redis API completion.
6006 For more information, see https://redis.io/commands/eval
6007 """
6008 return self._eval("EVAL", script, numkeys, *keys_and_args)
6010 def eval_ro(
6011 self, script: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
6012 ) -> Union[Awaitable[str], str]:
6013 """
6014 The read-only variant of the EVAL command
6016 Execute the read-only Lua ``script`` specifying the ``numkeys`` the script
6017 will touch and the key names and argument values in ``keys_and_args``.
6018 Returns the result of the script.
6020 For more information, see https://redis.io/commands/eval_ro
6021 """
6022 return self._eval("EVAL_RO", script, numkeys, *keys_and_args)
6024 def _evalsha(
6025 self,
6026 command: str,
6027 sha: str,
6028 numkeys: int,
6029 *keys_and_args: Union[KeyT, EncodableT],
6030 ) -> Union[Awaitable[str], str]:
6031 return self.execute_command(command, sha, numkeys, *keys_and_args)
6033 def evalsha(
6034 self, sha: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
6035 ) -> Union[Awaitable[str], str]:
6036 """
6037 Use the ``sha`` to execute a Lua script already registered via EVAL
6038 or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
6039 key names and argument values in ``keys_and_args``. Returns the result
6040 of the script.
6042 In practice, use the object returned by ``register_script``. This
6043 function exists purely for Redis API completion.
6045 For more information, see https://redis.io/commands/evalsha
6046 """
6047 return self._evalsha("EVALSHA", sha, numkeys, *keys_and_args)
6049 def evalsha_ro(
6050 self, sha: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
6051 ) -> Union[Awaitable[str], str]:
6052 """
6053 The read-only variant of the EVALSHA command
6055 Use the ``sha`` to execute a read-only Lua script already registered via EVAL
6056 or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
6057 key names and argument values in ``keys_and_args``. Returns the result
6058 of the script.
6060 For more information, see https://redis.io/commands/evalsha_ro
6061 """
6062 return self._evalsha("EVALSHA_RO", sha, numkeys, *keys_and_args)
6064 def script_exists(self, *args: str) -> ResponseT:
6065 """
6066 Check if a script exists in the script cache by specifying the SHAs of
6067 each script as ``args``. Returns a list of boolean values indicating if
6068 if each already script exists in the cache_data.
6070 For more information, see https://redis.io/commands/script-exists
6071 """
6072 return self.execute_command("SCRIPT EXISTS", *args)
6074 def script_debug(self, *args) -> None:
6075 raise NotImplementedError(
6076 "SCRIPT DEBUG is intentionally not implemented in the client."
6077 )
6079 def script_flush(
6080 self, sync_type: Union[Literal["SYNC"], Literal["ASYNC"]] = None
6081 ) -> ResponseT:
6082 """Flush all scripts from the script cache_data.
6084 ``sync_type`` is by default SYNC (synchronous) but it can also be
6085 ASYNC.
6087 For more information, see https://redis.io/commands/script-flush
6088 """
6090 # Redis pre 6 had no sync_type.
6091 if sync_type not in ["SYNC", "ASYNC", None]:
6092 raise DataError(
6093 "SCRIPT FLUSH defaults to SYNC in redis > 6.2, or "
6094 "accepts SYNC/ASYNC. For older versions, "
6095 "of redis leave as None."
6096 )
6097 if sync_type is None:
6098 pieces = []
6099 else:
6100 pieces = [sync_type]
6101 return self.execute_command("SCRIPT FLUSH", *pieces)
6103 def script_kill(self) -> ResponseT:
6104 """
6105 Kill the currently executing Lua script
6107 For more information, see https://redis.io/commands/script-kill
6108 """
6109 return self.execute_command("SCRIPT KILL")
6111 def script_load(self, script: ScriptTextT) -> ResponseT:
6112 """
6113 Load a Lua ``script`` into the script cache_data. Returns the SHA.
6115 For more information, see https://redis.io/commands/script-load
6116 """
6117 return self.execute_command("SCRIPT LOAD", script)
6119 def register_script(self: "redis.client.Redis", script: ScriptTextT) -> Script:
6120 """
6121 Register a Lua ``script`` specifying the ``keys`` it will touch.
6122 Returns a Script object that is callable and hides the complexity of
6123 deal with scripts, keys, and shas. This is the preferred way to work
6124 with Lua scripts.
6125 """
6126 return Script(self, script)
6129class AsyncScriptCommands(ScriptCommands):
6130 async def script_debug(self, *args) -> None:
6131 return super().script_debug()
6133 def register_script(
6134 self: "redis.asyncio.client.Redis",
6135 script: ScriptTextT,
6136 ) -> AsyncScript:
6137 """
6138 Register a Lua ``script`` specifying the ``keys`` it will touch.
6139 Returns a Script object that is callable and hides the complexity of
6140 deal with scripts, keys, and shas. This is the preferred way to work
6141 with Lua scripts.
6142 """
6143 return AsyncScript(self, script)
6146class GeoCommands(CommandsProtocol):
6147 """
6148 Redis Geospatial commands.
6149 see: https://redis.com/redis-best-practices/indexing-patterns/geospatial/
6150 """
6152 def geoadd(
6153 self,
6154 name: KeyT,
6155 values: Sequence[EncodableT],
6156 nx: bool = False,
6157 xx: bool = False,
6158 ch: bool = False,
6159 ) -> ResponseT:
6160 """
6161 Add the specified geospatial items to the specified key identified
6162 by the ``name`` argument. The Geospatial items are given as ordered
6163 members of the ``values`` argument, each item or place is formed by
6164 the triad longitude, latitude and name.
6166 Note: You can use ZREM to remove elements.
6168 ``nx`` forces ZADD to only create new elements and not to update
6169 scores for elements that already exist.
6171 ``xx`` forces ZADD to only update scores of elements that already
6172 exist. New elements will not be added.
6174 ``ch`` modifies the return value to be the numbers of elements changed.
6175 Changed elements include new elements that were added and elements
6176 whose scores changed.
6178 For more information, see https://redis.io/commands/geoadd
6179 """
6180 if nx and xx:
6181 raise DataError("GEOADD allows either 'nx' or 'xx', not both")
6182 if len(values) % 3 != 0:
6183 raise DataError("GEOADD requires places with lon, lat and name values")
6184 pieces = [name]
6185 if nx:
6186 pieces.append("NX")
6187 if xx:
6188 pieces.append("XX")
6189 if ch:
6190 pieces.append("CH")
6191 pieces.extend(values)
6192 return self.execute_command("GEOADD", *pieces)
6194 def geodist(
6195 self, name: KeyT, place1: FieldT, place2: FieldT, unit: Optional[str] = None
6196 ) -> ResponseT:
6197 """
6198 Return the distance between ``place1`` and ``place2`` members of the
6199 ``name`` key.
6200 The units must be one of the following : m, km mi, ft. By default
6201 meters are used.
6203 For more information, see https://redis.io/commands/geodist
6204 """
6205 pieces: list[EncodableT] = [name, place1, place2]
6206 if unit and unit not in ("m", "km", "mi", "ft"):
6207 raise DataError("GEODIST invalid unit")
6208 elif unit:
6209 pieces.append(unit)
6210 return self.execute_command("GEODIST", *pieces, keys=[name])
6212 def geohash(self, name: KeyT, *values: FieldT) -> ResponseT:
6213 """
6214 Return the geo hash string for each item of ``values`` members of
6215 the specified key identified by the ``name`` argument.
6217 For more information, see https://redis.io/commands/geohash
6218 """
6219 return self.execute_command("GEOHASH", name, *values, keys=[name])
6221 def geopos(self, name: KeyT, *values: FieldT) -> ResponseT:
6222 """
6223 Return the positions of each item of ``values`` as members of
6224 the specified key identified by the ``name`` argument. Each position
6225 is represented by the pairs lon and lat.
6227 For more information, see https://redis.io/commands/geopos
6228 """
6229 return self.execute_command("GEOPOS", name, *values, keys=[name])
6231 def georadius(
6232 self,
6233 name: KeyT,
6234 longitude: float,
6235 latitude: float,
6236 radius: float,
6237 unit: Optional[str] = None,
6238 withdist: bool = False,
6239 withcoord: bool = False,
6240 withhash: bool = False,
6241 count: Optional[int] = None,
6242 sort: Optional[str] = None,
6243 store: Optional[KeyT] = None,
6244 store_dist: Optional[KeyT] = None,
6245 any: bool = False,
6246 ) -> ResponseT:
6247 """
6248 Return the members of the specified key identified by the
6249 ``name`` argument which are within the borders of the area specified
6250 with the ``latitude`` and ``longitude`` location and the maximum
6251 distance from the center specified by the ``radius`` value.
6253 The units must be one of the following : m, km mi, ft. By default
6255 ``withdist`` indicates to return the distances of each place.
6257 ``withcoord`` indicates to return the latitude and longitude of
6258 each place.
6260 ``withhash`` indicates to return the geohash string of each place.
6262 ``count`` indicates to return the number of elements up to N.
6264 ``sort`` indicates to return the places in a sorted way, ASC for
6265 nearest to fairest and DESC for fairest to nearest.
6267 ``store`` indicates to save the places names in a sorted set named
6268 with a specific key, each element of the destination sorted set is
6269 populated with the score got from the original geo sorted set.
6271 ``store_dist`` indicates to save the places names in a sorted set
6272 named with a specific key, instead of ``store`` the sorted set
6273 destination score is set with the distance.
6275 For more information, see https://redis.io/commands/georadius
6276 """
6277 return self._georadiusgeneric(
6278 "GEORADIUS",
6279 name,
6280 longitude,
6281 latitude,
6282 radius,
6283 unit=unit,
6284 withdist=withdist,
6285 withcoord=withcoord,
6286 withhash=withhash,
6287 count=count,
6288 sort=sort,
6289 store=store,
6290 store_dist=store_dist,
6291 any=any,
6292 )
6294 def georadiusbymember(
6295 self,
6296 name: KeyT,
6297 member: FieldT,
6298 radius: float,
6299 unit: Optional[str] = None,
6300 withdist: bool = False,
6301 withcoord: bool = False,
6302 withhash: bool = False,
6303 count: Optional[int] = None,
6304 sort: Optional[str] = None,
6305 store: Union[KeyT, None] = None,
6306 store_dist: Union[KeyT, None] = None,
6307 any: bool = False,
6308 ) -> ResponseT:
6309 """
6310 This command is exactly like ``georadius`` with the sole difference
6311 that instead of taking, as the center of the area to query, a longitude
6312 and latitude value, it takes the name of a member already existing
6313 inside the geospatial index represented by the sorted set.
6315 For more information, see https://redis.io/commands/georadiusbymember
6316 """
6317 return self._georadiusgeneric(
6318 "GEORADIUSBYMEMBER",
6319 name,
6320 member,
6321 radius,
6322 unit=unit,
6323 withdist=withdist,
6324 withcoord=withcoord,
6325 withhash=withhash,
6326 count=count,
6327 sort=sort,
6328 store=store,
6329 store_dist=store_dist,
6330 any=any,
6331 )
6333 def _georadiusgeneric(
6334 self, command: str, *args: EncodableT, **kwargs: Union[EncodableT, None]
6335 ) -> ResponseT:
6336 pieces = list(args)
6337 if kwargs["unit"] and kwargs["unit"] not in ("m", "km", "mi", "ft"):
6338 raise DataError("GEORADIUS invalid unit")
6339 elif kwargs["unit"]:
6340 pieces.append(kwargs["unit"])
6341 else:
6342 pieces.append("m")
6344 if kwargs["any"] and kwargs["count"] is None:
6345 raise DataError("``any`` can't be provided without ``count``")
6347 for arg_name, byte_repr in (
6348 ("withdist", "WITHDIST"),
6349 ("withcoord", "WITHCOORD"),
6350 ("withhash", "WITHHASH"),
6351 ):
6352 if kwargs[arg_name]:
6353 pieces.append(byte_repr)
6355 if kwargs["count"] is not None:
6356 pieces.extend(["COUNT", kwargs["count"]])
6357 if kwargs["any"]:
6358 pieces.append("ANY")
6360 if kwargs["sort"]:
6361 if kwargs["sort"] == "ASC":
6362 pieces.append("ASC")
6363 elif kwargs["sort"] == "DESC":
6364 pieces.append("DESC")
6365 else:
6366 raise DataError("GEORADIUS invalid sort")
6368 if kwargs["store"] and kwargs["store_dist"]:
6369 raise DataError("GEORADIUS store and store_dist cant be set together")
6371 if kwargs["store"]:
6372 pieces.extend([b"STORE", kwargs["store"]])
6374 if kwargs["store_dist"]:
6375 pieces.extend([b"STOREDIST", kwargs["store_dist"]])
6377 return self.execute_command(command, *pieces, **kwargs)
6379 def geosearch(
6380 self,
6381 name: KeyT,
6382 member: Union[FieldT, None] = None,
6383 longitude: Union[float, None] = None,
6384 latitude: Union[float, None] = None,
6385 unit: str = "m",
6386 radius: Union[float, None] = None,
6387 width: Union[float, None] = None,
6388 height: Union[float, None] = None,
6389 sort: Optional[str] = None,
6390 count: Optional[int] = None,
6391 any: bool = False,
6392 withcoord: bool = False,
6393 withdist: bool = False,
6394 withhash: bool = False,
6395 ) -> ResponseT:
6396 """
6397 Return the members of specified key identified by the
6398 ``name`` argument, which are within the borders of the
6399 area specified by a given shape. This command extends the
6400 GEORADIUS command, so in addition to searching within circular
6401 areas, it supports searching within rectangular areas.
6403 This command should be used in place of the deprecated
6404 GEORADIUS and GEORADIUSBYMEMBER commands.
6406 ``member`` Use the position of the given existing
6407 member in the sorted set. Can't be given with ``longitude``
6408 and ``latitude``.
6410 ``longitude`` and ``latitude`` Use the position given by
6411 this coordinates. Can't be given with ``member``
6412 ``radius`` Similar to GEORADIUS, search inside circular
6413 area according the given radius. Can't be given with
6414 ``height`` and ``width``.
6415 ``height`` and ``width`` Search inside an axis-aligned
6416 rectangle, determined by the given height and width.
6417 Can't be given with ``radius``
6419 ``unit`` must be one of the following : m, km, mi, ft.
6420 `m` for meters (the default value), `km` for kilometers,
6421 `mi` for miles and `ft` for feet.
6423 ``sort`` indicates to return the places in a sorted way,
6424 ASC for nearest to furthest and DESC for furthest to nearest.
6426 ``count`` limit the results to the first count matching items.
6428 ``any`` is set to True, the command will return as soon as
6429 enough matches are found. Can't be provided without ``count``
6431 ``withdist`` indicates to return the distances of each place.
6432 ``withcoord`` indicates to return the latitude and longitude of
6433 each place.
6435 ``withhash`` indicates to return the geohash string of each place.
6437 For more information, see https://redis.io/commands/geosearch
6438 """
6440 return self._geosearchgeneric(
6441 "GEOSEARCH",
6442 name,
6443 member=member,
6444 longitude=longitude,
6445 latitude=latitude,
6446 unit=unit,
6447 radius=radius,
6448 width=width,
6449 height=height,
6450 sort=sort,
6451 count=count,
6452 any=any,
6453 withcoord=withcoord,
6454 withdist=withdist,
6455 withhash=withhash,
6456 store=None,
6457 store_dist=None,
6458 )
6460 def geosearchstore(
6461 self,
6462 dest: KeyT,
6463 name: KeyT,
6464 member: Optional[FieldT] = None,
6465 longitude: Optional[float] = None,
6466 latitude: Optional[float] = None,
6467 unit: str = "m",
6468 radius: Optional[float] = None,
6469 width: Optional[float] = None,
6470 height: Optional[float] = None,
6471 sort: Optional[str] = None,
6472 count: Optional[int] = None,
6473 any: bool = False,
6474 storedist: bool = False,
6475 ) -> ResponseT:
6476 """
6477 This command is like GEOSEARCH, but stores the result in
6478 ``dest``. By default, it stores the results in the destination
6479 sorted set with their geospatial information.
6480 if ``store_dist`` set to True, the command will stores the
6481 items in a sorted set populated with their distance from the
6482 center of the circle or box, as a floating-point number.
6484 For more information, see https://redis.io/commands/geosearchstore
6485 """
6486 return self._geosearchgeneric(
6487 "GEOSEARCHSTORE",
6488 dest,
6489 name,
6490 member=member,
6491 longitude=longitude,
6492 latitude=latitude,
6493 unit=unit,
6494 radius=radius,
6495 width=width,
6496 height=height,
6497 sort=sort,
6498 count=count,
6499 any=any,
6500 withcoord=None,
6501 withdist=None,
6502 withhash=None,
6503 store=None,
6504 store_dist=storedist,
6505 )
6507 def _geosearchgeneric(
6508 self, command: str, *args: EncodableT, **kwargs: Union[EncodableT, None]
6509 ) -> ResponseT:
6510 pieces = list(args)
6512 # FROMMEMBER or FROMLONLAT
6513 if kwargs["member"] is None:
6514 if kwargs["longitude"] is None or kwargs["latitude"] is None:
6515 raise DataError("GEOSEARCH must have member or longitude and latitude")
6516 if kwargs["member"]:
6517 if kwargs["longitude"] or kwargs["latitude"]:
6518 raise DataError(
6519 "GEOSEARCH member and longitude or latitude cant be set together"
6520 )
6521 pieces.extend([b"FROMMEMBER", kwargs["member"]])
6522 if kwargs["longitude"] is not None and kwargs["latitude"] is not None:
6523 pieces.extend([b"FROMLONLAT", kwargs["longitude"], kwargs["latitude"]])
6525 # BYRADIUS or BYBOX
6526 if kwargs["radius"] is None:
6527 if kwargs["width"] is None or kwargs["height"] is None:
6528 raise DataError("GEOSEARCH must have radius or width and height")
6529 if kwargs["unit"] is None:
6530 raise DataError("GEOSEARCH must have unit")
6531 if kwargs["unit"].lower() not in ("m", "km", "mi", "ft"):
6532 raise DataError("GEOSEARCH invalid unit")
6533 if kwargs["radius"]:
6534 if kwargs["width"] or kwargs["height"]:
6535 raise DataError(
6536 "GEOSEARCH radius and width or height cant be set together"
6537 )
6538 pieces.extend([b"BYRADIUS", kwargs["radius"], kwargs["unit"]])
6539 if kwargs["width"] and kwargs["height"]:
6540 pieces.extend([b"BYBOX", kwargs["width"], kwargs["height"], kwargs["unit"]])
6542 # sort
6543 if kwargs["sort"]:
6544 if kwargs["sort"].upper() == "ASC":
6545 pieces.append(b"ASC")
6546 elif kwargs["sort"].upper() == "DESC":
6547 pieces.append(b"DESC")
6548 else:
6549 raise DataError("GEOSEARCH invalid sort")
6551 # count any
6552 if kwargs["count"]:
6553 pieces.extend([b"COUNT", kwargs["count"]])
6554 if kwargs["any"]:
6555 pieces.append(b"ANY")
6556 elif kwargs["any"]:
6557 raise DataError("GEOSEARCH ``any`` can't be provided without count")
6559 # other properties
6560 for arg_name, byte_repr in (
6561 ("withdist", b"WITHDIST"),
6562 ("withcoord", b"WITHCOORD"),
6563 ("withhash", b"WITHHASH"),
6564 ("store_dist", b"STOREDIST"),
6565 ):
6566 if kwargs[arg_name]:
6567 pieces.append(byte_repr)
6569 kwargs["keys"] = [args[0] if command == "GEOSEARCH" else args[1]]
6571 return self.execute_command(command, *pieces, **kwargs)
6574AsyncGeoCommands = GeoCommands
6577class ModuleCommands(CommandsProtocol):
6578 """
6579 Redis Module commands.
6580 see: https://redis.io/topics/modules-intro
6581 """
6583 def module_load(self, path, *args) -> ResponseT:
6584 """
6585 Loads the module from ``path``.
6586 Passes all ``*args`` to the module, during loading.
6587 Raises ``ModuleError`` if a module is not found at ``path``.
6589 For more information, see https://redis.io/commands/module-load
6590 """
6591 return self.execute_command("MODULE LOAD", path, *args)
6593 def module_loadex(
6594 self,
6595 path: str,
6596 options: Optional[List[str]] = None,
6597 args: Optional[List[str]] = None,
6598 ) -> ResponseT:
6599 """
6600 Loads a module from a dynamic library at runtime with configuration directives.
6602 For more information, see https://redis.io/commands/module-loadex
6603 """
6604 pieces = []
6605 if options is not None:
6606 pieces.append("CONFIG")
6607 pieces.extend(options)
6608 if args is not None:
6609 pieces.append("ARGS")
6610 pieces.extend(args)
6612 return self.execute_command("MODULE LOADEX", path, *pieces)
6614 def module_unload(self, name) -> ResponseT:
6615 """
6616 Unloads the module ``name``.
6617 Raises ``ModuleError`` if ``name`` is not in loaded modules.
6619 For more information, see https://redis.io/commands/module-unload
6620 """
6621 return self.execute_command("MODULE UNLOAD", name)
6623 def module_list(self) -> ResponseT:
6624 """
6625 Returns a list of dictionaries containing the name and version of
6626 all loaded modules.
6628 For more information, see https://redis.io/commands/module-list
6629 """
6630 return self.execute_command("MODULE LIST")
6632 def command_info(self) -> None:
6633 raise NotImplementedError(
6634 "COMMAND INFO is intentionally not implemented in the client."
6635 )
6637 def command_count(self) -> ResponseT:
6638 return self.execute_command("COMMAND COUNT")
6640 def command_getkeys(self, *args) -> ResponseT:
6641 return self.execute_command("COMMAND GETKEYS", *args)
6643 def command(self) -> ResponseT:
6644 return self.execute_command("COMMAND")
6647class AsyncModuleCommands(ModuleCommands):
6648 async def command_info(self) -> None:
6649 return super().command_info()
6652class ClusterCommands(CommandsProtocol):
6653 """
6654 Class for Redis Cluster commands
6655 """
6657 def cluster(self, cluster_arg, *args, **kwargs) -> ResponseT:
6658 return self.execute_command(f"CLUSTER {cluster_arg.upper()}", *args, **kwargs)
6660 def readwrite(self, **kwargs) -> ResponseT:
6661 """
6662 Disables read queries for a connection to a Redis Cluster slave node.
6664 For more information, see https://redis.io/commands/readwrite
6665 """
6666 return self.execute_command("READWRITE", **kwargs)
6668 def readonly(self, **kwargs) -> ResponseT:
6669 """
6670 Enables read queries for a connection to a Redis Cluster replica node.
6672 For more information, see https://redis.io/commands/readonly
6673 """
6674 return self.execute_command("READONLY", **kwargs)
6677AsyncClusterCommands = ClusterCommands
6680class FunctionCommands:
6681 """
6682 Redis Function commands
6683 """
6685 def function_load(
6686 self, code: str, replace: Optional[bool] = False
6687 ) -> Union[Awaitable[str], str]:
6688 """
6689 Load a library to Redis.
6690 :param code: the source code (must start with
6691 Shebang statement that provides a metadata about the library)
6692 :param replace: changes the behavior to overwrite the existing library
6693 with the new contents.
6694 Return the library name that was loaded.
6696 For more information, see https://redis.io/commands/function-load
6697 """
6698 pieces = ["REPLACE"] if replace else []
6699 pieces.append(code)
6700 return self.execute_command("FUNCTION LOAD", *pieces)
6702 def function_delete(self, library: str) -> Union[Awaitable[str], str]:
6703 """
6704 Delete the library called ``library`` and all its functions.
6706 For more information, see https://redis.io/commands/function-delete
6707 """
6708 return self.execute_command("FUNCTION DELETE", library)
6710 def function_flush(self, mode: str = "SYNC") -> Union[Awaitable[str], str]:
6711 """
6712 Deletes all the libraries.
6714 For more information, see https://redis.io/commands/function-flush
6715 """
6716 return self.execute_command("FUNCTION FLUSH", mode)
6718 def function_list(
6719 self, library: Optional[str] = "*", withcode: Optional[bool] = False
6720 ) -> Union[Awaitable[List], List]:
6721 """
6722 Return information about the functions and libraries.
6724 Args:
6726 library: specify a pattern for matching library names
6727 withcode: cause the server to include the libraries source implementation
6728 in the reply
6729 """
6730 args = ["LIBRARYNAME", library]
6731 if withcode:
6732 args.append("WITHCODE")
6733 return self.execute_command("FUNCTION LIST", *args)
6735 def _fcall(
6736 self, command: str, function, numkeys: int, *keys_and_args: Any
6737 ) -> Union[Awaitable[str], str]:
6738 return self.execute_command(command, function, numkeys, *keys_and_args)
6740 def fcall(
6741 self, function, numkeys: int, *keys_and_args: Any
6742 ) -> Union[Awaitable[str], str]:
6743 """
6744 Invoke a function.
6746 For more information, see https://redis.io/commands/fcall
6747 """
6748 return self._fcall("FCALL", function, numkeys, *keys_and_args)
6750 def fcall_ro(
6751 self, function, numkeys: int, *keys_and_args: Any
6752 ) -> Union[Awaitable[str], str]:
6753 """
6754 This is a read-only variant of the FCALL command that cannot
6755 execute commands that modify data.
6757 For more information, see https://redis.io/commands/fcall_ro
6758 """
6759 return self._fcall("FCALL_RO", function, numkeys, *keys_and_args)
6761 def function_dump(self) -> Union[Awaitable[str], str]:
6762 """
6763 Return the serialized payload of loaded libraries.
6765 For more information, see https://redis.io/commands/function-dump
6766 """
6767 from redis.client import NEVER_DECODE
6769 options = {}
6770 options[NEVER_DECODE] = []
6772 return self.execute_command("FUNCTION DUMP", **options)
6774 def function_restore(
6775 self, payload: str, policy: Optional[str] = "APPEND"
6776 ) -> Union[Awaitable[str], str]:
6777 """
6778 Restore libraries from the serialized ``payload``.
6779 You can use the optional policy argument to provide a policy
6780 for handling existing libraries.
6782 For more information, see https://redis.io/commands/function-restore
6783 """
6784 return self.execute_command("FUNCTION RESTORE", payload, policy)
6786 def function_kill(self) -> Union[Awaitable[str], str]:
6787 """
6788 Kill a function that is currently executing.
6790 For more information, see https://redis.io/commands/function-kill
6791 """
6792 return self.execute_command("FUNCTION KILL")
6794 def function_stats(self) -> Union[Awaitable[List], List]:
6795 """
6796 Return information about the function that's currently running
6797 and information about the available execution engines.
6799 For more information, see https://redis.io/commands/function-stats
6800 """
6801 return self.execute_command("FUNCTION STATS")
6804AsyncFunctionCommands = FunctionCommands
6807class DataAccessCommands(
6808 BasicKeyCommands,
6809 HyperlogCommands,
6810 HashCommands,
6811 GeoCommands,
6812 ListCommands,
6813 ScanCommands,
6814 SetCommands,
6815 StreamCommands,
6816 SortedSetCommands,
6817):
6818 """
6819 A class containing all of the implemented data access redis commands.
6820 This class is to be used as a mixin for synchronous Redis clients.
6821 """
6824class AsyncDataAccessCommands(
6825 AsyncBasicKeyCommands,
6826 AsyncHyperlogCommands,
6827 AsyncHashCommands,
6828 AsyncGeoCommands,
6829 AsyncListCommands,
6830 AsyncScanCommands,
6831 AsyncSetCommands,
6832 AsyncStreamCommands,
6833 AsyncSortedSetCommands,
6834):
6835 """
6836 A class containing all of the implemented data access redis commands.
6837 This class is to be used as a mixin for asynchronous Redis clients.
6838 """
6841class CoreCommands(
6842 ACLCommands,
6843 ClusterCommands,
6844 DataAccessCommands,
6845 ManagementCommands,
6846 ModuleCommands,
6847 PubSubCommands,
6848 ScriptCommands,
6849 FunctionCommands,
6850):
6851 """
6852 A class containing all of the implemented redis commands. This class is
6853 to be used as a mixin for synchronous Redis clients.
6854 """
6857class AsyncCoreCommands(
6858 AsyncACLCommands,
6859 AsyncClusterCommands,
6860 AsyncDataAccessCommands,
6861 AsyncManagementCommands,
6862 AsyncModuleCommands,
6863 AsyncPubSubCommands,
6864 AsyncScriptCommands,
6865 AsyncFunctionCommands,
6866):
6867 """
6868 A class containing all of the implemented redis commands. This class is
6869 to be used as a mixin for asynchronous Redis clients.
6870 """