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 BasicKeyCommands(CommandsProtocol):
1563 """
1564 Redis basic key-based commands
1565 """
1567 def append(self, key: KeyT, value: EncodableT) -> ResponseT:
1568 """
1569 Appends the string ``value`` to the value at ``key``. If ``key``
1570 doesn't already exist, create it with a value of ``value``.
1571 Returns the new length of the value at ``key``.
1573 For more information, see https://redis.io/commands/append
1574 """
1575 return self.execute_command("APPEND", key, value)
1577 def bitcount(
1578 self,
1579 key: KeyT,
1580 start: Optional[int] = None,
1581 end: Optional[int] = None,
1582 mode: Optional[str] = None,
1583 ) -> ResponseT:
1584 """
1585 Returns the count of set bits in the value of ``key``. Optional
1586 ``start`` and ``end`` parameters indicate which bytes to consider
1588 For more information, see https://redis.io/commands/bitcount
1589 """
1590 params = [key]
1591 if start is not None and end is not None:
1592 params.append(start)
1593 params.append(end)
1594 elif (start is not None and end is None) or (end is not None and start is None):
1595 raise DataError("Both start and end must be specified")
1596 if mode is not None:
1597 params.append(mode)
1598 return self.execute_command("BITCOUNT", *params, keys=[key])
1600 def bitfield(
1601 self: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
1602 key: KeyT,
1603 default_overflow: Optional[str] = None,
1604 ) -> BitFieldOperation:
1605 """
1606 Return a BitFieldOperation instance to conveniently construct one or
1607 more bitfield operations on ``key``.
1609 For more information, see https://redis.io/commands/bitfield
1610 """
1611 return BitFieldOperation(self, key, default_overflow=default_overflow)
1613 def bitfield_ro(
1614 self: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
1615 key: KeyT,
1616 encoding: str,
1617 offset: BitfieldOffsetT,
1618 items: Optional[list] = None,
1619 ) -> ResponseT:
1620 """
1621 Return an array of the specified bitfield values
1622 where the first value is found using ``encoding`` and ``offset``
1623 parameters and remaining values are result of corresponding
1624 encoding/offset pairs in optional list ``items``
1625 Read-only variant of the BITFIELD command.
1627 For more information, see https://redis.io/commands/bitfield_ro
1628 """
1629 params = [key, "GET", encoding, offset]
1631 items = items or []
1632 for encoding, offset in items:
1633 params.extend(["GET", encoding, offset])
1634 return self.execute_command("BITFIELD_RO", *params, keys=[key])
1636 def bitop(self, operation: str, dest: KeyT, *keys: KeyT) -> ResponseT:
1637 """
1638 Perform a bitwise operation using ``operation`` between ``keys`` and
1639 store the result in ``dest``.
1641 For more information, see https://redis.io/commands/bitop
1642 """
1643 return self.execute_command("BITOP", operation, dest, *keys)
1645 def bitpos(
1646 self,
1647 key: KeyT,
1648 bit: int,
1649 start: Optional[int] = None,
1650 end: Optional[int] = None,
1651 mode: Optional[str] = None,
1652 ) -> ResponseT:
1653 """
1654 Return the position of the first bit set to 1 or 0 in a string.
1655 ``start`` and ``end`` defines search range. The range is interpreted
1656 as a range of bytes and not a range of bits, so start=0 and end=2
1657 means to look at the first three bytes.
1659 For more information, see https://redis.io/commands/bitpos
1660 """
1661 if bit not in (0, 1):
1662 raise DataError("bit must be 0 or 1")
1663 params = [key, bit]
1665 start is not None and params.append(start)
1667 if start is not None and end is not None:
1668 params.append(end)
1669 elif start is None and end is not None:
1670 raise DataError("start argument is not set, when end is specified")
1672 if mode is not None:
1673 params.append(mode)
1674 return self.execute_command("BITPOS", *params, keys=[key])
1676 def copy(
1677 self,
1678 source: str,
1679 destination: str,
1680 destination_db: Optional[str] = None,
1681 replace: bool = False,
1682 ) -> ResponseT:
1683 """
1684 Copy the value stored in the ``source`` key to the ``destination`` key.
1686 ``destination_db`` an alternative destination database. By default,
1687 the ``destination`` key is created in the source Redis database.
1689 ``replace`` whether the ``destination`` key should be removed before
1690 copying the value to it. By default, the value is not copied if
1691 the ``destination`` key already exists.
1693 For more information, see https://redis.io/commands/copy
1694 """
1695 params = [source, destination]
1696 if destination_db is not None:
1697 params.extend(["DB", destination_db])
1698 if replace:
1699 params.append("REPLACE")
1700 return self.execute_command("COPY", *params)
1702 def decrby(self, name: KeyT, amount: int = 1) -> ResponseT:
1703 """
1704 Decrements the value of ``key`` by ``amount``. If no key exists,
1705 the value will be initialized as 0 - ``amount``
1707 For more information, see https://redis.io/commands/decrby
1708 """
1709 return self.execute_command("DECRBY", name, amount)
1711 decr = decrby
1713 def delete(self, *names: KeyT) -> ResponseT:
1714 """
1715 Delete one or more keys specified by ``names``
1716 """
1717 return self.execute_command("DEL", *names)
1719 def __delitem__(self, name: KeyT):
1720 self.delete(name)
1722 def dump(self, name: KeyT) -> ResponseT:
1723 """
1724 Return a serialized version of the value stored at the specified key.
1725 If key does not exist a nil bulk reply is returned.
1727 For more information, see https://redis.io/commands/dump
1728 """
1729 from redis.client import NEVER_DECODE
1731 options = {}
1732 options[NEVER_DECODE] = []
1733 return self.execute_command("DUMP", name, **options)
1735 def exists(self, *names: KeyT) -> ResponseT:
1736 """
1737 Returns the number of ``names`` that exist
1739 For more information, see https://redis.io/commands/exists
1740 """
1741 return self.execute_command("EXISTS", *names, keys=names)
1743 __contains__ = exists
1745 def expire(
1746 self,
1747 name: KeyT,
1748 time: ExpiryT,
1749 nx: bool = False,
1750 xx: bool = False,
1751 gt: bool = False,
1752 lt: bool = False,
1753 ) -> ResponseT:
1754 """
1755 Set an expire flag on key ``name`` for ``time`` seconds with given
1756 ``option``. ``time`` can be represented by an integer or a Python timedelta
1757 object.
1759 Valid options are:
1760 NX -> Set expiry only when the key has no expiry
1761 XX -> Set expiry only when the key has an existing expiry
1762 GT -> Set expiry only when the new expiry is greater than current one
1763 LT -> Set expiry only when the new expiry is less than current one
1765 For more information, see https://redis.io/commands/expire
1766 """
1767 if isinstance(time, datetime.timedelta):
1768 time = int(time.total_seconds())
1770 exp_option = list()
1771 if nx:
1772 exp_option.append("NX")
1773 if xx:
1774 exp_option.append("XX")
1775 if gt:
1776 exp_option.append("GT")
1777 if lt:
1778 exp_option.append("LT")
1780 return self.execute_command("EXPIRE", name, time, *exp_option)
1782 def expireat(
1783 self,
1784 name: KeyT,
1785 when: AbsExpiryT,
1786 nx: bool = False,
1787 xx: bool = False,
1788 gt: bool = False,
1789 lt: bool = False,
1790 ) -> ResponseT:
1791 """
1792 Set an expire flag on key ``name`` with given ``option``. ``when``
1793 can be represented as an integer indicating unix time or a Python
1794 datetime object.
1796 Valid options are:
1797 -> NX -- Set expiry only when the key has no expiry
1798 -> XX -- Set expiry only when the key has an existing expiry
1799 -> GT -- Set expiry only when the new expiry is greater than current one
1800 -> LT -- Set expiry only when the new expiry is less than current one
1802 For more information, see https://redis.io/commands/expireat
1803 """
1804 if isinstance(when, datetime.datetime):
1805 when = int(when.timestamp())
1807 exp_option = list()
1808 if nx:
1809 exp_option.append("NX")
1810 if xx:
1811 exp_option.append("XX")
1812 if gt:
1813 exp_option.append("GT")
1814 if lt:
1815 exp_option.append("LT")
1817 return self.execute_command("EXPIREAT", name, when, *exp_option)
1819 def expiretime(self, key: str) -> int:
1820 """
1821 Returns the absolute Unix timestamp (since January 1, 1970) in seconds
1822 at which the given key will expire.
1824 For more information, see https://redis.io/commands/expiretime
1825 """
1826 return self.execute_command("EXPIRETIME", key)
1828 def get(self, name: KeyT) -> ResponseT:
1829 """
1830 Return the value at key ``name``, or None if the key doesn't exist
1832 For more information, see https://redis.io/commands/get
1833 """
1834 return self.execute_command("GET", name, keys=[name])
1836 def getdel(self, name: KeyT) -> ResponseT:
1837 """
1838 Get the value at key ``name`` and delete the key. This command
1839 is similar to GET, except for the fact that it also deletes
1840 the key on success (if and only if the key's value type
1841 is a string).
1843 For more information, see https://redis.io/commands/getdel
1844 """
1845 return self.execute_command("GETDEL", name)
1847 def getex(
1848 self,
1849 name: KeyT,
1850 ex: Optional[ExpiryT] = None,
1851 px: Optional[ExpiryT] = None,
1852 exat: Optional[AbsExpiryT] = None,
1853 pxat: Optional[AbsExpiryT] = None,
1854 persist: bool = False,
1855 ) -> ResponseT:
1856 """
1857 Get the value of key and optionally set its expiration.
1858 GETEX is similar to GET, but is a write command with
1859 additional options. All time parameters can be given as
1860 datetime.timedelta or integers.
1862 ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
1864 ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
1866 ``exat`` sets an expire flag on key ``name`` for ``ex`` seconds,
1867 specified in unix time.
1869 ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,
1870 specified in unix time.
1872 ``persist`` remove the time to live associated with ``name``.
1874 For more information, see https://redis.io/commands/getex
1875 """
1876 opset = {ex, px, exat, pxat}
1877 if len(opset) > 2 or len(opset) > 1 and persist:
1878 raise DataError(
1879 "``ex``, ``px``, ``exat``, ``pxat``, "
1880 "and ``persist`` are mutually exclusive."
1881 )
1883 exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
1885 if persist:
1886 exp_options.append("PERSIST")
1888 return self.execute_command("GETEX", name, *exp_options)
1890 def __getitem__(self, name: KeyT):
1891 """
1892 Return the value at key ``name``, raises a KeyError if the key
1893 doesn't exist.
1894 """
1895 value = self.get(name)
1896 if value is not None:
1897 return value
1898 raise KeyError(name)
1900 def getbit(self, name: KeyT, offset: int) -> ResponseT:
1901 """
1902 Returns an integer indicating the value of ``offset`` in ``name``
1904 For more information, see https://redis.io/commands/getbit
1905 """
1906 return self.execute_command("GETBIT", name, offset, keys=[name])
1908 def getrange(self, key: KeyT, start: int, end: int) -> ResponseT:
1909 """
1910 Returns the substring of the string value stored at ``key``,
1911 determined by the offsets ``start`` and ``end`` (both are inclusive)
1913 For more information, see https://redis.io/commands/getrange
1914 """
1915 return self.execute_command("GETRANGE", key, start, end, keys=[key])
1917 def getset(self, name: KeyT, value: EncodableT) -> ResponseT:
1918 """
1919 Sets the value at key ``name`` to ``value``
1920 and returns the old value at key ``name`` atomically.
1922 As per Redis 6.2, GETSET is considered deprecated.
1923 Please use SET with GET parameter in new code.
1925 For more information, see https://redis.io/commands/getset
1926 """
1927 return self.execute_command("GETSET", name, value)
1929 def incrby(self, name: KeyT, amount: int = 1) -> ResponseT:
1930 """
1931 Increments the value of ``key`` by ``amount``. If no key exists,
1932 the value will be initialized as ``amount``
1934 For more information, see https://redis.io/commands/incrby
1935 """
1936 return self.execute_command("INCRBY", name, amount)
1938 incr = incrby
1940 def incrbyfloat(self, name: KeyT, amount: float = 1.0) -> ResponseT:
1941 """
1942 Increments the value at key ``name`` by floating ``amount``.
1943 If no key exists, the value will be initialized as ``amount``
1945 For more information, see https://redis.io/commands/incrbyfloat
1946 """
1947 return self.execute_command("INCRBYFLOAT", name, amount)
1949 def keys(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
1950 """
1951 Returns a list of keys matching ``pattern``
1953 For more information, see https://redis.io/commands/keys
1954 """
1955 return self.execute_command("KEYS", pattern, **kwargs)
1957 def lmove(
1958 self, first_list: str, second_list: str, src: str = "LEFT", dest: str = "RIGHT"
1959 ) -> ResponseT:
1960 """
1961 Atomically returns and removes the first/last element of a list,
1962 pushing it as the first/last element on the destination list.
1963 Returns the element being popped and pushed.
1965 For more information, see https://redis.io/commands/lmove
1966 """
1967 params = [first_list, second_list, src, dest]
1968 return self.execute_command("LMOVE", *params)
1970 def blmove(
1971 self,
1972 first_list: str,
1973 second_list: str,
1974 timeout: int,
1975 src: str = "LEFT",
1976 dest: str = "RIGHT",
1977 ) -> ResponseT:
1978 """
1979 Blocking version of lmove.
1981 For more information, see https://redis.io/commands/blmove
1982 """
1983 params = [first_list, second_list, src, dest, timeout]
1984 return self.execute_command("BLMOVE", *params)
1986 def mget(self, keys: KeysT, *args: EncodableT) -> ResponseT:
1987 """
1988 Returns a list of values ordered identically to ``keys``
1990 For more information, see https://redis.io/commands/mget
1991 """
1992 from redis.client import EMPTY_RESPONSE
1994 args = list_or_args(keys, args)
1995 options = {}
1996 if not args:
1997 options[EMPTY_RESPONSE] = []
1998 options["keys"] = args
1999 return self.execute_command("MGET", *args, **options)
2001 def mset(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
2002 """
2003 Sets key/values based on a mapping. Mapping is a dictionary of
2004 key/value pairs. Both keys and values should be strings or types that
2005 can be cast to a string via str().
2007 For more information, see https://redis.io/commands/mset
2008 """
2009 items = []
2010 for pair in mapping.items():
2011 items.extend(pair)
2012 return self.execute_command("MSET", *items)
2014 def msetnx(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
2015 """
2016 Sets key/values based on a mapping if none of the keys are already set.
2017 Mapping is a dictionary of key/value pairs. Both keys and values
2018 should be strings or types that can be cast to a string via str().
2019 Returns a boolean indicating if the operation was successful.
2021 For more information, see https://redis.io/commands/msetnx
2022 """
2023 items = []
2024 for pair in mapping.items():
2025 items.extend(pair)
2026 return self.execute_command("MSETNX", *items)
2028 def move(self, name: KeyT, db: int) -> ResponseT:
2029 """
2030 Moves the key ``name`` to a different Redis database ``db``
2032 For more information, see https://redis.io/commands/move
2033 """
2034 return self.execute_command("MOVE", name, db)
2036 def persist(self, name: KeyT) -> ResponseT:
2037 """
2038 Removes an expiration on ``name``
2040 For more information, see https://redis.io/commands/persist
2041 """
2042 return self.execute_command("PERSIST", name)
2044 def pexpire(
2045 self,
2046 name: KeyT,
2047 time: ExpiryT,
2048 nx: bool = False,
2049 xx: bool = False,
2050 gt: bool = False,
2051 lt: bool = False,
2052 ) -> ResponseT:
2053 """
2054 Set an expire flag on key ``name`` for ``time`` milliseconds
2055 with given ``option``. ``time`` can be represented by an
2056 integer or a Python timedelta object.
2058 Valid options are:
2059 NX -> Set expiry only when the key has no expiry
2060 XX -> Set expiry only when the key has an existing expiry
2061 GT -> Set expiry only when the new expiry is greater than current one
2062 LT -> Set expiry only when the new expiry is less than current one
2064 For more information, see https://redis.io/commands/pexpire
2065 """
2066 if isinstance(time, datetime.timedelta):
2067 time = int(time.total_seconds() * 1000)
2069 exp_option = list()
2070 if nx:
2071 exp_option.append("NX")
2072 if xx:
2073 exp_option.append("XX")
2074 if gt:
2075 exp_option.append("GT")
2076 if lt:
2077 exp_option.append("LT")
2078 return self.execute_command("PEXPIRE", name, time, *exp_option)
2080 def pexpireat(
2081 self,
2082 name: KeyT,
2083 when: AbsExpiryT,
2084 nx: bool = False,
2085 xx: bool = False,
2086 gt: bool = False,
2087 lt: bool = False,
2088 ) -> ResponseT:
2089 """
2090 Set an expire flag on key ``name`` with given ``option``. ``when``
2091 can be represented as an integer representing unix time in
2092 milliseconds (unix time * 1000) or a Python datetime object.
2094 Valid options are:
2095 NX -> Set expiry only when the key has no expiry
2096 XX -> Set expiry only when the key has an existing expiry
2097 GT -> Set expiry only when the new expiry is greater than current one
2098 LT -> Set expiry only when the new expiry is less than current one
2100 For more information, see https://redis.io/commands/pexpireat
2101 """
2102 if isinstance(when, datetime.datetime):
2103 when = int(when.timestamp() * 1000)
2104 exp_option = list()
2105 if nx:
2106 exp_option.append("NX")
2107 if xx:
2108 exp_option.append("XX")
2109 if gt:
2110 exp_option.append("GT")
2111 if lt:
2112 exp_option.append("LT")
2113 return self.execute_command("PEXPIREAT", name, when, *exp_option)
2115 def pexpiretime(self, key: str) -> int:
2116 """
2117 Returns the absolute Unix timestamp (since January 1, 1970) in milliseconds
2118 at which the given key will expire.
2120 For more information, see https://redis.io/commands/pexpiretime
2121 """
2122 return self.execute_command("PEXPIRETIME", key)
2124 def psetex(self, name: KeyT, time_ms: ExpiryT, value: EncodableT):
2125 """
2126 Set the value of key ``name`` to ``value`` that expires in ``time_ms``
2127 milliseconds. ``time_ms`` can be represented by an integer or a Python
2128 timedelta object
2130 For more information, see https://redis.io/commands/psetex
2131 """
2132 if isinstance(time_ms, datetime.timedelta):
2133 time_ms = int(time_ms.total_seconds() * 1000)
2134 return self.execute_command("PSETEX", name, time_ms, value)
2136 def pttl(self, name: KeyT) -> ResponseT:
2137 """
2138 Returns the number of milliseconds until the key ``name`` will expire
2140 For more information, see https://redis.io/commands/pttl
2141 """
2142 return self.execute_command("PTTL", name)
2144 def hrandfield(
2145 self, key: str, count: Optional[int] = None, withvalues: bool = False
2146 ) -> ResponseT:
2147 """
2148 Return a random field from the hash value stored at key.
2150 count: if the argument is positive, return an array of distinct fields.
2151 If called with a negative count, the behavior changes and the command
2152 is allowed to return the same field multiple times. In this case,
2153 the number of returned fields is the absolute value of the
2154 specified count.
2155 withvalues: The optional WITHVALUES modifier changes the reply so it
2156 includes the respective values of the randomly selected hash fields.
2158 For more information, see https://redis.io/commands/hrandfield
2159 """
2160 params = []
2161 if count is not None:
2162 params.append(count)
2163 if withvalues:
2164 params.append("WITHVALUES")
2166 return self.execute_command("HRANDFIELD", key, *params)
2168 def randomkey(self, **kwargs) -> ResponseT:
2169 """
2170 Returns the name of a random key
2172 For more information, see https://redis.io/commands/randomkey
2173 """
2174 return self.execute_command("RANDOMKEY", **kwargs)
2176 def rename(self, src: KeyT, dst: KeyT) -> ResponseT:
2177 """
2178 Rename key ``src`` to ``dst``
2180 For more information, see https://redis.io/commands/rename
2181 """
2182 return self.execute_command("RENAME", src, dst)
2184 def renamenx(self, src: KeyT, dst: KeyT):
2185 """
2186 Rename key ``src`` to ``dst`` if ``dst`` doesn't already exist
2188 For more information, see https://redis.io/commands/renamenx
2189 """
2190 return self.execute_command("RENAMENX", src, dst)
2192 def restore(
2193 self,
2194 name: KeyT,
2195 ttl: float,
2196 value: EncodableT,
2197 replace: bool = False,
2198 absttl: bool = False,
2199 idletime: Optional[int] = None,
2200 frequency: Optional[int] = None,
2201 ) -> ResponseT:
2202 """
2203 Create a key using the provided serialized value, previously obtained
2204 using DUMP.
2206 ``replace`` allows an existing key on ``name`` to be overridden. If
2207 it's not specified an error is raised on collision.
2209 ``absttl`` if True, specified ``ttl`` should represent an absolute Unix
2210 timestamp in milliseconds in which the key will expire. (Redis 5.0 or
2211 greater).
2213 ``idletime`` Used for eviction, this is the number of seconds the
2214 key must be idle, prior to execution.
2216 ``frequency`` Used for eviction, this is the frequency counter of
2217 the object stored at the key, prior to execution.
2219 For more information, see https://redis.io/commands/restore
2220 """
2221 params = [name, ttl, value]
2222 if replace:
2223 params.append("REPLACE")
2224 if absttl:
2225 params.append("ABSTTL")
2226 if idletime is not None:
2227 params.append("IDLETIME")
2228 try:
2229 params.append(int(idletime))
2230 except ValueError:
2231 raise DataError("idletimemust be an integer")
2233 if frequency is not None:
2234 params.append("FREQ")
2235 try:
2236 params.append(int(frequency))
2237 except ValueError:
2238 raise DataError("frequency must be an integer")
2240 return self.execute_command("RESTORE", *params)
2242 def set(
2243 self,
2244 name: KeyT,
2245 value: EncodableT,
2246 ex: Optional[ExpiryT] = None,
2247 px: Optional[ExpiryT] = None,
2248 nx: bool = False,
2249 xx: bool = False,
2250 keepttl: bool = False,
2251 get: bool = False,
2252 exat: Optional[AbsExpiryT] = None,
2253 pxat: Optional[AbsExpiryT] = None,
2254 ) -> ResponseT:
2255 """
2256 Set the value at key ``name`` to ``value``
2258 ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
2260 ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
2262 ``nx`` if set to True, set the value at key ``name`` to ``value`` only
2263 if it does not exist.
2265 ``xx`` if set to True, set the value at key ``name`` to ``value`` only
2266 if it already exists.
2268 ``keepttl`` if True, retain the time to live associated with the key.
2269 (Available since Redis 6.0)
2271 ``get`` if True, set the value at key ``name`` to ``value`` and return
2272 the old value stored at key, or None if the key did not exist.
2273 (Available since Redis 6.2)
2275 ``exat`` sets an expire flag on key ``name`` for ``ex`` seconds,
2276 specified in unix time.
2278 ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,
2279 specified in unix time.
2281 For more information, see https://redis.io/commands/set
2282 """
2283 opset = {ex, px, exat, pxat}
2284 if len(opset) > 2 or len(opset) > 1 and keepttl:
2285 raise DataError(
2286 "``ex``, ``px``, ``exat``, ``pxat``, "
2287 "and ``keepttl`` are mutually exclusive."
2288 )
2290 if nx and xx:
2291 raise DataError("``nx`` and ``xx`` are mutually exclusive.")
2293 pieces: list[EncodableT] = [name, value]
2294 options = {}
2296 pieces.extend(extract_expire_flags(ex, px, exat, pxat))
2298 if keepttl:
2299 pieces.append("KEEPTTL")
2301 if nx:
2302 pieces.append("NX")
2303 if xx:
2304 pieces.append("XX")
2306 if get:
2307 pieces.append("GET")
2308 options["get"] = True
2310 return self.execute_command("SET", *pieces, **options)
2312 def __setitem__(self, name: KeyT, value: EncodableT):
2313 self.set(name, value)
2315 def setbit(self, name: KeyT, offset: int, value: int) -> ResponseT:
2316 """
2317 Flag the ``offset`` in ``name`` as ``value``. Returns an integer
2318 indicating the previous value of ``offset``.
2320 For more information, see https://redis.io/commands/setbit
2321 """
2322 value = value and 1 or 0
2323 return self.execute_command("SETBIT", name, offset, value)
2325 def setex(self, name: KeyT, time: ExpiryT, value: EncodableT) -> ResponseT:
2326 """
2327 Set the value of key ``name`` to ``value`` that expires in ``time``
2328 seconds. ``time`` can be represented by an integer or a Python
2329 timedelta object.
2331 For more information, see https://redis.io/commands/setex
2332 """
2333 if isinstance(time, datetime.timedelta):
2334 time = int(time.total_seconds())
2335 return self.execute_command("SETEX", name, time, value)
2337 def setnx(self, name: KeyT, value: EncodableT) -> ResponseT:
2338 """
2339 Set the value of key ``name`` to ``value`` if key doesn't exist
2341 For more information, see https://redis.io/commands/setnx
2342 """
2343 return self.execute_command("SETNX", name, value)
2345 def setrange(self, name: KeyT, offset: int, value: EncodableT) -> ResponseT:
2346 """
2347 Overwrite bytes in the value of ``name`` starting at ``offset`` with
2348 ``value``. If ``offset`` plus the length of ``value`` exceeds the
2349 length of the original value, the new value will be larger than before.
2350 If ``offset`` exceeds the length of the original value, null bytes
2351 will be used to pad between the end of the previous value and the start
2352 of what's being injected.
2354 Returns the length of the new string.
2356 For more information, see https://redis.io/commands/setrange
2357 """
2358 return self.execute_command("SETRANGE", name, offset, value)
2360 def stralgo(
2361 self,
2362 algo: Literal["LCS"],
2363 value1: KeyT,
2364 value2: KeyT,
2365 specific_argument: Union[Literal["strings"], Literal["keys"]] = "strings",
2366 len: bool = False,
2367 idx: bool = False,
2368 minmatchlen: Optional[int] = None,
2369 withmatchlen: bool = False,
2370 **kwargs,
2371 ) -> ResponseT:
2372 """
2373 Implements complex algorithms that operate on strings.
2374 Right now the only algorithm implemented is the LCS algorithm
2375 (longest common substring). However new algorithms could be
2376 implemented in the future.
2378 ``algo`` Right now must be LCS
2379 ``value1`` and ``value2`` Can be two strings or two keys
2380 ``specific_argument`` Specifying if the arguments to the algorithm
2381 will be keys or strings. strings is the default.
2382 ``len`` Returns just the len of the match.
2383 ``idx`` Returns the match positions in each string.
2384 ``minmatchlen`` Restrict the list of matches to the ones of a given
2385 minimal length. Can be provided only when ``idx`` set to True.
2386 ``withmatchlen`` Returns the matches with the len of the match.
2387 Can be provided only when ``idx`` set to True.
2389 For more information, see https://redis.io/commands/stralgo
2390 """
2391 # check validity
2392 supported_algo = ["LCS"]
2393 if algo not in supported_algo:
2394 supported_algos_str = ", ".join(supported_algo)
2395 raise DataError(f"The supported algorithms are: {supported_algos_str}")
2396 if specific_argument not in ["keys", "strings"]:
2397 raise DataError("specific_argument can be only keys or strings")
2398 if len and idx:
2399 raise DataError("len and idx cannot be provided together.")
2401 pieces: list[EncodableT] = [algo, specific_argument.upper(), value1, value2]
2402 if len:
2403 pieces.append(b"LEN")
2404 if idx:
2405 pieces.append(b"IDX")
2406 try:
2407 int(minmatchlen)
2408 pieces.extend([b"MINMATCHLEN", minmatchlen])
2409 except TypeError:
2410 pass
2411 if withmatchlen:
2412 pieces.append(b"WITHMATCHLEN")
2414 return self.execute_command(
2415 "STRALGO",
2416 *pieces,
2417 len=len,
2418 idx=idx,
2419 minmatchlen=minmatchlen,
2420 withmatchlen=withmatchlen,
2421 **kwargs,
2422 )
2424 def strlen(self, name: KeyT) -> ResponseT:
2425 """
2426 Return the number of bytes stored in the value of ``name``
2428 For more information, see https://redis.io/commands/strlen
2429 """
2430 return self.execute_command("STRLEN", name, keys=[name])
2432 def substr(self, name: KeyT, start: int, end: int = -1) -> ResponseT:
2433 """
2434 Return a substring of the string at key ``name``. ``start`` and ``end``
2435 are 0-based integers specifying the portion of the string to return.
2436 """
2437 return self.execute_command("SUBSTR", name, start, end, keys=[name])
2439 def touch(self, *args: KeyT) -> ResponseT:
2440 """
2441 Alters the last access time of a key(s) ``*args``. A key is ignored
2442 if it does not exist.
2444 For more information, see https://redis.io/commands/touch
2445 """
2446 return self.execute_command("TOUCH", *args)
2448 def ttl(self, name: KeyT) -> ResponseT:
2449 """
2450 Returns the number of seconds until the key ``name`` will expire
2452 For more information, see https://redis.io/commands/ttl
2453 """
2454 return self.execute_command("TTL", name)
2456 def type(self, name: KeyT) -> ResponseT:
2457 """
2458 Returns the type of key ``name``
2460 For more information, see https://redis.io/commands/type
2461 """
2462 return self.execute_command("TYPE", name, keys=[name])
2464 def watch(self, *names: KeyT) -> None:
2465 """
2466 Watches the values at keys ``names``, or None if the key doesn't exist
2468 For more information, see https://redis.io/commands/watch
2469 """
2470 warnings.warn(DeprecationWarning("Call WATCH from a Pipeline object"))
2472 def unwatch(self) -> None:
2473 """
2474 Unwatches all previously watched keys for a transaction
2476 For more information, see https://redis.io/commands/unwatch
2477 """
2478 warnings.warn(DeprecationWarning("Call UNWATCH from a Pipeline object"))
2480 def unlink(self, *names: KeyT) -> ResponseT:
2481 """
2482 Unlink one or more keys specified by ``names``
2484 For more information, see https://redis.io/commands/unlink
2485 """
2486 return self.execute_command("UNLINK", *names)
2488 def lcs(
2489 self,
2490 key1: str,
2491 key2: str,
2492 len: Optional[bool] = False,
2493 idx: Optional[bool] = False,
2494 minmatchlen: Optional[int] = 0,
2495 withmatchlen: Optional[bool] = False,
2496 ) -> Union[str, int, list]:
2497 """
2498 Find the longest common subsequence between ``key1`` and ``key2``.
2499 If ``len`` is true the length of the match will will be returned.
2500 If ``idx`` is true the match position in each strings will be returned.
2501 ``minmatchlen`` restrict the list of matches to the ones of
2502 the given ``minmatchlen``.
2503 If ``withmatchlen`` the length of the match also will be returned.
2504 For more information, see https://redis.io/commands/lcs
2505 """
2506 pieces = [key1, key2]
2507 if len:
2508 pieces.append("LEN")
2509 if idx:
2510 pieces.append("IDX")
2511 if minmatchlen != 0:
2512 pieces.extend(["MINMATCHLEN", minmatchlen])
2513 if withmatchlen:
2514 pieces.append("WITHMATCHLEN")
2515 return self.execute_command("LCS", *pieces, keys=[key1, key2])
2518class AsyncBasicKeyCommands(BasicKeyCommands):
2519 def __delitem__(self, name: KeyT):
2520 raise TypeError("Async Redis client does not support class deletion")
2522 def __contains__(self, name: KeyT):
2523 raise TypeError("Async Redis client does not support class inclusion")
2525 def __getitem__(self, name: KeyT):
2526 raise TypeError("Async Redis client does not support class retrieval")
2528 def __setitem__(self, name: KeyT, value: EncodableT):
2529 raise TypeError("Async Redis client does not support class assignment")
2531 async def watch(self, *names: KeyT) -> None:
2532 return super().watch(*names)
2534 async def unwatch(self) -> None:
2535 return super().unwatch()
2538class ListCommands(CommandsProtocol):
2539 """
2540 Redis commands for List data type.
2541 see: https://redis.io/topics/data-types#lists
2542 """
2544 def blpop(
2545 self, keys: List, timeout: Optional[Number] = 0
2546 ) -> Union[Awaitable[list], list]:
2547 """
2548 LPOP a value off of the first non-empty list
2549 named in the ``keys`` list.
2551 If none of the lists in ``keys`` has a value to LPOP, then block
2552 for ``timeout`` seconds, or until a value gets pushed on to one
2553 of the lists.
2555 If timeout is 0, then block indefinitely.
2557 For more information, see https://redis.io/commands/blpop
2558 """
2559 if timeout is None:
2560 timeout = 0
2561 keys = list_or_args(keys, None)
2562 keys.append(timeout)
2563 return self.execute_command("BLPOP", *keys)
2565 def brpop(
2566 self, keys: List, timeout: Optional[Number] = 0
2567 ) -> Union[Awaitable[list], list]:
2568 """
2569 RPOP a value off of the first non-empty list
2570 named in the ``keys`` list.
2572 If none of the lists in ``keys`` has a value to RPOP, then block
2573 for ``timeout`` seconds, or until a value gets pushed on to one
2574 of the lists.
2576 If timeout is 0, then block indefinitely.
2578 For more information, see https://redis.io/commands/brpop
2579 """
2580 if timeout is None:
2581 timeout = 0
2582 keys = list_or_args(keys, None)
2583 keys.append(timeout)
2584 return self.execute_command("BRPOP", *keys)
2586 def brpoplpush(
2587 self, src: str, dst: str, timeout: Optional[Number] = 0
2588 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
2589 """
2590 Pop a value off the tail of ``src``, push it on the head of ``dst``
2591 and then return it.
2593 This command blocks until a value is in ``src`` or until ``timeout``
2594 seconds elapse, whichever is first. A ``timeout`` value of 0 blocks
2595 forever.
2597 For more information, see https://redis.io/commands/brpoplpush
2598 """
2599 if timeout is None:
2600 timeout = 0
2601 return self.execute_command("BRPOPLPUSH", src, dst, timeout)
2603 def blmpop(
2604 self,
2605 timeout: float,
2606 numkeys: int,
2607 *args: str,
2608 direction: str,
2609 count: Optional[int] = 1,
2610 ) -> Optional[list]:
2611 """
2612 Pop ``count`` values (default 1) from first non-empty in the list
2613 of provided key names.
2615 When all lists are empty this command blocks the connection until another
2616 client pushes to it or until the timeout, timeout of 0 blocks indefinitely
2618 For more information, see https://redis.io/commands/blmpop
2619 """
2620 cmd_args = [timeout, numkeys, *args, direction, "COUNT", count]
2622 return self.execute_command("BLMPOP", *cmd_args)
2624 def lmpop(
2625 self,
2626 num_keys: int,
2627 *args: str,
2628 direction: str,
2629 count: Optional[int] = 1,
2630 ) -> Union[Awaitable[list], list]:
2631 """
2632 Pop ``count`` values (default 1) first non-empty list key from the list
2633 of args provided key names.
2635 For more information, see https://redis.io/commands/lmpop
2636 """
2637 cmd_args = [num_keys] + list(args) + [direction]
2638 if count != 1:
2639 cmd_args.extend(["COUNT", count])
2641 return self.execute_command("LMPOP", *cmd_args)
2643 def lindex(
2644 self, name: str, index: int
2645 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
2646 """
2647 Return the item from list ``name`` at position ``index``
2649 Negative indexes are supported and will return an item at the
2650 end of the list
2652 For more information, see https://redis.io/commands/lindex
2653 """
2654 return self.execute_command("LINDEX", name, index, keys=[name])
2656 def linsert(
2657 self, name: str, where: str, refvalue: str, value: str
2658 ) -> Union[Awaitable[int], int]:
2659 """
2660 Insert ``value`` in list ``name`` either immediately before or after
2661 [``where``] ``refvalue``
2663 Returns the new length of the list on success or -1 if ``refvalue``
2664 is not in the list.
2666 For more information, see https://redis.io/commands/linsert
2667 """
2668 return self.execute_command("LINSERT", name, where, refvalue, value)
2670 def llen(self, name: str) -> Union[Awaitable[int], int]:
2671 """
2672 Return the length of the list ``name``
2674 For more information, see https://redis.io/commands/llen
2675 """
2676 return self.execute_command("LLEN", name, keys=[name])
2678 def lpop(
2679 self,
2680 name: str,
2681 count: Optional[int] = None,
2682 ) -> Union[Awaitable[Union[str, List, None]], Union[str, List, None]]:
2683 """
2684 Removes and returns the first elements of the list ``name``.
2686 By default, the command pops a single element from the beginning of
2687 the list. When provided with the optional ``count`` argument, the reply
2688 will consist of up to count elements, depending on the list's length.
2690 For more information, see https://redis.io/commands/lpop
2691 """
2692 if count is not None:
2693 return self.execute_command("LPOP", name, count)
2694 else:
2695 return self.execute_command("LPOP", name)
2697 def lpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2698 """
2699 Push ``values`` onto the head of the list ``name``
2701 For more information, see https://redis.io/commands/lpush
2702 """
2703 return self.execute_command("LPUSH", name, *values)
2705 def lpushx(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2706 """
2707 Push ``value`` onto the head of the list ``name`` if ``name`` exists
2709 For more information, see https://redis.io/commands/lpushx
2710 """
2711 return self.execute_command("LPUSHX", name, *values)
2713 def lrange(self, name: str, start: int, end: int) -> Union[Awaitable[list], list]:
2714 """
2715 Return a slice of the list ``name`` between
2716 position ``start`` and ``end``
2718 ``start`` and ``end`` can be negative numbers just like
2719 Python slicing notation
2721 For more information, see https://redis.io/commands/lrange
2722 """
2723 return self.execute_command("LRANGE", name, start, end, keys=[name])
2725 def lrem(self, name: str, count: int, value: str) -> Union[Awaitable[int], int]:
2726 """
2727 Remove the first ``count`` occurrences of elements equal to ``value``
2728 from the list stored at ``name``.
2730 The count argument influences the operation in the following ways:
2731 count > 0: Remove elements equal to value moving from head to tail.
2732 count < 0: Remove elements equal to value moving from tail to head.
2733 count = 0: Remove all elements equal to value.
2735 For more information, see https://redis.io/commands/lrem
2736 """
2737 return self.execute_command("LREM", name, count, value)
2739 def lset(self, name: str, index: int, value: str) -> Union[Awaitable[str], str]:
2740 """
2741 Set element at ``index`` of list ``name`` to ``value``
2743 For more information, see https://redis.io/commands/lset
2744 """
2745 return self.execute_command("LSET", name, index, value)
2747 def ltrim(self, name: str, start: int, end: int) -> Union[Awaitable[str], str]:
2748 """
2749 Trim the list ``name``, removing all values not within the slice
2750 between ``start`` and ``end``
2752 ``start`` and ``end`` can be negative numbers just like
2753 Python slicing notation
2755 For more information, see https://redis.io/commands/ltrim
2756 """
2757 return self.execute_command("LTRIM", name, start, end)
2759 def rpop(
2760 self,
2761 name: str,
2762 count: Optional[int] = None,
2763 ) -> Union[Awaitable[Union[str, List, None]], Union[str, List, None]]:
2764 """
2765 Removes and returns the last elements of the list ``name``.
2767 By default, the command pops a single element from the end of the list.
2768 When provided with the optional ``count`` argument, the reply will
2769 consist of up to count elements, depending on the list's length.
2771 For more information, see https://redis.io/commands/rpop
2772 """
2773 if count is not None:
2774 return self.execute_command("RPOP", name, count)
2775 else:
2776 return self.execute_command("RPOP", name)
2778 def rpoplpush(self, src: str, dst: str) -> Union[Awaitable[str], str]:
2779 """
2780 RPOP a value off of the ``src`` list and atomically LPUSH it
2781 on to the ``dst`` list. Returns the value.
2783 For more information, see https://redis.io/commands/rpoplpush
2784 """
2785 return self.execute_command("RPOPLPUSH", src, dst)
2787 def rpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2788 """
2789 Push ``values`` onto the tail of the list ``name``
2791 For more information, see https://redis.io/commands/rpush
2792 """
2793 return self.execute_command("RPUSH", name, *values)
2795 def rpushx(self, name: str, *values: str) -> Union[Awaitable[int], int]:
2796 """
2797 Push ``value`` onto the tail of the list ``name`` if ``name`` exists
2799 For more information, see https://redis.io/commands/rpushx
2800 """
2801 return self.execute_command("RPUSHX", name, *values)
2803 def lpos(
2804 self,
2805 name: str,
2806 value: str,
2807 rank: Optional[int] = None,
2808 count: Optional[int] = None,
2809 maxlen: Optional[int] = None,
2810 ) -> Union[str, List, None]:
2811 """
2812 Get position of ``value`` within the list ``name``
2814 If specified, ``rank`` indicates the "rank" of the first element to
2815 return in case there are multiple copies of ``value`` in the list.
2816 By default, LPOS returns the position of the first occurrence of
2817 ``value`` in the list. When ``rank`` 2, LPOS returns the position of
2818 the second ``value`` in the list. If ``rank`` is negative, LPOS
2819 searches the list in reverse. For example, -1 would return the
2820 position of the last occurrence of ``value`` and -2 would return the
2821 position of the next to last occurrence of ``value``.
2823 If specified, ``count`` indicates that LPOS should return a list of
2824 up to ``count`` positions. A ``count`` of 2 would return a list of
2825 up to 2 positions. A ``count`` of 0 returns a list of all positions
2826 matching ``value``. When ``count`` is specified and but ``value``
2827 does not exist in the list, an empty list is returned.
2829 If specified, ``maxlen`` indicates the maximum number of list
2830 elements to scan. A ``maxlen`` of 1000 will only return the
2831 position(s) of items within the first 1000 entries in the list.
2832 A ``maxlen`` of 0 (the default) will scan the entire list.
2834 For more information, see https://redis.io/commands/lpos
2835 """
2836 pieces: list[EncodableT] = [name, value]
2837 if rank is not None:
2838 pieces.extend(["RANK", rank])
2840 if count is not None:
2841 pieces.extend(["COUNT", count])
2843 if maxlen is not None:
2844 pieces.extend(["MAXLEN", maxlen])
2846 return self.execute_command("LPOS", *pieces, keys=[name])
2848 def sort(
2849 self,
2850 name: str,
2851 start: Optional[int] = None,
2852 num: Optional[int] = None,
2853 by: Optional[str] = None,
2854 get: Optional[List[str]] = None,
2855 desc: bool = False,
2856 alpha: bool = False,
2857 store: Optional[str] = None,
2858 groups: Optional[bool] = False,
2859 ) -> Union[List, int]:
2860 """
2861 Sort and return the list, set or sorted set at ``name``.
2863 ``start`` and ``num`` allow for paging through the sorted data
2865 ``by`` allows using an external key to weight and sort the items.
2866 Use an "*" to indicate where in the key the item value is located
2868 ``get`` allows for returning items from external keys rather than the
2869 sorted data itself. Use an "*" to indicate where in the key
2870 the item value is located
2872 ``desc`` allows for reversing the sort
2874 ``alpha`` allows for sorting lexicographically rather than numerically
2876 ``store`` allows for storing the result of the sort into
2877 the key ``store``
2879 ``groups`` if set to True and if ``get`` contains at least two
2880 elements, sort will return a list of tuples, each containing the
2881 values fetched from the arguments to ``get``.
2883 For more information, see https://redis.io/commands/sort
2884 """
2885 if (start is not None and num is None) or (num is not None and start is None):
2886 raise DataError("``start`` and ``num`` must both be specified")
2888 pieces: list[EncodableT] = [name]
2889 if by is not None:
2890 pieces.extend([b"BY", by])
2891 if start is not None and num is not None:
2892 pieces.extend([b"LIMIT", start, num])
2893 if get is not None:
2894 # If get is a string assume we want to get a single value.
2895 # Otherwise assume it's an interable and we want to get multiple
2896 # values. We can't just iterate blindly because strings are
2897 # iterable.
2898 if isinstance(get, (bytes, str)):
2899 pieces.extend([b"GET", get])
2900 else:
2901 for g in get:
2902 pieces.extend([b"GET", g])
2903 if desc:
2904 pieces.append(b"DESC")
2905 if alpha:
2906 pieces.append(b"ALPHA")
2907 if store is not None:
2908 pieces.extend([b"STORE", store])
2909 if groups:
2910 if not get or isinstance(get, (bytes, str)) or len(get) < 2:
2911 raise DataError(
2912 'when using "groups" the "get" argument '
2913 "must be specified and contain at least "
2914 "two keys"
2915 )
2917 options = {"groups": len(get) if groups else None}
2918 options["keys"] = [name]
2919 return self.execute_command("SORT", *pieces, **options)
2921 def sort_ro(
2922 self,
2923 key: str,
2924 start: Optional[int] = None,
2925 num: Optional[int] = None,
2926 by: Optional[str] = None,
2927 get: Optional[List[str]] = None,
2928 desc: bool = False,
2929 alpha: bool = False,
2930 ) -> list:
2931 """
2932 Returns the elements contained in the list, set or sorted set at key.
2933 (read-only variant of the SORT command)
2935 ``start`` and ``num`` allow for paging through the sorted data
2937 ``by`` allows using an external key to weight and sort the items.
2938 Use an "*" to indicate where in the key the item value is located
2940 ``get`` allows for returning items from external keys rather than the
2941 sorted data itself. Use an "*" to indicate where in the key
2942 the item value is located
2944 ``desc`` allows for reversing the sort
2946 ``alpha`` allows for sorting lexicographically rather than numerically
2948 For more information, see https://redis.io/commands/sort_ro
2949 """
2950 return self.sort(
2951 key, start=start, num=num, by=by, get=get, desc=desc, alpha=alpha
2952 )
2955AsyncListCommands = ListCommands
2958class ScanCommands(CommandsProtocol):
2959 """
2960 Redis SCAN commands.
2961 see: https://redis.io/commands/scan
2962 """
2964 def scan(
2965 self,
2966 cursor: int = 0,
2967 match: Union[PatternT, None] = None,
2968 count: Optional[int] = None,
2969 _type: Optional[str] = None,
2970 **kwargs,
2971 ) -> ResponseT:
2972 """
2973 Incrementally return lists of key names. Also return a cursor
2974 indicating the scan position.
2976 ``match`` allows for filtering the keys by pattern
2978 ``count`` provides a hint to Redis about the number of keys to
2979 return per batch.
2981 ``_type`` filters the returned values by a particular Redis type.
2982 Stock Redis instances allow for the following types:
2983 HASH, LIST, SET, STREAM, STRING, ZSET
2984 Additionally, Redis modules can expose other types as well.
2986 For more information, see https://redis.io/commands/scan
2987 """
2988 pieces: list[EncodableT] = [cursor]
2989 if match is not None:
2990 pieces.extend([b"MATCH", match])
2991 if count is not None:
2992 pieces.extend([b"COUNT", count])
2993 if _type is not None:
2994 pieces.extend([b"TYPE", _type])
2995 return self.execute_command("SCAN", *pieces, **kwargs)
2997 def scan_iter(
2998 self,
2999 match: Union[PatternT, None] = None,
3000 count: Optional[int] = None,
3001 _type: Optional[str] = None,
3002 **kwargs,
3003 ) -> Iterator:
3004 """
3005 Make an iterator using the SCAN command so that the client doesn't
3006 need to remember the cursor position.
3008 ``match`` allows for filtering the keys by pattern
3010 ``count`` provides a hint to Redis about the number of keys to
3011 return per batch.
3013 ``_type`` filters the returned values by a particular Redis type.
3014 Stock Redis instances allow for the following types:
3015 HASH, LIST, SET, STREAM, STRING, ZSET
3016 Additionally, Redis modules can expose other types as well.
3017 """
3018 cursor = "0"
3019 while cursor != 0:
3020 cursor, data = self.scan(
3021 cursor=cursor, match=match, count=count, _type=_type, **kwargs
3022 )
3023 yield from data
3025 def sscan(
3026 self,
3027 name: KeyT,
3028 cursor: int = 0,
3029 match: Union[PatternT, None] = None,
3030 count: Optional[int] = None,
3031 ) -> ResponseT:
3032 """
3033 Incrementally return lists of elements in a set. Also return a cursor
3034 indicating the scan position.
3036 ``match`` allows for filtering the keys by pattern
3038 ``count`` allows for hint the minimum number of returns
3040 For more information, see https://redis.io/commands/sscan
3041 """
3042 pieces: list[EncodableT] = [name, cursor]
3043 if match is not None:
3044 pieces.extend([b"MATCH", match])
3045 if count is not None:
3046 pieces.extend([b"COUNT", count])
3047 return self.execute_command("SSCAN", *pieces)
3049 def sscan_iter(
3050 self,
3051 name: KeyT,
3052 match: Union[PatternT, None] = None,
3053 count: Optional[int] = None,
3054 ) -> Iterator:
3055 """
3056 Make an iterator using the SSCAN command so that the client doesn't
3057 need to remember the cursor position.
3059 ``match`` allows for filtering the keys by pattern
3061 ``count`` allows for hint the minimum number of returns
3062 """
3063 cursor = "0"
3064 while cursor != 0:
3065 cursor, data = self.sscan(name, cursor=cursor, match=match, count=count)
3066 yield from data
3068 def hscan(
3069 self,
3070 name: KeyT,
3071 cursor: int = 0,
3072 match: Union[PatternT, None] = None,
3073 count: Optional[int] = None,
3074 no_values: Union[bool, None] = None,
3075 ) -> ResponseT:
3076 """
3077 Incrementally return key/value slices in a hash. Also return a cursor
3078 indicating the scan position.
3080 ``match`` allows for filtering the keys by pattern
3082 ``count`` allows for hint the minimum number of returns
3084 ``no_values`` indicates to return only the keys, without values.
3086 For more information, see https://redis.io/commands/hscan
3087 """
3088 pieces: list[EncodableT] = [name, cursor]
3089 if match is not None:
3090 pieces.extend([b"MATCH", match])
3091 if count is not None:
3092 pieces.extend([b"COUNT", count])
3093 if no_values is not None:
3094 pieces.extend([b"NOVALUES"])
3095 return self.execute_command("HSCAN", *pieces, no_values=no_values)
3097 def hscan_iter(
3098 self,
3099 name: str,
3100 match: Union[PatternT, None] = None,
3101 count: Optional[int] = None,
3102 no_values: Union[bool, None] = None,
3103 ) -> Iterator:
3104 """
3105 Make an iterator using the HSCAN command so that the client doesn't
3106 need to remember the cursor position.
3108 ``match`` allows for filtering the keys by pattern
3110 ``count`` allows for hint the minimum number of returns
3112 ``no_values`` indicates to return only the keys, without values
3113 """
3114 cursor = "0"
3115 while cursor != 0:
3116 cursor, data = self.hscan(
3117 name, cursor=cursor, match=match, count=count, no_values=no_values
3118 )
3119 if no_values:
3120 yield from data
3121 else:
3122 yield from data.items()
3124 def zscan(
3125 self,
3126 name: KeyT,
3127 cursor: int = 0,
3128 match: Union[PatternT, None] = None,
3129 count: Optional[int] = None,
3130 score_cast_func: Union[type, Callable] = float,
3131 ) -> ResponseT:
3132 """
3133 Incrementally return lists of elements in a sorted set. Also return a
3134 cursor indicating the scan position.
3136 ``match`` allows for filtering the keys by pattern
3138 ``count`` allows for hint the minimum number of returns
3140 ``score_cast_func`` a callable used to cast the score return value
3142 For more information, see https://redis.io/commands/zscan
3143 """
3144 pieces = [name, cursor]
3145 if match is not None:
3146 pieces.extend([b"MATCH", match])
3147 if count is not None:
3148 pieces.extend([b"COUNT", count])
3149 options = {"score_cast_func": score_cast_func}
3150 return self.execute_command("ZSCAN", *pieces, **options)
3152 def zscan_iter(
3153 self,
3154 name: KeyT,
3155 match: Union[PatternT, None] = None,
3156 count: Optional[int] = None,
3157 score_cast_func: Union[type, Callable] = float,
3158 ) -> Iterator:
3159 """
3160 Make an iterator using the ZSCAN command so that the client doesn't
3161 need to remember the cursor position.
3163 ``match`` allows for filtering the keys by pattern
3165 ``count`` allows for hint the minimum number of returns
3167 ``score_cast_func`` a callable used to cast the score return value
3168 """
3169 cursor = "0"
3170 while cursor != 0:
3171 cursor, data = self.zscan(
3172 name,
3173 cursor=cursor,
3174 match=match,
3175 count=count,
3176 score_cast_func=score_cast_func,
3177 )
3178 yield from data
3181class AsyncScanCommands(ScanCommands):
3182 async def scan_iter(
3183 self,
3184 match: Union[PatternT, None] = None,
3185 count: Optional[int] = None,
3186 _type: Optional[str] = None,
3187 **kwargs,
3188 ) -> AsyncIterator:
3189 """
3190 Make an iterator using the SCAN command so that the client doesn't
3191 need to remember the cursor position.
3193 ``match`` allows for filtering the keys by pattern
3195 ``count`` provides a hint to Redis about the number of keys to
3196 return per batch.
3198 ``_type`` filters the returned values by a particular Redis type.
3199 Stock Redis instances allow for the following types:
3200 HASH, LIST, SET, STREAM, STRING, ZSET
3201 Additionally, Redis modules can expose other types as well.
3202 """
3203 cursor = "0"
3204 while cursor != 0:
3205 cursor, data = await self.scan(
3206 cursor=cursor, match=match, count=count, _type=_type, **kwargs
3207 )
3208 for d in data:
3209 yield d
3211 async def sscan_iter(
3212 self,
3213 name: KeyT,
3214 match: Union[PatternT, None] = None,
3215 count: Optional[int] = None,
3216 ) -> AsyncIterator:
3217 """
3218 Make an iterator using the SSCAN command so that the client doesn't
3219 need to remember the cursor position.
3221 ``match`` allows for filtering the keys by pattern
3223 ``count`` allows for hint the minimum number of returns
3224 """
3225 cursor = "0"
3226 while cursor != 0:
3227 cursor, data = await self.sscan(
3228 name, cursor=cursor, match=match, count=count
3229 )
3230 for d in data:
3231 yield d
3233 async def hscan_iter(
3234 self,
3235 name: str,
3236 match: Union[PatternT, None] = None,
3237 count: Optional[int] = None,
3238 no_values: Union[bool, None] = None,
3239 ) -> AsyncIterator:
3240 """
3241 Make an iterator using the HSCAN command so that the client doesn't
3242 need to remember the cursor position.
3244 ``match`` allows for filtering the keys by pattern
3246 ``count`` allows for hint the minimum number of returns
3248 ``no_values`` indicates to return only the keys, without values
3249 """
3250 cursor = "0"
3251 while cursor != 0:
3252 cursor, data = await self.hscan(
3253 name, cursor=cursor, match=match, count=count, no_values=no_values
3254 )
3255 if no_values:
3256 for it in data:
3257 yield it
3258 else:
3259 for it in data.items():
3260 yield it
3262 async def zscan_iter(
3263 self,
3264 name: KeyT,
3265 match: Union[PatternT, None] = None,
3266 count: Optional[int] = None,
3267 score_cast_func: Union[type, Callable] = float,
3268 ) -> AsyncIterator:
3269 """
3270 Make an iterator using the ZSCAN command so that the client doesn't
3271 need to remember the cursor position.
3273 ``match`` allows for filtering the keys by pattern
3275 ``count`` allows for hint the minimum number of returns
3277 ``score_cast_func`` a callable used to cast the score return value
3278 """
3279 cursor = "0"
3280 while cursor != 0:
3281 cursor, data = await self.zscan(
3282 name,
3283 cursor=cursor,
3284 match=match,
3285 count=count,
3286 score_cast_func=score_cast_func,
3287 )
3288 for d in data:
3289 yield d
3292class SetCommands(CommandsProtocol):
3293 """
3294 Redis commands for Set data type.
3295 see: https://redis.io/topics/data-types#sets
3296 """
3298 def sadd(self, name: KeyT, *values: FieldT) -> Union[Awaitable[int], int]:
3299 """
3300 Add ``value(s)`` to set ``name``
3302 For more information, see https://redis.io/commands/sadd
3303 """
3304 return self.execute_command("SADD", name, *values)
3306 def scard(self, name: KeyT) -> Union[Awaitable[int], int]:
3307 """
3308 Return the number of elements in set ``name``
3310 For more information, see https://redis.io/commands/scard
3311 """
3312 return self.execute_command("SCARD", name, keys=[name])
3314 def sdiff(self, keys: List, *args: List) -> Union[Awaitable[list], list]:
3315 """
3316 Return the difference of sets specified by ``keys``
3318 For more information, see https://redis.io/commands/sdiff
3319 """
3320 args = list_or_args(keys, args)
3321 return self.execute_command("SDIFF", *args, keys=args)
3323 def sdiffstore(
3324 self, dest: str, keys: List, *args: List
3325 ) -> Union[Awaitable[int], int]:
3326 """
3327 Store the difference of sets specified by ``keys`` into a new
3328 set named ``dest``. Returns the number of keys in the new set.
3330 For more information, see https://redis.io/commands/sdiffstore
3331 """
3332 args = list_or_args(keys, args)
3333 return self.execute_command("SDIFFSTORE", dest, *args)
3335 def sinter(self, keys: List, *args: List) -> Union[Awaitable[list], list]:
3336 """
3337 Return the intersection of sets specified by ``keys``
3339 For more information, see https://redis.io/commands/sinter
3340 """
3341 args = list_or_args(keys, args)
3342 return self.execute_command("SINTER", *args, keys=args)
3344 def sintercard(
3345 self, numkeys: int, keys: List[KeyT], limit: int = 0
3346 ) -> Union[Awaitable[int], int]:
3347 """
3348 Return the cardinality of the intersect of multiple sets specified by ``keys``.
3350 When LIMIT provided (defaults to 0 and means unlimited), if the intersection
3351 cardinality reaches limit partway through the computation, the algorithm will
3352 exit and yield limit as the cardinality
3354 For more information, see https://redis.io/commands/sintercard
3355 """
3356 args = [numkeys, *keys, "LIMIT", limit]
3357 return self.execute_command("SINTERCARD", *args, keys=keys)
3359 def sinterstore(
3360 self, dest: KeyT, keys: List, *args: List
3361 ) -> Union[Awaitable[int], int]:
3362 """
3363 Store the intersection of sets specified by ``keys`` into a new
3364 set named ``dest``. Returns the number of keys in the new set.
3366 For more information, see https://redis.io/commands/sinterstore
3367 """
3368 args = list_or_args(keys, args)
3369 return self.execute_command("SINTERSTORE", dest, *args)
3371 def sismember(
3372 self, name: KeyT, value: str
3373 ) -> Union[Awaitable[Union[Literal[0], Literal[1]]], Union[Literal[0], Literal[1]]]:
3374 """
3375 Return whether ``value`` is a member of set ``name``:
3376 - 1 if the value is a member of the set.
3377 - 0 if the value is not a member of the set or if key does not exist.
3379 For more information, see https://redis.io/commands/sismember
3380 """
3381 return self.execute_command("SISMEMBER", name, value, keys=[name])
3383 def smembers(self, name: KeyT) -> Union[Awaitable[Set], Set]:
3384 """
3385 Return all members of the set ``name``
3387 For more information, see https://redis.io/commands/smembers
3388 """
3389 return self.execute_command("SMEMBERS", name, keys=[name])
3391 def smismember(
3392 self, name: KeyT, values: List, *args: List
3393 ) -> Union[
3394 Awaitable[List[Union[Literal[0], Literal[1]]]],
3395 List[Union[Literal[0], Literal[1]]],
3396 ]:
3397 """
3398 Return whether each value in ``values`` is a member of the set ``name``
3399 as a list of ``int`` in the order of ``values``:
3400 - 1 if the value is a member of the set.
3401 - 0 if the value is not a member of the set or if key does not exist.
3403 For more information, see https://redis.io/commands/smismember
3404 """
3405 args = list_or_args(values, args)
3406 return self.execute_command("SMISMEMBER", name, *args, keys=[name])
3408 def smove(self, src: KeyT, dst: KeyT, value: str) -> Union[Awaitable[bool], bool]:
3409 """
3410 Move ``value`` from set ``src`` to set ``dst`` atomically
3412 For more information, see https://redis.io/commands/smove
3413 """
3414 return self.execute_command("SMOVE", src, dst, value)
3416 def spop(self, name: KeyT, count: Optional[int] = None) -> Union[str, List, None]:
3417 """
3418 Remove and return a random member of set ``name``
3420 For more information, see https://redis.io/commands/spop
3421 """
3422 args = (count is not None) and [count] or []
3423 return self.execute_command("SPOP", name, *args)
3425 def srandmember(
3426 self, name: KeyT, number: Optional[int] = None
3427 ) -> Union[str, List, None]:
3428 """
3429 If ``number`` is None, returns a random member of set ``name``.
3431 If ``number`` is supplied, returns a list of ``number`` random
3432 members of set ``name``. Note this is only available when running
3433 Redis 2.6+.
3435 For more information, see https://redis.io/commands/srandmember
3436 """
3437 args = (number is not None) and [number] or []
3438 return self.execute_command("SRANDMEMBER", name, *args)
3440 def srem(self, name: KeyT, *values: FieldT) -> Union[Awaitable[int], int]:
3441 """
3442 Remove ``values`` from set ``name``
3444 For more information, see https://redis.io/commands/srem
3445 """
3446 return self.execute_command("SREM", name, *values)
3448 def sunion(self, keys: List, *args: List) -> Union[Awaitable[List], List]:
3449 """
3450 Return the union of sets specified by ``keys``
3452 For more information, see https://redis.io/commands/sunion
3453 """
3454 args = list_or_args(keys, args)
3455 return self.execute_command("SUNION", *args, keys=args)
3457 def sunionstore(
3458 self, dest: KeyT, keys: List, *args: List
3459 ) -> Union[Awaitable[int], int]:
3460 """
3461 Store the union of sets specified by ``keys`` into a new
3462 set named ``dest``. Returns the number of keys in the new set.
3464 For more information, see https://redis.io/commands/sunionstore
3465 """
3466 args = list_or_args(keys, args)
3467 return self.execute_command("SUNIONSTORE", dest, *args)
3470AsyncSetCommands = SetCommands
3473class StreamCommands(CommandsProtocol):
3474 """
3475 Redis commands for Stream data type.
3476 see: https://redis.io/topics/streams-intro
3477 """
3479 def xack(self, name: KeyT, groupname: GroupT, *ids: StreamIdT) -> ResponseT:
3480 """
3481 Acknowledges the successful processing of one or more messages.
3483 Args:
3484 name: name of the stream.
3485 groupname: name of the consumer group.
3486 *ids: message ids to acknowledge.
3488 For more information, see https://redis.io/commands/xack
3489 """
3490 return self.execute_command("XACK", name, groupname, *ids)
3492 def xackdel(
3493 self,
3494 name: KeyT,
3495 groupname: GroupT,
3496 *ids: StreamIdT,
3497 ref_policy: Literal["KEEPREF", "DELREF", "ACKED"] = "KEEPREF",
3498 ) -> ResponseT:
3499 """
3500 Combines the functionality of XACK and XDEL. Acknowledges the specified
3501 message IDs in the given consumer group and simultaneously attempts to
3502 delete the corresponding entries from the stream.
3503 """
3504 if not ids:
3505 raise DataError("XACKDEL requires at least one message ID")
3507 if ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
3508 raise DataError("XACKDEL ref_policy must be one of: KEEPREF, DELREF, ACKED")
3510 pieces = [name, groupname, ref_policy, "IDS", len(ids)]
3511 pieces.extend(ids)
3512 return self.execute_command("XACKDEL", *pieces)
3514 def xadd(
3515 self,
3516 name: KeyT,
3517 fields: Dict[FieldT, EncodableT],
3518 id: StreamIdT = "*",
3519 maxlen: Optional[int] = None,
3520 approximate: bool = True,
3521 nomkstream: bool = False,
3522 minid: Union[StreamIdT, None] = None,
3523 limit: Optional[int] = None,
3524 ref_policy: Optional[Literal["KEEPREF", "DELREF", "ACKED"]] = None,
3525 ) -> ResponseT:
3526 """
3527 Add to a stream.
3528 name: name of the stream
3529 fields: dict of field/value pairs to insert into the stream
3530 id: Location to insert this record. By default it is appended.
3531 maxlen: truncate old stream members beyond this size.
3532 Can't be specified with minid.
3533 approximate: actual stream length may be slightly more than maxlen
3534 nomkstream: When set to true, do not make a stream
3535 minid: the minimum id in the stream to query.
3536 Can't be specified with maxlen.
3537 limit: specifies the maximum number of entries to retrieve
3538 ref_policy: optional reference policy for consumer groups when trimming:
3539 - KEEPREF (default): When trimming, preserves references in consumer groups' PEL
3540 - DELREF: When trimming, removes all references from consumer groups' PEL
3541 - ACKED: When trimming, only removes entries acknowledged by all consumer groups
3543 For more information, see https://redis.io/commands/xadd
3544 """
3545 pieces: list[EncodableT] = []
3546 if maxlen is not None and minid is not None:
3547 raise DataError("Only one of ```maxlen``` or ```minid``` may be specified")
3549 if ref_policy is not None and ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
3550 raise DataError("XADD ref_policy must be one of: KEEPREF, DELREF, ACKED")
3552 if maxlen is not None:
3553 if not isinstance(maxlen, int) or maxlen < 0:
3554 raise DataError("XADD maxlen must be non-negative integer")
3555 pieces.append(b"MAXLEN")
3556 if approximate:
3557 pieces.append(b"~")
3558 pieces.append(str(maxlen))
3559 if minid is not None:
3560 pieces.append(b"MINID")
3561 if approximate:
3562 pieces.append(b"~")
3563 pieces.append(minid)
3564 if limit is not None:
3565 pieces.extend([b"LIMIT", limit])
3566 if nomkstream:
3567 pieces.append(b"NOMKSTREAM")
3568 if ref_policy is not None:
3569 pieces.append(ref_policy)
3570 pieces.append(id)
3571 if not isinstance(fields, dict) or len(fields) == 0:
3572 raise DataError("XADD fields must be a non-empty dict")
3573 for pair in fields.items():
3574 pieces.extend(pair)
3575 return self.execute_command("XADD", name, *pieces)
3577 def xautoclaim(
3578 self,
3579 name: KeyT,
3580 groupname: GroupT,
3581 consumername: ConsumerT,
3582 min_idle_time: int,
3583 start_id: StreamIdT = "0-0",
3584 count: Optional[int] = None,
3585 justid: bool = False,
3586 ) -> ResponseT:
3587 """
3588 Transfers ownership of pending stream entries that match the specified
3589 criteria. Conceptually, equivalent to calling XPENDING and then XCLAIM,
3590 but provides a more straightforward way to deal with message delivery
3591 failures via SCAN-like semantics.
3592 name: name of the stream.
3593 groupname: name of the consumer group.
3594 consumername: name of a consumer that claims the message.
3595 min_idle_time: filter messages that were idle less than this amount of
3596 milliseconds.
3597 start_id: filter messages with equal or greater ID.
3598 count: optional integer, upper limit of the number of entries that the
3599 command attempts to claim. Set to 100 by default.
3600 justid: optional boolean, false by default. Return just an array of IDs
3601 of messages successfully claimed, without returning the actual message
3603 For more information, see https://redis.io/commands/xautoclaim
3604 """
3605 try:
3606 if int(min_idle_time) < 0:
3607 raise DataError(
3608 "XAUTOCLAIM min_idle_time must be a nonnegative integer"
3609 )
3610 except TypeError:
3611 pass
3613 kwargs = {}
3614 pieces = [name, groupname, consumername, min_idle_time, start_id]
3616 try:
3617 if int(count) < 0:
3618 raise DataError("XPENDING count must be a integer >= 0")
3619 pieces.extend([b"COUNT", count])
3620 except TypeError:
3621 pass
3622 if justid:
3623 pieces.append(b"JUSTID")
3624 kwargs["parse_justid"] = True
3626 return self.execute_command("XAUTOCLAIM", *pieces, **kwargs)
3628 def xclaim(
3629 self,
3630 name: KeyT,
3631 groupname: GroupT,
3632 consumername: ConsumerT,
3633 min_idle_time: int,
3634 message_ids: Union[List[StreamIdT], Tuple[StreamIdT]],
3635 idle: Optional[int] = None,
3636 time: Optional[int] = None,
3637 retrycount: Optional[int] = None,
3638 force: bool = False,
3639 justid: bool = False,
3640 ) -> ResponseT:
3641 """
3642 Changes the ownership of a pending message.
3644 name: name of the stream.
3646 groupname: name of the consumer group.
3648 consumername: name of a consumer that claims the message.
3650 min_idle_time: filter messages that were idle less than this amount of
3651 milliseconds
3653 message_ids: non-empty list or tuple of message IDs to claim
3655 idle: optional. Set the idle time (last time it was delivered) of the
3656 message in ms
3658 time: optional integer. This is the same as idle but instead of a
3659 relative amount of milliseconds, it sets the idle time to a specific
3660 Unix time (in milliseconds).
3662 retrycount: optional integer. set the retry counter to the specified
3663 value. This counter is incremented every time a message is delivered
3664 again.
3666 force: optional boolean, false by default. Creates the pending message
3667 entry in the PEL even if certain specified IDs are not already in the
3668 PEL assigned to a different client.
3670 justid: optional boolean, false by default. Return just an array of IDs
3671 of messages successfully claimed, without returning the actual message
3673 For more information, see https://redis.io/commands/xclaim
3674 """
3675 if not isinstance(min_idle_time, int) or min_idle_time < 0:
3676 raise DataError("XCLAIM min_idle_time must be a non negative integer")
3677 if not isinstance(message_ids, (list, tuple)) or not message_ids:
3678 raise DataError(
3679 "XCLAIM message_ids must be a non empty list or "
3680 "tuple of message IDs to claim"
3681 )
3683 kwargs = {}
3684 pieces: list[EncodableT] = [name, groupname, consumername, str(min_idle_time)]
3685 pieces.extend(list(message_ids))
3687 if idle is not None:
3688 if not isinstance(idle, int):
3689 raise DataError("XCLAIM idle must be an integer")
3690 pieces.extend((b"IDLE", str(idle)))
3691 if time is not None:
3692 if not isinstance(time, int):
3693 raise DataError("XCLAIM time must be an integer")
3694 pieces.extend((b"TIME", str(time)))
3695 if retrycount is not None:
3696 if not isinstance(retrycount, int):
3697 raise DataError("XCLAIM retrycount must be an integer")
3698 pieces.extend((b"RETRYCOUNT", str(retrycount)))
3700 if force:
3701 if not isinstance(force, bool):
3702 raise DataError("XCLAIM force must be a boolean")
3703 pieces.append(b"FORCE")
3704 if justid:
3705 if not isinstance(justid, bool):
3706 raise DataError("XCLAIM justid must be a boolean")
3707 pieces.append(b"JUSTID")
3708 kwargs["parse_justid"] = True
3709 return self.execute_command("XCLAIM", *pieces, **kwargs)
3711 def xdel(self, name: KeyT, *ids: StreamIdT) -> ResponseT:
3712 """
3713 Deletes one or more messages from a stream.
3715 Args:
3716 name: name of the stream.
3717 *ids: message ids to delete.
3719 For more information, see https://redis.io/commands/xdel
3720 """
3721 return self.execute_command("XDEL", name, *ids)
3723 def xdelex(
3724 self,
3725 name: KeyT,
3726 *ids: StreamIdT,
3727 ref_policy: Literal["KEEPREF", "DELREF", "ACKED"] = "KEEPREF",
3728 ) -> ResponseT:
3729 """
3730 Extended version of XDEL that provides more control over how message entries
3731 are deleted concerning consumer groups.
3732 """
3733 if not ids:
3734 raise DataError("XDELEX requires at least one message ID")
3736 if ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
3737 raise DataError("XDELEX ref_policy must be one of: KEEPREF, DELREF, ACKED")
3739 pieces = [name, ref_policy, "IDS", len(ids)]
3740 pieces.extend(ids)
3741 return self.execute_command("XDELEX", *pieces)
3743 def xgroup_create(
3744 self,
3745 name: KeyT,
3746 groupname: GroupT,
3747 id: StreamIdT = "$",
3748 mkstream: bool = False,
3749 entries_read: Optional[int] = None,
3750 ) -> ResponseT:
3751 """
3752 Create a new consumer group associated with a stream.
3753 name: name of the stream.
3754 groupname: name of the consumer group.
3755 id: ID of the last item in the stream to consider already delivered.
3757 For more information, see https://redis.io/commands/xgroup-create
3758 """
3759 pieces: list[EncodableT] = ["XGROUP CREATE", name, groupname, id]
3760 if mkstream:
3761 pieces.append(b"MKSTREAM")
3762 if entries_read is not None:
3763 pieces.extend(["ENTRIESREAD", entries_read])
3765 return self.execute_command(*pieces)
3767 def xgroup_delconsumer(
3768 self, name: KeyT, groupname: GroupT, consumername: ConsumerT
3769 ) -> ResponseT:
3770 """
3771 Remove a specific consumer from a consumer group.
3772 Returns the number of pending messages that the consumer had before it
3773 was deleted.
3774 name: name of the stream.
3775 groupname: name of the consumer group.
3776 consumername: name of consumer to delete
3778 For more information, see https://redis.io/commands/xgroup-delconsumer
3779 """
3780 return self.execute_command("XGROUP DELCONSUMER", name, groupname, consumername)
3782 def xgroup_destroy(self, name: KeyT, groupname: GroupT) -> ResponseT:
3783 """
3784 Destroy a consumer group.
3785 name: name of the stream.
3786 groupname: name of the consumer group.
3788 For more information, see https://redis.io/commands/xgroup-destroy
3789 """
3790 return self.execute_command("XGROUP DESTROY", name, groupname)
3792 def xgroup_createconsumer(
3793 self, name: KeyT, groupname: GroupT, consumername: ConsumerT
3794 ) -> ResponseT:
3795 """
3796 Consumers in a consumer group are auto-created every time a new
3797 consumer name is mentioned by some command.
3798 They can be explicitly created by using this command.
3799 name: name of the stream.
3800 groupname: name of the consumer group.
3801 consumername: name of consumer to create.
3803 See: https://redis.io/commands/xgroup-createconsumer
3804 """
3805 return self.execute_command(
3806 "XGROUP CREATECONSUMER", name, groupname, consumername
3807 )
3809 def xgroup_setid(
3810 self,
3811 name: KeyT,
3812 groupname: GroupT,
3813 id: StreamIdT,
3814 entries_read: Optional[int] = None,
3815 ) -> ResponseT:
3816 """
3817 Set the consumer group last delivered ID to something else.
3818 name: name of the stream.
3819 groupname: name of the consumer group.
3820 id: ID of the last item in the stream to consider already delivered.
3822 For more information, see https://redis.io/commands/xgroup-setid
3823 """
3824 pieces = [name, groupname, id]
3825 if entries_read is not None:
3826 pieces.extend(["ENTRIESREAD", entries_read])
3827 return self.execute_command("XGROUP SETID", *pieces)
3829 def xinfo_consumers(self, name: KeyT, groupname: GroupT) -> ResponseT:
3830 """
3831 Returns general information about the consumers in the group.
3832 name: name of the stream.
3833 groupname: name of the consumer group.
3835 For more information, see https://redis.io/commands/xinfo-consumers
3836 """
3837 return self.execute_command("XINFO CONSUMERS", name, groupname)
3839 def xinfo_groups(self, name: KeyT) -> ResponseT:
3840 """
3841 Returns general information about the consumer groups of the stream.
3842 name: name of the stream.
3844 For more information, see https://redis.io/commands/xinfo-groups
3845 """
3846 return self.execute_command("XINFO GROUPS", name)
3848 def xinfo_stream(self, name: KeyT, full: bool = False) -> ResponseT:
3849 """
3850 Returns general information about the stream.
3851 name: name of the stream.
3852 full: optional boolean, false by default. Return full summary
3854 For more information, see https://redis.io/commands/xinfo-stream
3855 """
3856 pieces = [name]
3857 options = {}
3858 if full:
3859 pieces.append(b"FULL")
3860 options = {"full": full}
3861 return self.execute_command("XINFO STREAM", *pieces, **options)
3863 def xlen(self, name: KeyT) -> ResponseT:
3864 """
3865 Returns the number of elements in a given stream.
3867 For more information, see https://redis.io/commands/xlen
3868 """
3869 return self.execute_command("XLEN", name, keys=[name])
3871 def xpending(self, name: KeyT, groupname: GroupT) -> ResponseT:
3872 """
3873 Returns information about pending messages of a group.
3874 name: name of the stream.
3875 groupname: name of the consumer group.
3877 For more information, see https://redis.io/commands/xpending
3878 """
3879 return self.execute_command("XPENDING", name, groupname, keys=[name])
3881 def xpending_range(
3882 self,
3883 name: KeyT,
3884 groupname: GroupT,
3885 min: StreamIdT,
3886 max: StreamIdT,
3887 count: int,
3888 consumername: Union[ConsumerT, None] = None,
3889 idle: Optional[int] = None,
3890 ) -> ResponseT:
3891 """
3892 Returns information about pending messages, in a range.
3894 name: name of the stream.
3895 groupname: name of the consumer group.
3896 idle: available from version 6.2. filter entries by their
3897 idle-time, given in milliseconds (optional).
3898 min: minimum stream ID.
3899 max: maximum stream ID.
3900 count: number of messages to return
3901 consumername: name of a consumer to filter by (optional).
3902 """
3903 if {min, max, count} == {None}:
3904 if idle is not None or consumername is not None:
3905 raise DataError(
3906 "if XPENDING is provided with idle time"
3907 " or consumername, it must be provided"
3908 " with min, max and count parameters"
3909 )
3910 return self.xpending(name, groupname)
3912 pieces = [name, groupname]
3913 if min is None or max is None or count is None:
3914 raise DataError(
3915 "XPENDING must be provided with min, max "
3916 "and count parameters, or none of them."
3917 )
3918 # idle
3919 try:
3920 if int(idle) < 0:
3921 raise DataError("XPENDING idle must be a integer >= 0")
3922 pieces.extend(["IDLE", idle])
3923 except TypeError:
3924 pass
3925 # count
3926 try:
3927 if int(count) < 0:
3928 raise DataError("XPENDING count must be a integer >= 0")
3929 pieces.extend([min, max, count])
3930 except TypeError:
3931 pass
3932 # consumername
3933 if consumername:
3934 pieces.append(consumername)
3936 return self.execute_command("XPENDING", *pieces, parse_detail=True)
3938 def xrange(
3939 self,
3940 name: KeyT,
3941 min: StreamIdT = "-",
3942 max: StreamIdT = "+",
3943 count: Optional[int] = None,
3944 ) -> ResponseT:
3945 """
3946 Read stream values within an interval.
3948 name: name of the stream.
3950 start: first stream ID. defaults to '-',
3951 meaning the earliest available.
3953 finish: last stream ID. defaults to '+',
3954 meaning the latest available.
3956 count: if set, only return this many items, beginning with the
3957 earliest available.
3959 For more information, see https://redis.io/commands/xrange
3960 """
3961 pieces = [min, max]
3962 if count is not None:
3963 if not isinstance(count, int) or count < 1:
3964 raise DataError("XRANGE count must be a positive integer")
3965 pieces.append(b"COUNT")
3966 pieces.append(str(count))
3968 return self.execute_command("XRANGE", name, *pieces, keys=[name])
3970 def xread(
3971 self,
3972 streams: Dict[KeyT, StreamIdT],
3973 count: Optional[int] = None,
3974 block: Optional[int] = None,
3975 ) -> ResponseT:
3976 """
3977 Block and monitor multiple streams for new data.
3979 streams: a dict of stream names to stream IDs, where
3980 IDs indicate the last ID already seen.
3982 count: if set, only return this many items, beginning with the
3983 earliest available.
3985 block: number of milliseconds to wait, if nothing already present.
3987 For more information, see https://redis.io/commands/xread
3988 """
3989 pieces = []
3990 if block is not None:
3991 if not isinstance(block, int) or block < 0:
3992 raise DataError("XREAD block must be a non-negative integer")
3993 pieces.append(b"BLOCK")
3994 pieces.append(str(block))
3995 if count is not None:
3996 if not isinstance(count, int) or count < 1:
3997 raise DataError("XREAD count must be a positive integer")
3998 pieces.append(b"COUNT")
3999 pieces.append(str(count))
4000 if not isinstance(streams, dict) or len(streams) == 0:
4001 raise DataError("XREAD streams must be a non empty dict")
4002 pieces.append(b"STREAMS")
4003 keys, values = zip(*streams.items())
4004 pieces.extend(keys)
4005 pieces.extend(values)
4006 return self.execute_command("XREAD", *pieces, keys=keys)
4008 def xreadgroup(
4009 self,
4010 groupname: str,
4011 consumername: str,
4012 streams: Dict[KeyT, StreamIdT],
4013 count: Optional[int] = None,
4014 block: Optional[int] = None,
4015 noack: bool = False,
4016 ) -> ResponseT:
4017 """
4018 Read from a stream via a consumer group.
4020 groupname: name of the consumer group.
4022 consumername: name of the requesting consumer.
4024 streams: a dict of stream names to stream IDs, where
4025 IDs indicate the last ID already seen.
4027 count: if set, only return this many items, beginning with the
4028 earliest available.
4030 block: number of milliseconds to wait, if nothing already present.
4031 noack: do not add messages to the PEL
4033 For more information, see https://redis.io/commands/xreadgroup
4034 """
4035 pieces: list[EncodableT] = [b"GROUP", groupname, consumername]
4036 if count is not None:
4037 if not isinstance(count, int) or count < 1:
4038 raise DataError("XREADGROUP count must be a positive integer")
4039 pieces.append(b"COUNT")
4040 pieces.append(str(count))
4041 if block is not None:
4042 if not isinstance(block, int) or block < 0:
4043 raise DataError("XREADGROUP block must be a non-negative integer")
4044 pieces.append(b"BLOCK")
4045 pieces.append(str(block))
4046 if noack:
4047 pieces.append(b"NOACK")
4048 if not isinstance(streams, dict) or len(streams) == 0:
4049 raise DataError("XREADGROUP streams must be a non empty dict")
4050 pieces.append(b"STREAMS")
4051 pieces.extend(streams.keys())
4052 pieces.extend(streams.values())
4053 return self.execute_command("XREADGROUP", *pieces)
4055 def xrevrange(
4056 self,
4057 name: KeyT,
4058 max: StreamIdT = "+",
4059 min: StreamIdT = "-",
4060 count: Optional[int] = None,
4061 ) -> ResponseT:
4062 """
4063 Read stream values within an interval, in reverse order.
4065 name: name of the stream
4067 start: first stream ID. defaults to '+',
4068 meaning the latest available.
4070 finish: last stream ID. defaults to '-',
4071 meaning the earliest available.
4073 count: if set, only return this many items, beginning with the
4074 latest available.
4076 For more information, see https://redis.io/commands/xrevrange
4077 """
4078 pieces: list[EncodableT] = [max, min]
4079 if count is not None:
4080 if not isinstance(count, int) or count < 1:
4081 raise DataError("XREVRANGE count must be a positive integer")
4082 pieces.append(b"COUNT")
4083 pieces.append(str(count))
4085 return self.execute_command("XREVRANGE", name, *pieces, keys=[name])
4087 def xtrim(
4088 self,
4089 name: KeyT,
4090 maxlen: Optional[int] = None,
4091 approximate: bool = True,
4092 minid: Union[StreamIdT, None] = None,
4093 limit: Optional[int] = None,
4094 ref_policy: Optional[Literal["KEEPREF", "DELREF", "ACKED"]] = None,
4095 ) -> ResponseT:
4096 """
4097 Trims old messages from a stream.
4098 name: name of the stream.
4099 maxlen: truncate old stream messages beyond this size
4100 Can't be specified with minid.
4101 approximate: actual stream length may be slightly more than maxlen
4102 minid: the minimum id in the stream to query
4103 Can't be specified with maxlen.
4104 limit: specifies the maximum number of entries to retrieve
4105 ref_policy: optional reference policy for consumer groups:
4106 - KEEPREF (default): Trims entries but preserves references in consumer groups' PEL
4107 - DELREF: Trims entries and removes all references from consumer groups' PEL
4108 - ACKED: Only trims entries that were read and acknowledged by all consumer groups
4110 For more information, see https://redis.io/commands/xtrim
4111 """
4112 pieces: list[EncodableT] = []
4113 if maxlen is not None and minid is not None:
4114 raise DataError("Only one of ``maxlen`` or ``minid`` may be specified")
4116 if maxlen is None and minid is None:
4117 raise DataError("One of ``maxlen`` or ``minid`` must be specified")
4119 if ref_policy is not None and ref_policy not in {"KEEPREF", "DELREF", "ACKED"}:
4120 raise DataError("XTRIM ref_policy must be one of: KEEPREF, DELREF, ACKED")
4122 if maxlen is not None:
4123 pieces.append(b"MAXLEN")
4124 if minid is not None:
4125 pieces.append(b"MINID")
4126 if approximate:
4127 pieces.append(b"~")
4128 if maxlen is not None:
4129 pieces.append(maxlen)
4130 if minid is not None:
4131 pieces.append(minid)
4132 if limit is not None:
4133 pieces.append(b"LIMIT")
4134 pieces.append(limit)
4135 if ref_policy is not None:
4136 pieces.append(ref_policy)
4138 return self.execute_command("XTRIM", name, *pieces)
4141AsyncStreamCommands = StreamCommands
4144class SortedSetCommands(CommandsProtocol):
4145 """
4146 Redis commands for Sorted Sets data type.
4147 see: https://redis.io/topics/data-types-intro#redis-sorted-sets
4148 """
4150 def zadd(
4151 self,
4152 name: KeyT,
4153 mapping: Mapping[AnyKeyT, EncodableT],
4154 nx: bool = False,
4155 xx: bool = False,
4156 ch: bool = False,
4157 incr: bool = False,
4158 gt: bool = False,
4159 lt: bool = False,
4160 ) -> ResponseT:
4161 """
4162 Set any number of element-name, score pairs to the key ``name``. Pairs
4163 are specified as a dict of element-names keys to score values.
4165 ``nx`` forces ZADD to only create new elements and not to update
4166 scores for elements that already exist.
4168 ``xx`` forces ZADD to only update scores of elements that already
4169 exist. New elements will not be added.
4171 ``ch`` modifies the return value to be the numbers of elements changed.
4172 Changed elements include new elements that were added and elements
4173 whose scores changed.
4175 ``incr`` modifies ZADD to behave like ZINCRBY. In this mode only a
4176 single element/score pair can be specified and the score is the amount
4177 the existing score will be incremented by. When using this mode the
4178 return value of ZADD will be the new score of the element.
4180 ``lt`` only updates existing elements if the new score is less than
4181 the current score. This flag doesn't prevent adding new elements.
4183 ``gt`` only updates existing elements if the new score is greater than
4184 the current score. This flag doesn't prevent adding new elements.
4186 The return value of ZADD varies based on the mode specified. With no
4187 options, ZADD returns the number of new elements added to the sorted
4188 set.
4190 ``nx``, ``lt``, and ``gt`` are mutually exclusive options.
4192 See: https://redis.io/commands/ZADD
4193 """
4194 if not mapping:
4195 raise DataError("ZADD requires at least one element/score pair")
4196 if nx and xx:
4197 raise DataError("ZADD allows either 'nx' or 'xx', not both")
4198 if gt and lt:
4199 raise DataError("ZADD allows either 'gt' or 'lt', not both")
4200 if incr and len(mapping) != 1:
4201 raise DataError(
4202 "ZADD option 'incr' only works when passing a single element/score pair"
4203 )
4204 if nx and (gt or lt):
4205 raise DataError("Only one of 'nx', 'lt', or 'gr' may be defined.")
4207 pieces: list[EncodableT] = []
4208 options = {}
4209 if nx:
4210 pieces.append(b"NX")
4211 if xx:
4212 pieces.append(b"XX")
4213 if ch:
4214 pieces.append(b"CH")
4215 if incr:
4216 pieces.append(b"INCR")
4217 options["as_score"] = True
4218 if gt:
4219 pieces.append(b"GT")
4220 if lt:
4221 pieces.append(b"LT")
4222 for pair in mapping.items():
4223 pieces.append(pair[1])
4224 pieces.append(pair[0])
4225 return self.execute_command("ZADD", name, *pieces, **options)
4227 def zcard(self, name: KeyT) -> ResponseT:
4228 """
4229 Return the number of elements in the sorted set ``name``
4231 For more information, see https://redis.io/commands/zcard
4232 """
4233 return self.execute_command("ZCARD", name, keys=[name])
4235 def zcount(self, name: KeyT, min: ZScoreBoundT, max: ZScoreBoundT) -> ResponseT:
4236 """
4237 Returns the number of elements in the sorted set at key ``name`` with
4238 a score between ``min`` and ``max``.
4240 For more information, see https://redis.io/commands/zcount
4241 """
4242 return self.execute_command("ZCOUNT", name, min, max, keys=[name])
4244 def zdiff(self, keys: KeysT, withscores: bool = False) -> ResponseT:
4245 """
4246 Returns the difference between the first and all successive input
4247 sorted sets provided in ``keys``.
4249 For more information, see https://redis.io/commands/zdiff
4250 """
4251 pieces = [len(keys), *keys]
4252 if withscores:
4253 pieces.append("WITHSCORES")
4254 return self.execute_command("ZDIFF", *pieces, keys=keys)
4256 def zdiffstore(self, dest: KeyT, keys: KeysT) -> ResponseT:
4257 """
4258 Computes the difference between the first and all successive input
4259 sorted sets provided in ``keys`` and stores the result in ``dest``.
4261 For more information, see https://redis.io/commands/zdiffstore
4262 """
4263 pieces = [len(keys), *keys]
4264 return self.execute_command("ZDIFFSTORE", dest, *pieces)
4266 def zincrby(self, name: KeyT, amount: float, value: EncodableT) -> ResponseT:
4267 """
4268 Increment the score of ``value`` in sorted set ``name`` by ``amount``
4270 For more information, see https://redis.io/commands/zincrby
4271 """
4272 return self.execute_command("ZINCRBY", name, amount, value)
4274 def zinter(
4275 self, keys: KeysT, aggregate: Optional[str] = None, withscores: bool = False
4276 ) -> ResponseT:
4277 """
4278 Return the intersect of multiple sorted sets specified by ``keys``.
4279 With the ``aggregate`` option, it is possible to specify how the
4280 results of the union are aggregated. This option defaults to SUM,
4281 where the score of an element is summed across the inputs where it
4282 exists. When this option is set to either MIN or MAX, the resulting
4283 set will contain the minimum or maximum score of an element across
4284 the inputs where it exists.
4286 For more information, see https://redis.io/commands/zinter
4287 """
4288 return self._zaggregate("ZINTER", None, keys, aggregate, withscores=withscores)
4290 def zinterstore(
4291 self,
4292 dest: KeyT,
4293 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4294 aggregate: Optional[str] = None,
4295 ) -> ResponseT:
4296 """
4297 Intersect multiple sorted sets specified by ``keys`` into a new
4298 sorted set, ``dest``. Scores in the destination will be aggregated
4299 based on the ``aggregate``. This option defaults to SUM, where the
4300 score of an element is summed across the inputs where it exists.
4301 When this option is set to either MIN or MAX, the resulting set will
4302 contain the minimum or maximum score of an element across the inputs
4303 where it exists.
4305 For more information, see https://redis.io/commands/zinterstore
4306 """
4307 return self._zaggregate("ZINTERSTORE", dest, keys, aggregate)
4309 def zintercard(
4310 self, numkeys: int, keys: List[str], limit: int = 0
4311 ) -> Union[Awaitable[int], int]:
4312 """
4313 Return the cardinality of the intersect of multiple sorted sets
4314 specified by ``keys``.
4315 When LIMIT provided (defaults to 0 and means unlimited), if the intersection
4316 cardinality reaches limit partway through the computation, the algorithm will
4317 exit and yield limit as the cardinality
4319 For more information, see https://redis.io/commands/zintercard
4320 """
4321 args = [numkeys, *keys, "LIMIT", limit]
4322 return self.execute_command("ZINTERCARD", *args, keys=keys)
4324 def zlexcount(self, name, min, max):
4325 """
4326 Return the number of items in the sorted set ``name`` between the
4327 lexicographical range ``min`` and ``max``.
4329 For more information, see https://redis.io/commands/zlexcount
4330 """
4331 return self.execute_command("ZLEXCOUNT", name, min, max, keys=[name])
4333 def zpopmax(self, name: KeyT, count: Optional[int] = None) -> ResponseT:
4334 """
4335 Remove and return up to ``count`` members with the highest scores
4336 from the sorted set ``name``.
4338 For more information, see https://redis.io/commands/zpopmax
4339 """
4340 args = (count is not None) and [count] or []
4341 options = {"withscores": True}
4342 return self.execute_command("ZPOPMAX", name, *args, **options)
4344 def zpopmin(self, name: KeyT, count: Optional[int] = None) -> ResponseT:
4345 """
4346 Remove and return up to ``count`` members with the lowest scores
4347 from the sorted set ``name``.
4349 For more information, see https://redis.io/commands/zpopmin
4350 """
4351 args = (count is not None) and [count] or []
4352 options = {"withscores": True}
4353 return self.execute_command("ZPOPMIN", name, *args, **options)
4355 def zrandmember(
4356 self, key: KeyT, count: Optional[int] = None, withscores: bool = False
4357 ) -> ResponseT:
4358 """
4359 Return a random element from the sorted set value stored at key.
4361 ``count`` if the argument is positive, return an array of distinct
4362 fields. If called with a negative count, the behavior changes and
4363 the command is allowed to return the same field multiple times.
4364 In this case, the number of returned fields is the absolute value
4365 of the specified count.
4367 ``withscores`` The optional WITHSCORES modifier changes the reply so it
4368 includes the respective scores of the randomly selected elements from
4369 the sorted set.
4371 For more information, see https://redis.io/commands/zrandmember
4372 """
4373 params = []
4374 if count is not None:
4375 params.append(count)
4376 if withscores:
4377 params.append("WITHSCORES")
4379 return self.execute_command("ZRANDMEMBER", key, *params)
4381 def bzpopmax(self, keys: KeysT, timeout: TimeoutSecT = 0) -> ResponseT:
4382 """
4383 ZPOPMAX a value off of the first non-empty sorted set
4384 named in the ``keys`` list.
4386 If none of the sorted sets in ``keys`` has a value to ZPOPMAX,
4387 then block for ``timeout`` seconds, or until a member gets added
4388 to one of the sorted sets.
4390 If timeout is 0, then block indefinitely.
4392 For more information, see https://redis.io/commands/bzpopmax
4393 """
4394 if timeout is None:
4395 timeout = 0
4396 keys = list_or_args(keys, None)
4397 keys.append(timeout)
4398 return self.execute_command("BZPOPMAX", *keys)
4400 def bzpopmin(self, keys: KeysT, timeout: TimeoutSecT = 0) -> ResponseT:
4401 """
4402 ZPOPMIN a value off of the first non-empty sorted set
4403 named in the ``keys`` list.
4405 If none of the sorted sets in ``keys`` has a value to ZPOPMIN,
4406 then block for ``timeout`` seconds, or until a member gets added
4407 to one of the sorted sets.
4409 If timeout is 0, then block indefinitely.
4411 For more information, see https://redis.io/commands/bzpopmin
4412 """
4413 if timeout is None:
4414 timeout = 0
4415 keys: list[EncodableT] = list_or_args(keys, None)
4416 keys.append(timeout)
4417 return self.execute_command("BZPOPMIN", *keys)
4419 def zmpop(
4420 self,
4421 num_keys: int,
4422 keys: List[str],
4423 min: Optional[bool] = False,
4424 max: Optional[bool] = False,
4425 count: Optional[int] = 1,
4426 ) -> Union[Awaitable[list], list]:
4427 """
4428 Pop ``count`` values (default 1) off of the first non-empty sorted set
4429 named in the ``keys`` list.
4430 For more information, see https://redis.io/commands/zmpop
4431 """
4432 args = [num_keys] + keys
4433 if (min and max) or (not min and not max):
4434 raise DataError
4435 elif min:
4436 args.append("MIN")
4437 else:
4438 args.append("MAX")
4439 if count != 1:
4440 args.extend(["COUNT", count])
4442 return self.execute_command("ZMPOP", *args)
4444 def bzmpop(
4445 self,
4446 timeout: float,
4447 numkeys: int,
4448 keys: List[str],
4449 min: Optional[bool] = False,
4450 max: Optional[bool] = False,
4451 count: Optional[int] = 1,
4452 ) -> Optional[list]:
4453 """
4454 Pop ``count`` values (default 1) off of the first non-empty sorted set
4455 named in the ``keys`` list.
4457 If none of the sorted sets in ``keys`` has a value to pop,
4458 then block for ``timeout`` seconds, or until a member gets added
4459 to one of the sorted sets.
4461 If timeout is 0, then block indefinitely.
4463 For more information, see https://redis.io/commands/bzmpop
4464 """
4465 args = [timeout, numkeys, *keys]
4466 if (min and max) or (not min and not max):
4467 raise DataError("Either min or max, but not both must be set")
4468 elif min:
4469 args.append("MIN")
4470 else:
4471 args.append("MAX")
4472 args.extend(["COUNT", count])
4474 return self.execute_command("BZMPOP", *args)
4476 def _zrange(
4477 self,
4478 command,
4479 dest: Union[KeyT, None],
4480 name: KeyT,
4481 start: int,
4482 end: int,
4483 desc: bool = False,
4484 byscore: bool = False,
4485 bylex: bool = False,
4486 withscores: bool = False,
4487 score_cast_func: Union[type, Callable, None] = float,
4488 offset: Optional[int] = None,
4489 num: Optional[int] = None,
4490 ) -> ResponseT:
4491 if byscore and bylex:
4492 raise DataError("``byscore`` and ``bylex`` can not be specified together.")
4493 if (offset is not None and num is None) or (num is not None and offset is None):
4494 raise DataError("``offset`` and ``num`` must both be specified.")
4495 if bylex and withscores:
4496 raise DataError(
4497 "``withscores`` not supported in combination with ``bylex``."
4498 )
4499 pieces = [command]
4500 if dest:
4501 pieces.append(dest)
4502 pieces.extend([name, start, end])
4503 if byscore:
4504 pieces.append("BYSCORE")
4505 if bylex:
4506 pieces.append("BYLEX")
4507 if desc:
4508 pieces.append("REV")
4509 if offset is not None and num is not None:
4510 pieces.extend(["LIMIT", offset, num])
4511 if withscores:
4512 pieces.append("WITHSCORES")
4513 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4514 options["keys"] = [name]
4515 return self.execute_command(*pieces, **options)
4517 def zrange(
4518 self,
4519 name: KeyT,
4520 start: int,
4521 end: int,
4522 desc: bool = False,
4523 withscores: bool = False,
4524 score_cast_func: Union[type, Callable] = float,
4525 byscore: bool = False,
4526 bylex: bool = False,
4527 offset: Optional[int] = None,
4528 num: Optional[int] = None,
4529 ) -> ResponseT:
4530 """
4531 Return a range of values from sorted set ``name`` between
4532 ``start`` and ``end`` sorted in ascending order.
4534 ``start`` and ``end`` can be negative, indicating the end of the range.
4536 ``desc`` a boolean indicating whether to sort the results in reversed
4537 order.
4539 ``withscores`` indicates to return the scores along with the values.
4540 The return type is a list of (value, score) pairs.
4542 ``score_cast_func`` a callable used to cast the score return value.
4544 ``byscore`` when set to True, returns the range of elements from the
4545 sorted set having scores equal or between ``start`` and ``end``.
4547 ``bylex`` when set to True, returns the range of elements from the
4548 sorted set between the ``start`` and ``end`` lexicographical closed
4549 range intervals.
4550 Valid ``start`` and ``end`` must start with ( or [, in order to specify
4551 whether the range interval is exclusive or inclusive, respectively.
4553 ``offset`` and ``num`` are specified, then return a slice of the range.
4554 Can't be provided when using ``bylex``.
4556 For more information, see https://redis.io/commands/zrange
4557 """
4558 # Need to support ``desc`` also when using old redis version
4559 # because it was supported in 3.5.3 (of redis-py)
4560 if not byscore and not bylex and (offset is None and num is None) and desc:
4561 return self.zrevrange(name, start, end, withscores, score_cast_func)
4563 return self._zrange(
4564 "ZRANGE",
4565 None,
4566 name,
4567 start,
4568 end,
4569 desc,
4570 byscore,
4571 bylex,
4572 withscores,
4573 score_cast_func,
4574 offset,
4575 num,
4576 )
4578 def zrevrange(
4579 self,
4580 name: KeyT,
4581 start: int,
4582 end: int,
4583 withscores: bool = False,
4584 score_cast_func: Union[type, Callable] = float,
4585 ) -> ResponseT:
4586 """
4587 Return a range of values from sorted set ``name`` between
4588 ``start`` and ``end`` sorted in descending order.
4590 ``start`` and ``end`` can be negative, indicating the end of the range.
4592 ``withscores`` indicates to return the scores along with the values
4593 The return type is a list of (value, score) pairs
4595 ``score_cast_func`` a callable used to cast the score return value
4597 For more information, see https://redis.io/commands/zrevrange
4598 """
4599 pieces = ["ZREVRANGE", name, start, end]
4600 if withscores:
4601 pieces.append(b"WITHSCORES")
4602 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4603 options["keys"] = name
4604 return self.execute_command(*pieces, **options)
4606 def zrangestore(
4607 self,
4608 dest: KeyT,
4609 name: KeyT,
4610 start: int,
4611 end: int,
4612 byscore: bool = False,
4613 bylex: bool = False,
4614 desc: bool = False,
4615 offset: Optional[int] = None,
4616 num: Optional[int] = None,
4617 ) -> ResponseT:
4618 """
4619 Stores in ``dest`` the result of a range of values from sorted set
4620 ``name`` between ``start`` and ``end`` sorted in ascending order.
4622 ``start`` and ``end`` can be negative, indicating the end of the range.
4624 ``byscore`` when set to True, returns the range of elements from the
4625 sorted set having scores equal or between ``start`` and ``end``.
4627 ``bylex`` when set to True, returns the range of elements from the
4628 sorted set between the ``start`` and ``end`` lexicographical closed
4629 range intervals.
4630 Valid ``start`` and ``end`` must start with ( or [, in order to specify
4631 whether the range interval is exclusive or inclusive, respectively.
4633 ``desc`` a boolean indicating whether to sort the results in reversed
4634 order.
4636 ``offset`` and ``num`` are specified, then return a slice of the range.
4637 Can't be provided when using ``bylex``.
4639 For more information, see https://redis.io/commands/zrangestore
4640 """
4641 return self._zrange(
4642 "ZRANGESTORE",
4643 dest,
4644 name,
4645 start,
4646 end,
4647 desc,
4648 byscore,
4649 bylex,
4650 False,
4651 None,
4652 offset,
4653 num,
4654 )
4656 def zrangebylex(
4657 self,
4658 name: KeyT,
4659 min: EncodableT,
4660 max: EncodableT,
4661 start: Optional[int] = None,
4662 num: Optional[int] = None,
4663 ) -> ResponseT:
4664 """
4665 Return the lexicographical range of values from sorted set ``name``
4666 between ``min`` and ``max``.
4668 If ``start`` and ``num`` are specified, then return a slice of the
4669 range.
4671 For more information, see https://redis.io/commands/zrangebylex
4672 """
4673 if (start is not None and num is None) or (num is not None and start is None):
4674 raise DataError("``start`` and ``num`` must both be specified")
4675 pieces = ["ZRANGEBYLEX", name, min, max]
4676 if start is not None and num is not None:
4677 pieces.extend([b"LIMIT", start, num])
4678 return self.execute_command(*pieces, keys=[name])
4680 def zrevrangebylex(
4681 self,
4682 name: KeyT,
4683 max: EncodableT,
4684 min: EncodableT,
4685 start: Optional[int] = None,
4686 num: Optional[int] = None,
4687 ) -> ResponseT:
4688 """
4689 Return the reversed lexicographical range of values from sorted set
4690 ``name`` between ``max`` and ``min``.
4692 If ``start`` and ``num`` are specified, then return a slice of the
4693 range.
4695 For more information, see https://redis.io/commands/zrevrangebylex
4696 """
4697 if (start is not None and num is None) or (num is not None and start is None):
4698 raise DataError("``start`` and ``num`` must both be specified")
4699 pieces = ["ZREVRANGEBYLEX", name, max, min]
4700 if start is not None and num is not None:
4701 pieces.extend(["LIMIT", start, num])
4702 return self.execute_command(*pieces, keys=[name])
4704 def zrangebyscore(
4705 self,
4706 name: KeyT,
4707 min: ZScoreBoundT,
4708 max: ZScoreBoundT,
4709 start: Optional[int] = None,
4710 num: Optional[int] = None,
4711 withscores: bool = False,
4712 score_cast_func: Union[type, Callable] = float,
4713 ) -> ResponseT:
4714 """
4715 Return a range of values from the sorted set ``name`` with scores
4716 between ``min`` and ``max``.
4718 If ``start`` and ``num`` are specified, then return a slice
4719 of the range.
4721 ``withscores`` indicates to return the scores along with the values.
4722 The return type is a list of (value, score) pairs
4724 `score_cast_func`` a callable used to cast the score return value
4726 For more information, see https://redis.io/commands/zrangebyscore
4727 """
4728 if (start is not None and num is None) or (num is not None and start is None):
4729 raise DataError("``start`` and ``num`` must both be specified")
4730 pieces = ["ZRANGEBYSCORE", name, min, max]
4731 if start is not None and num is not None:
4732 pieces.extend(["LIMIT", start, num])
4733 if withscores:
4734 pieces.append("WITHSCORES")
4735 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4736 options["keys"] = [name]
4737 return self.execute_command(*pieces, **options)
4739 def zrevrangebyscore(
4740 self,
4741 name: KeyT,
4742 max: ZScoreBoundT,
4743 min: ZScoreBoundT,
4744 start: Optional[int] = None,
4745 num: Optional[int] = None,
4746 withscores: bool = False,
4747 score_cast_func: Union[type, Callable] = float,
4748 ):
4749 """
4750 Return a range of values from the sorted set ``name`` with scores
4751 between ``min`` and ``max`` in descending order.
4753 If ``start`` and ``num`` are specified, then return a slice
4754 of the range.
4756 ``withscores`` indicates to return the scores along with the values.
4757 The return type is a list of (value, score) pairs
4759 ``score_cast_func`` a callable used to cast the score return value
4761 For more information, see https://redis.io/commands/zrevrangebyscore
4762 """
4763 if (start is not None and num is None) or (num is not None and start is None):
4764 raise DataError("``start`` and ``num`` must both be specified")
4765 pieces = ["ZREVRANGEBYSCORE", name, max, min]
4766 if start is not None and num is not None:
4767 pieces.extend(["LIMIT", start, num])
4768 if withscores:
4769 pieces.append("WITHSCORES")
4770 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4771 options["keys"] = [name]
4772 return self.execute_command(*pieces, **options)
4774 def zrank(
4775 self,
4776 name: KeyT,
4777 value: EncodableT,
4778 withscore: bool = False,
4779 score_cast_func: Union[type, Callable] = float,
4780 ) -> ResponseT:
4781 """
4782 Returns a 0-based value indicating the rank of ``value`` in sorted set
4783 ``name``.
4784 The optional WITHSCORE argument supplements the command's
4785 reply with the score of the element returned.
4787 ``score_cast_func`` a callable used to cast the score return value
4789 For more information, see https://redis.io/commands/zrank
4790 """
4791 pieces = ["ZRANK", name, value]
4792 if withscore:
4793 pieces.append("WITHSCORE")
4795 options = {"withscore": withscore, "score_cast_func": score_cast_func}
4797 return self.execute_command(*pieces, **options)
4799 def zrem(self, name: KeyT, *values: FieldT) -> ResponseT:
4800 """
4801 Remove member ``values`` from sorted set ``name``
4803 For more information, see https://redis.io/commands/zrem
4804 """
4805 return self.execute_command("ZREM", name, *values)
4807 def zremrangebylex(self, name: KeyT, min: EncodableT, max: EncodableT) -> ResponseT:
4808 """
4809 Remove all elements in the sorted set ``name`` between the
4810 lexicographical range specified by ``min`` and ``max``.
4812 Returns the number of elements removed.
4814 For more information, see https://redis.io/commands/zremrangebylex
4815 """
4816 return self.execute_command("ZREMRANGEBYLEX", name, min, max)
4818 def zremrangebyrank(self, name: KeyT, min: int, max: int) -> ResponseT:
4819 """
4820 Remove all elements in the sorted set ``name`` with ranks between
4821 ``min`` and ``max``. Values are 0-based, ordered from smallest score
4822 to largest. Values can be negative indicating the highest scores.
4823 Returns the number of elements removed
4825 For more information, see https://redis.io/commands/zremrangebyrank
4826 """
4827 return self.execute_command("ZREMRANGEBYRANK", name, min, max)
4829 def zremrangebyscore(
4830 self, name: KeyT, min: ZScoreBoundT, max: ZScoreBoundT
4831 ) -> ResponseT:
4832 """
4833 Remove all elements in the sorted set ``name`` with scores
4834 between ``min`` and ``max``. Returns the number of elements removed.
4836 For more information, see https://redis.io/commands/zremrangebyscore
4837 """
4838 return self.execute_command("ZREMRANGEBYSCORE", name, min, max)
4840 def zrevrank(
4841 self,
4842 name: KeyT,
4843 value: EncodableT,
4844 withscore: bool = False,
4845 score_cast_func: Union[type, Callable] = float,
4846 ) -> ResponseT:
4847 """
4848 Returns a 0-based value indicating the descending rank of
4849 ``value`` in sorted set ``name``.
4850 The optional ``withscore`` argument supplements the command's
4851 reply with the score of the element returned.
4853 ``score_cast_func`` a callable used to cast the score return value
4855 For more information, see https://redis.io/commands/zrevrank
4856 """
4857 pieces = ["ZREVRANK", name, value]
4858 if withscore:
4859 pieces.append("WITHSCORE")
4861 options = {"withscore": withscore, "score_cast_func": score_cast_func}
4863 return self.execute_command(*pieces, **options)
4865 def zscore(self, name: KeyT, value: EncodableT) -> ResponseT:
4866 """
4867 Return the score of element ``value`` in sorted set ``name``
4869 For more information, see https://redis.io/commands/zscore
4870 """
4871 return self.execute_command("ZSCORE", name, value, keys=[name])
4873 def zunion(
4874 self,
4875 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4876 aggregate: Optional[str] = None,
4877 withscores: bool = False,
4878 score_cast_func: Union[type, Callable] = float,
4879 ) -> ResponseT:
4880 """
4881 Return the union of multiple sorted sets specified by ``keys``.
4882 ``keys`` can be provided as dictionary of keys and their weights.
4883 Scores will be aggregated based on the ``aggregate``, or SUM if
4884 none is provided.
4886 ``score_cast_func`` a callable used to cast the score return value
4888 For more information, see https://redis.io/commands/zunion
4889 """
4890 return self._zaggregate(
4891 "ZUNION",
4892 None,
4893 keys,
4894 aggregate,
4895 withscores=withscores,
4896 score_cast_func=score_cast_func,
4897 )
4899 def zunionstore(
4900 self,
4901 dest: KeyT,
4902 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4903 aggregate: Optional[str] = None,
4904 ) -> ResponseT:
4905 """
4906 Union multiple sorted sets specified by ``keys`` into
4907 a new sorted set, ``dest``. Scores in the destination will be
4908 aggregated based on the ``aggregate``, or SUM if none is provided.
4910 For more information, see https://redis.io/commands/zunionstore
4911 """
4912 return self._zaggregate("ZUNIONSTORE", dest, keys, aggregate)
4914 def zmscore(self, key: KeyT, members: List[str]) -> ResponseT:
4915 """
4916 Returns the scores associated with the specified members
4917 in the sorted set stored at key.
4918 ``members`` should be a list of the member name.
4919 Return type is a list of score.
4920 If the member does not exist, a None will be returned
4921 in corresponding position.
4923 For more information, see https://redis.io/commands/zmscore
4924 """
4925 if not members:
4926 raise DataError("ZMSCORE members must be a non-empty list")
4927 pieces = [key] + members
4928 return self.execute_command("ZMSCORE", *pieces, keys=[key])
4930 def _zaggregate(
4931 self,
4932 command: str,
4933 dest: Union[KeyT, None],
4934 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4935 aggregate: Optional[str] = None,
4936 **options,
4937 ) -> ResponseT:
4938 pieces: list[EncodableT] = [command]
4939 if dest is not None:
4940 pieces.append(dest)
4941 pieces.append(len(keys))
4942 if isinstance(keys, dict):
4943 keys, weights = keys.keys(), keys.values()
4944 else:
4945 weights = None
4946 pieces.extend(keys)
4947 if weights:
4948 pieces.append(b"WEIGHTS")
4949 pieces.extend(weights)
4950 if aggregate:
4951 if aggregate.upper() in ["SUM", "MIN", "MAX"]:
4952 pieces.append(b"AGGREGATE")
4953 pieces.append(aggregate)
4954 else:
4955 raise DataError("aggregate can be sum, min or max.")
4956 if options.get("withscores", False):
4957 pieces.append(b"WITHSCORES")
4958 options["keys"] = keys
4959 return self.execute_command(*pieces, **options)
4962AsyncSortedSetCommands = SortedSetCommands
4965class HyperlogCommands(CommandsProtocol):
4966 """
4967 Redis commands of HyperLogLogs data type.
4968 see: https://redis.io/topics/data-types-intro#hyperloglogs
4969 """
4971 def pfadd(self, name: KeyT, *values: FieldT) -> ResponseT:
4972 """
4973 Adds the specified elements to the specified HyperLogLog.
4975 For more information, see https://redis.io/commands/pfadd
4976 """
4977 return self.execute_command("PFADD", name, *values)
4979 def pfcount(self, *sources: KeyT) -> ResponseT:
4980 """
4981 Return the approximated cardinality of
4982 the set observed by the HyperLogLog at key(s).
4984 For more information, see https://redis.io/commands/pfcount
4985 """
4986 return self.execute_command("PFCOUNT", *sources)
4988 def pfmerge(self, dest: KeyT, *sources: KeyT) -> ResponseT:
4989 """
4990 Merge N different HyperLogLogs into a single one.
4992 For more information, see https://redis.io/commands/pfmerge
4993 """
4994 return self.execute_command("PFMERGE", dest, *sources)
4997AsyncHyperlogCommands = HyperlogCommands
5000class HashDataPersistOptions(Enum):
5001 # set the value for each provided key to each
5002 # provided value only if all do not already exist.
5003 FNX = "FNX"
5005 # set the value for each provided key to each
5006 # provided value only if all already exist.
5007 FXX = "FXX"
5010class HashCommands(CommandsProtocol):
5011 """
5012 Redis commands for Hash data type.
5013 see: https://redis.io/topics/data-types-intro#redis-hashes
5014 """
5016 def hdel(self, name: str, *keys: str) -> Union[Awaitable[int], int]:
5017 """
5018 Delete ``keys`` from hash ``name``
5020 For more information, see https://redis.io/commands/hdel
5021 """
5022 return self.execute_command("HDEL", name, *keys)
5024 def hexists(self, name: str, key: str) -> Union[Awaitable[bool], bool]:
5025 """
5026 Returns a boolean indicating if ``key`` exists within hash ``name``
5028 For more information, see https://redis.io/commands/hexists
5029 """
5030 return self.execute_command("HEXISTS", name, key, keys=[name])
5032 def hget(
5033 self, name: str, key: str
5034 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
5035 """
5036 Return the value of ``key`` within the hash ``name``
5038 For more information, see https://redis.io/commands/hget
5039 """
5040 return self.execute_command("HGET", name, key, keys=[name])
5042 def hgetall(self, name: str) -> Union[Awaitable[dict], dict]:
5043 """
5044 Return a Python dict of the hash's name/value pairs
5046 For more information, see https://redis.io/commands/hgetall
5047 """
5048 return self.execute_command("HGETALL", name, keys=[name])
5050 def hgetdel(
5051 self, name: str, *keys: str
5052 ) -> Union[
5053 Awaitable[Optional[List[Union[str, bytes]]]], Optional[List[Union[str, bytes]]]
5054 ]:
5055 """
5056 Return the value of ``key`` within the hash ``name`` and
5057 delete the field in the hash.
5058 This command is similar to HGET, except for the fact that it also deletes
5059 the key on success from the hash with the provided ```name```.
5061 Available since Redis 8.0
5062 For more information, see https://redis.io/commands/hgetdel
5063 """
5064 if len(keys) == 0:
5065 raise DataError("'hgetdel' should have at least one key provided")
5067 return self.execute_command("HGETDEL", name, "FIELDS", len(keys), *keys)
5069 def hgetex(
5070 self,
5071 name: KeyT,
5072 *keys: str,
5073 ex: Optional[ExpiryT] = None,
5074 px: Optional[ExpiryT] = None,
5075 exat: Optional[AbsExpiryT] = None,
5076 pxat: Optional[AbsExpiryT] = None,
5077 persist: bool = False,
5078 ) -> Union[
5079 Awaitable[Optional[List[Union[str, bytes]]]], Optional[List[Union[str, bytes]]]
5080 ]:
5081 """
5082 Return the values of ``key`` and ``keys`` within the hash ``name``
5083 and optionally set their expiration.
5085 ``ex`` sets an expire flag on ``kyes`` for ``ex`` seconds.
5087 ``px`` sets an expire flag on ``keys`` for ``px`` milliseconds.
5089 ``exat`` sets an expire flag on ``keys`` for ``ex`` seconds,
5090 specified in unix time.
5092 ``pxat`` sets an expire flag on ``keys`` for ``ex`` milliseconds,
5093 specified in unix time.
5095 ``persist`` remove the time to live associated with the ``keys``.
5097 Available since Redis 8.0
5098 For more information, see https://redis.io/commands/hgetex
5099 """
5100 if not keys:
5101 raise DataError("'hgetex' should have at least one key provided")
5103 opset = {ex, px, exat, pxat}
5104 if len(opset) > 2 or len(opset) > 1 and persist:
5105 raise DataError(
5106 "``ex``, ``px``, ``exat``, ``pxat``, "
5107 "and ``persist`` are mutually exclusive."
5108 )
5110 exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
5112 if persist:
5113 exp_options.append("PERSIST")
5115 return self.execute_command(
5116 "HGETEX",
5117 name,
5118 *exp_options,
5119 "FIELDS",
5120 len(keys),
5121 *keys,
5122 )
5124 def hincrby(
5125 self, name: str, key: str, amount: int = 1
5126 ) -> Union[Awaitable[int], int]:
5127 """
5128 Increment the value of ``key`` in hash ``name`` by ``amount``
5130 For more information, see https://redis.io/commands/hincrby
5131 """
5132 return self.execute_command("HINCRBY", name, key, amount)
5134 def hincrbyfloat(
5135 self, name: str, key: str, amount: float = 1.0
5136 ) -> Union[Awaitable[float], float]:
5137 """
5138 Increment the value of ``key`` in hash ``name`` by floating ``amount``
5140 For more information, see https://redis.io/commands/hincrbyfloat
5141 """
5142 return self.execute_command("HINCRBYFLOAT", name, key, amount)
5144 def hkeys(self, name: str) -> Union[Awaitable[List], List]:
5145 """
5146 Return the list of keys within hash ``name``
5148 For more information, see https://redis.io/commands/hkeys
5149 """
5150 return self.execute_command("HKEYS", name, keys=[name])
5152 def hlen(self, name: str) -> Union[Awaitable[int], int]:
5153 """
5154 Return the number of elements in hash ``name``
5156 For more information, see https://redis.io/commands/hlen
5157 """
5158 return self.execute_command("HLEN", name, keys=[name])
5160 def hset(
5161 self,
5162 name: str,
5163 key: Optional[str] = None,
5164 value: Optional[str] = None,
5165 mapping: Optional[dict] = None,
5166 items: Optional[list] = None,
5167 ) -> Union[Awaitable[int], int]:
5168 """
5169 Set ``key`` to ``value`` within hash ``name``,
5170 ``mapping`` accepts a dict of key/value pairs that will be
5171 added to hash ``name``.
5172 ``items`` accepts a list of key/value pairs that will be
5173 added to hash ``name``.
5174 Returns the number of fields that were added.
5176 For more information, see https://redis.io/commands/hset
5177 """
5179 if key is None and not mapping and not items:
5180 raise DataError("'hset' with no key value pairs")
5182 pieces = []
5183 if items:
5184 pieces.extend(items)
5185 if key is not None:
5186 pieces.extend((key, value))
5187 if mapping:
5188 for pair in mapping.items():
5189 pieces.extend(pair)
5191 return self.execute_command("HSET", name, *pieces)
5193 def hsetex(
5194 self,
5195 name: str,
5196 key: Optional[str] = None,
5197 value: Optional[str] = None,
5198 mapping: Optional[dict] = None,
5199 items: Optional[list] = None,
5200 ex: Optional[ExpiryT] = None,
5201 px: Optional[ExpiryT] = None,
5202 exat: Optional[AbsExpiryT] = None,
5203 pxat: Optional[AbsExpiryT] = None,
5204 data_persist_option: Optional[HashDataPersistOptions] = None,
5205 keepttl: bool = False,
5206 ) -> Union[Awaitable[int], int]:
5207 """
5208 Set ``key`` to ``value`` within hash ``name``
5210 ``mapping`` accepts a dict of key/value pairs that will be
5211 added to hash ``name``.
5213 ``items`` accepts a list of key/value pairs that will be
5214 added to hash ``name``.
5216 ``ex`` sets an expire flag on ``keys`` for ``ex`` seconds.
5218 ``px`` sets an expire flag on ``keys`` for ``px`` milliseconds.
5220 ``exat`` sets an expire flag on ``keys`` for ``ex`` seconds,
5221 specified in unix time.
5223 ``pxat`` sets an expire flag on ``keys`` for ``ex`` milliseconds,
5224 specified in unix time.
5226 ``data_persist_option`` can be set to ``FNX`` or ``FXX`` to control the
5227 behavior of the command.
5228 ``FNX`` will set the value for each provided key to each
5229 provided value only if all do not already exist.
5230 ``FXX`` will set the value for each provided key to each
5231 provided value only if all already exist.
5233 ``keepttl`` if True, retain the time to live associated with the keys.
5235 Returns the number of fields that were added.
5237 Available since Redis 8.0
5238 For more information, see https://redis.io/commands/hsetex
5239 """
5240 if key is None and not mapping and not items:
5241 raise DataError("'hsetex' with no key value pairs")
5243 if items and len(items) % 2 != 0:
5244 raise DataError(
5245 "'hsetex' with odd number of items. "
5246 "'items' must contain a list of key/value pairs."
5247 )
5249 opset = {ex, px, exat, pxat}
5250 if len(opset) > 2 or len(opset) > 1 and keepttl:
5251 raise DataError(
5252 "``ex``, ``px``, ``exat``, ``pxat``, "
5253 "and ``keepttl`` are mutually exclusive."
5254 )
5256 exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
5257 if data_persist_option:
5258 exp_options.append(data_persist_option.value)
5260 if keepttl:
5261 exp_options.append("KEEPTTL")
5263 pieces = []
5264 if items:
5265 pieces.extend(items)
5266 if key is not None:
5267 pieces.extend((key, value))
5268 if mapping:
5269 for pair in mapping.items():
5270 pieces.extend(pair)
5272 return self.execute_command(
5273 "HSETEX", name, *exp_options, "FIELDS", int(len(pieces) / 2), *pieces
5274 )
5276 def hsetnx(self, name: str, key: str, value: str) -> Union[Awaitable[bool], bool]:
5277 """
5278 Set ``key`` to ``value`` within hash ``name`` if ``key`` does not
5279 exist. Returns 1 if HSETNX created a field, otherwise 0.
5281 For more information, see https://redis.io/commands/hsetnx
5282 """
5283 return self.execute_command("HSETNX", name, key, value)
5285 @deprecated_function(
5286 version="4.0.0",
5287 reason="Use 'hset' instead.",
5288 name="hmset",
5289 )
5290 def hmset(self, name: str, mapping: dict) -> Union[Awaitable[str], str]:
5291 """
5292 Set key to value within hash ``name`` for each corresponding
5293 key and value from the ``mapping`` dict.
5295 For more information, see https://redis.io/commands/hmset
5296 """
5297 if not mapping:
5298 raise DataError("'hmset' with 'mapping' of length 0")
5299 items = []
5300 for pair in mapping.items():
5301 items.extend(pair)
5302 return self.execute_command("HMSET", name, *items)
5304 def hmget(self, name: str, keys: List, *args: List) -> Union[Awaitable[List], List]:
5305 """
5306 Returns a list of values ordered identically to ``keys``
5308 For more information, see https://redis.io/commands/hmget
5309 """
5310 args = list_or_args(keys, args)
5311 return self.execute_command("HMGET", name, *args, keys=[name])
5313 def hvals(self, name: str) -> Union[Awaitable[List], List]:
5314 """
5315 Return the list of values within hash ``name``
5317 For more information, see https://redis.io/commands/hvals
5318 """
5319 return self.execute_command("HVALS", name, keys=[name])
5321 def hstrlen(self, name: str, key: str) -> Union[Awaitable[int], int]:
5322 """
5323 Return the number of bytes stored in the value of ``key``
5324 within hash ``name``
5326 For more information, see https://redis.io/commands/hstrlen
5327 """
5328 return self.execute_command("HSTRLEN", name, key, keys=[name])
5330 def hexpire(
5331 self,
5332 name: KeyT,
5333 seconds: ExpiryT,
5334 *fields: str,
5335 nx: bool = False,
5336 xx: bool = False,
5337 gt: bool = False,
5338 lt: bool = False,
5339 ) -> ResponseT:
5340 """
5341 Sets or updates the expiration time for fields within a hash key, using relative
5342 time in seconds.
5344 If a field already has an expiration time, the behavior of the update can be
5345 controlled using the `nx`, `xx`, `gt`, and `lt` parameters.
5347 The return value provides detailed information about the outcome for each field.
5349 For more information, see https://redis.io/commands/hexpire
5351 Args:
5352 name: The name of the hash key.
5353 seconds: Expiration time in seconds, relative. Can be an integer, or a
5354 Python `timedelta` object.
5355 fields: List of fields within the hash to apply the expiration time to.
5356 nx: Set expiry only when the field has no expiry.
5357 xx: Set expiry only when the field has an existing expiry.
5358 gt: Set expiry only when the new expiry is greater than the current one.
5359 lt: Set expiry only when the new expiry is less than the current one.
5361 Returns:
5362 Returns a list which contains for each field in the request:
5363 - `-2` if the field does not exist, or if the key does not exist.
5364 - `0` if the specified NX | XX | GT | LT condition was not met.
5365 - `1` if the expiration time was set or updated.
5366 - `2` if the field was deleted because the specified expiration time is
5367 in the past.
5368 """
5369 conditions = [nx, xx, gt, lt]
5370 if sum(conditions) > 1:
5371 raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.")
5373 if isinstance(seconds, datetime.timedelta):
5374 seconds = int(seconds.total_seconds())
5376 options = []
5377 if nx:
5378 options.append("NX")
5379 if xx:
5380 options.append("XX")
5381 if gt:
5382 options.append("GT")
5383 if lt:
5384 options.append("LT")
5386 return self.execute_command(
5387 "HEXPIRE", name, seconds, *options, "FIELDS", len(fields), *fields
5388 )
5390 def hpexpire(
5391 self,
5392 name: KeyT,
5393 milliseconds: ExpiryT,
5394 *fields: str,
5395 nx: bool = False,
5396 xx: bool = False,
5397 gt: bool = False,
5398 lt: bool = False,
5399 ) -> ResponseT:
5400 """
5401 Sets or updates the expiration time for fields within a hash key, using relative
5402 time in milliseconds.
5404 If a field already has an expiration time, the behavior of the update can be
5405 controlled using the `nx`, `xx`, `gt`, and `lt` parameters.
5407 The return value provides detailed information about the outcome for each field.
5409 For more information, see https://redis.io/commands/hpexpire
5411 Args:
5412 name: The name of the hash key.
5413 milliseconds: Expiration time in milliseconds, relative. Can be an integer,
5414 or a Python `timedelta` object.
5415 fields: List of fields within the hash to apply the expiration time to.
5416 nx: Set expiry only when the field has no expiry.
5417 xx: Set expiry only when the field has an existing expiry.
5418 gt: Set expiry only when the new expiry is greater than the current one.
5419 lt: Set expiry only when the new expiry is less than the current one.
5421 Returns:
5422 Returns a list which contains for each field in the request:
5423 - `-2` if the field does not exist, or if the key does not exist.
5424 - `0` if the specified NX | XX | GT | LT condition was not met.
5425 - `1` if the expiration time was set or updated.
5426 - `2` if the field was deleted because the specified expiration time is
5427 in the past.
5428 """
5429 conditions = [nx, xx, gt, lt]
5430 if sum(conditions) > 1:
5431 raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.")
5433 if isinstance(milliseconds, datetime.timedelta):
5434 milliseconds = int(milliseconds.total_seconds() * 1000)
5436 options = []
5437 if nx:
5438 options.append("NX")
5439 if xx:
5440 options.append("XX")
5441 if gt:
5442 options.append("GT")
5443 if lt:
5444 options.append("LT")
5446 return self.execute_command(
5447 "HPEXPIRE", name, milliseconds, *options, "FIELDS", len(fields), *fields
5448 )
5450 def hexpireat(
5451 self,
5452 name: KeyT,
5453 unix_time_seconds: AbsExpiryT,
5454 *fields: str,
5455 nx: bool = False,
5456 xx: bool = False,
5457 gt: bool = False,
5458 lt: bool = False,
5459 ) -> ResponseT:
5460 """
5461 Sets or updates the expiration time for fields within a hash key, using an
5462 absolute Unix timestamp in seconds.
5464 If a field already has an expiration time, the behavior of the update can be
5465 controlled using the `nx`, `xx`, `gt`, and `lt` parameters.
5467 The return value provides detailed information about the outcome for each field.
5469 For more information, see https://redis.io/commands/hexpireat
5471 Args:
5472 name: The name of the hash key.
5473 unix_time_seconds: Expiration time as Unix timestamp in seconds. Can be an
5474 integer or a Python `datetime` object.
5475 fields: List of fields within the hash to apply the expiration time to.
5476 nx: Set expiry only when the field has no expiry.
5477 xx: Set expiry only when the field has an existing expiration time.
5478 gt: Set expiry only when the new expiry is greater than the current one.
5479 lt: Set expiry only when the new expiry is less than the current one.
5481 Returns:
5482 Returns a list which contains for each field in the request:
5483 - `-2` if the field does not exist, or if the key does not exist.
5484 - `0` if the specified NX | XX | GT | LT condition was not met.
5485 - `1` if the expiration time was set or updated.
5486 - `2` if the field was deleted because the specified expiration time is
5487 in the past.
5488 """
5489 conditions = [nx, xx, gt, lt]
5490 if sum(conditions) > 1:
5491 raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.")
5493 if isinstance(unix_time_seconds, datetime.datetime):
5494 unix_time_seconds = int(unix_time_seconds.timestamp())
5496 options = []
5497 if nx:
5498 options.append("NX")
5499 if xx:
5500 options.append("XX")
5501 if gt:
5502 options.append("GT")
5503 if lt:
5504 options.append("LT")
5506 return self.execute_command(
5507 "HEXPIREAT",
5508 name,
5509 unix_time_seconds,
5510 *options,
5511 "FIELDS",
5512 len(fields),
5513 *fields,
5514 )
5516 def hpexpireat(
5517 self,
5518 name: KeyT,
5519 unix_time_milliseconds: AbsExpiryT,
5520 *fields: str,
5521 nx: bool = False,
5522 xx: bool = False,
5523 gt: bool = False,
5524 lt: bool = False,
5525 ) -> ResponseT:
5526 """
5527 Sets or updates the expiration time for fields within a hash key, using an
5528 absolute Unix timestamp in milliseconds.
5530 If a field already has an expiration time, the behavior of the update can be
5531 controlled using the `nx`, `xx`, `gt`, and `lt` parameters.
5533 The return value provides detailed information about the outcome for each field.
5535 For more information, see https://redis.io/commands/hpexpireat
5537 Args:
5538 name: The name of the hash key.
5539 unix_time_milliseconds: Expiration time as Unix timestamp in milliseconds.
5540 Can be an integer or a Python `datetime` object.
5541 fields: List of fields within the hash to apply the expiry.
5542 nx: Set expiry only when the field has no expiry.
5543 xx: Set expiry only when the field has an existing expiry.
5544 gt: Set expiry only when the new expiry is greater than the current one.
5545 lt: Set expiry only when the new expiry is less than the current one.
5547 Returns:
5548 Returns a list which contains for each field in the request:
5549 - `-2` if the field does not exist, or if the key does not exist.
5550 - `0` if the specified NX | XX | GT | LT condition was not met.
5551 - `1` if the expiration time was set or updated.
5552 - `2` if the field was deleted because the specified expiration time is
5553 in the past.
5554 """
5555 conditions = [nx, xx, gt, lt]
5556 if sum(conditions) > 1:
5557 raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.")
5559 if isinstance(unix_time_milliseconds, datetime.datetime):
5560 unix_time_milliseconds = int(unix_time_milliseconds.timestamp() * 1000)
5562 options = []
5563 if nx:
5564 options.append("NX")
5565 if xx:
5566 options.append("XX")
5567 if gt:
5568 options.append("GT")
5569 if lt:
5570 options.append("LT")
5572 return self.execute_command(
5573 "HPEXPIREAT",
5574 name,
5575 unix_time_milliseconds,
5576 *options,
5577 "FIELDS",
5578 len(fields),
5579 *fields,
5580 )
5582 def hpersist(self, name: KeyT, *fields: str) -> ResponseT:
5583 """
5584 Removes the expiration time for each specified field in a hash.
5586 For more information, see https://redis.io/commands/hpersist
5588 Args:
5589 name: The name of the hash key.
5590 fields: A list of fields within the hash from which to remove the
5591 expiration time.
5593 Returns:
5594 Returns a list which contains for each field in the request:
5595 - `-2` if the field does not exist, or if the key does not exist.
5596 - `-1` if the field exists but has no associated expiration time.
5597 - `1` if the expiration time was successfully removed from the field.
5598 """
5599 return self.execute_command("HPERSIST", name, "FIELDS", len(fields), *fields)
5601 def hexpiretime(self, key: KeyT, *fields: str) -> ResponseT:
5602 """
5603 Returns the expiration times of hash fields as Unix timestamps in seconds.
5605 For more information, see https://redis.io/commands/hexpiretime
5607 Args:
5608 key: The hash key.
5609 fields: A list of fields within the hash for which to get the expiration
5610 time.
5612 Returns:
5613 Returns a list which contains for each field in the request:
5614 - `-2` if the field does not exist, or if the key does not exist.
5615 - `-1` if the field exists but has no associated expire time.
5616 - A positive integer representing the expiration Unix timestamp in
5617 seconds, if the field has an associated expiration time.
5618 """
5619 return self.execute_command(
5620 "HEXPIRETIME", key, "FIELDS", len(fields), *fields, keys=[key]
5621 )
5623 def hpexpiretime(self, key: KeyT, *fields: str) -> ResponseT:
5624 """
5625 Returns the expiration times of hash fields as Unix timestamps in milliseconds.
5627 For more information, see https://redis.io/commands/hpexpiretime
5629 Args:
5630 key: The hash key.
5631 fields: A list of fields within the hash for which to get the expiration
5632 time.
5634 Returns:
5635 Returns a list which contains for each field in the request:
5636 - `-2` if the field does not exist, or if the key does not exist.
5637 - `-1` if the field exists but has no associated expire time.
5638 - A positive integer representing the expiration Unix timestamp in
5639 milliseconds, if the field has an associated expiration time.
5640 """
5641 return self.execute_command(
5642 "HPEXPIRETIME", key, "FIELDS", len(fields), *fields, keys=[key]
5643 )
5645 def httl(self, key: KeyT, *fields: str) -> ResponseT:
5646 """
5647 Returns the TTL (Time To Live) in seconds for each specified field within a hash
5648 key.
5650 For more information, see https://redis.io/commands/httl
5652 Args:
5653 key: The hash key.
5654 fields: A list of fields within the hash for which to get the TTL.
5656 Returns:
5657 Returns a list which contains for each field in the request:
5658 - `-2` if the field does not exist, or if the key does not exist.
5659 - `-1` if the field exists but has no associated expire time.
5660 - A positive integer representing the TTL in seconds if the field has
5661 an associated expiration time.
5662 """
5663 return self.execute_command(
5664 "HTTL", key, "FIELDS", len(fields), *fields, keys=[key]
5665 )
5667 def hpttl(self, key: KeyT, *fields: str) -> ResponseT:
5668 """
5669 Returns the TTL (Time To Live) in milliseconds for each specified field within a
5670 hash key.
5672 For more information, see https://redis.io/commands/hpttl
5674 Args:
5675 key: The hash key.
5676 fields: A list of fields within the hash for which to get the TTL.
5678 Returns:
5679 Returns a list which contains for each field in the request:
5680 - `-2` if the field does not exist, or if the key does not exist.
5681 - `-1` if the field exists but has no associated expire time.
5682 - A positive integer representing the TTL in milliseconds if the field
5683 has an associated expiration time.
5684 """
5685 return self.execute_command(
5686 "HPTTL", key, "FIELDS", len(fields), *fields, keys=[key]
5687 )
5690AsyncHashCommands = HashCommands
5693class Script:
5694 """
5695 An executable Lua script object returned by ``register_script``
5696 """
5698 def __init__(self, registered_client: "redis.client.Redis", script: ScriptTextT):
5699 self.registered_client = registered_client
5700 self.script = script
5701 # Precalculate and store the SHA1 hex digest of the script.
5703 if isinstance(script, str):
5704 # We need the encoding from the client in order to generate an
5705 # accurate byte representation of the script
5706 encoder = self.get_encoder()
5707 script = encoder.encode(script)
5708 self.sha = hashlib.sha1(script).hexdigest()
5710 def __call__(
5711 self,
5712 keys: Union[Sequence[KeyT], None] = None,
5713 args: Union[Iterable[EncodableT], None] = None,
5714 client: Union["redis.client.Redis", None] = None,
5715 ):
5716 """Execute the script, passing any required ``args``"""
5717 keys = keys or []
5718 args = args or []
5719 if client is None:
5720 client = self.registered_client
5721 args = tuple(keys) + tuple(args)
5722 # make sure the Redis server knows about the script
5723 from redis.client import Pipeline
5725 if isinstance(client, Pipeline):
5726 # Make sure the pipeline can register the script before executing.
5727 client.scripts.add(self)
5728 try:
5729 return client.evalsha(self.sha, len(keys), *args)
5730 except NoScriptError:
5731 # Maybe the client is pointed to a different server than the client
5732 # that created this instance?
5733 # Overwrite the sha just in case there was a discrepancy.
5734 self.sha = client.script_load(self.script)
5735 return client.evalsha(self.sha, len(keys), *args)
5737 def get_encoder(self):
5738 """Get the encoder to encode string scripts into bytes."""
5739 try:
5740 return self.registered_client.get_encoder()
5741 except AttributeError:
5742 # DEPRECATED
5743 # In version <=4.1.2, this was the code we used to get the encoder.
5744 # However, after 4.1.2 we added support for scripting in clustered
5745 # redis. ClusteredRedis doesn't have a `.connection_pool` attribute
5746 # so we changed the Script class to use
5747 # `self.registered_client.get_encoder` (see above).
5748 # However, that is technically a breaking change, as consumers who
5749 # use Scripts directly might inject a `registered_client` that
5750 # doesn't have a `.get_encoder` field. This try/except prevents us
5751 # from breaking backward-compatibility. Ideally, it would be
5752 # removed in the next major release.
5753 return self.registered_client.connection_pool.get_encoder()
5756class AsyncScript:
5757 """
5758 An executable Lua script object returned by ``register_script``
5759 """
5761 def __init__(
5762 self,
5763 registered_client: "redis.asyncio.client.Redis",
5764 script: ScriptTextT,
5765 ):
5766 self.registered_client = registered_client
5767 self.script = script
5768 # Precalculate and store the SHA1 hex digest of the script.
5770 if isinstance(script, str):
5771 # We need the encoding from the client in order to generate an
5772 # accurate byte representation of the script
5773 try:
5774 encoder = registered_client.connection_pool.get_encoder()
5775 except AttributeError:
5776 # Cluster
5777 encoder = registered_client.get_encoder()
5778 script = encoder.encode(script)
5779 self.sha = hashlib.sha1(script).hexdigest()
5781 async def __call__(
5782 self,
5783 keys: Union[Sequence[KeyT], None] = None,
5784 args: Union[Iterable[EncodableT], None] = None,
5785 client: Union["redis.asyncio.client.Redis", None] = None,
5786 ):
5787 """Execute the script, passing any required ``args``"""
5788 keys = keys or []
5789 args = args or []
5790 if client is None:
5791 client = self.registered_client
5792 args = tuple(keys) + tuple(args)
5793 # make sure the Redis server knows about the script
5794 from redis.asyncio.client import Pipeline
5796 if isinstance(client, Pipeline):
5797 # Make sure the pipeline can register the script before executing.
5798 client.scripts.add(self)
5799 try:
5800 return await client.evalsha(self.sha, len(keys), *args)
5801 except NoScriptError:
5802 # Maybe the client is pointed to a different server than the client
5803 # that created this instance?
5804 # Overwrite the sha just in case there was a discrepancy.
5805 self.sha = await client.script_load(self.script)
5806 return await client.evalsha(self.sha, len(keys), *args)
5809class PubSubCommands(CommandsProtocol):
5810 """
5811 Redis PubSub commands.
5812 see https://redis.io/topics/pubsub
5813 """
5815 def publish(self, channel: ChannelT, message: EncodableT, **kwargs) -> ResponseT:
5816 """
5817 Publish ``message`` on ``channel``.
5818 Returns the number of subscribers the message was delivered to.
5820 For more information, see https://redis.io/commands/publish
5821 """
5822 return self.execute_command("PUBLISH", channel, message, **kwargs)
5824 def spublish(self, shard_channel: ChannelT, message: EncodableT) -> ResponseT:
5825 """
5826 Posts a message to the given shard channel.
5827 Returns the number of clients that received the message
5829 For more information, see https://redis.io/commands/spublish
5830 """
5831 return self.execute_command("SPUBLISH", shard_channel, message)
5833 def pubsub_channels(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
5834 """
5835 Return a list of channels that have at least one subscriber
5837 For more information, see https://redis.io/commands/pubsub-channels
5838 """
5839 return self.execute_command("PUBSUB CHANNELS", pattern, **kwargs)
5841 def pubsub_shardchannels(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
5842 """
5843 Return a list of shard_channels that have at least one subscriber
5845 For more information, see https://redis.io/commands/pubsub-shardchannels
5846 """
5847 return self.execute_command("PUBSUB SHARDCHANNELS", pattern, **kwargs)
5849 def pubsub_numpat(self, **kwargs) -> ResponseT:
5850 """
5851 Returns the number of subscriptions to patterns
5853 For more information, see https://redis.io/commands/pubsub-numpat
5854 """
5855 return self.execute_command("PUBSUB NUMPAT", **kwargs)
5857 def pubsub_numsub(self, *args: ChannelT, **kwargs) -> ResponseT:
5858 """
5859 Return a list of (channel, number of subscribers) tuples
5860 for each channel given in ``*args``
5862 For more information, see https://redis.io/commands/pubsub-numsub
5863 """
5864 return self.execute_command("PUBSUB NUMSUB", *args, **kwargs)
5866 def pubsub_shardnumsub(self, *args: ChannelT, **kwargs) -> ResponseT:
5867 """
5868 Return a list of (shard_channel, number of subscribers) tuples
5869 for each channel given in ``*args``
5871 For more information, see https://redis.io/commands/pubsub-shardnumsub
5872 """
5873 return self.execute_command("PUBSUB SHARDNUMSUB", *args, **kwargs)
5876AsyncPubSubCommands = PubSubCommands
5879class ScriptCommands(CommandsProtocol):
5880 """
5881 Redis Lua script commands. see:
5882 https://redis.io/ebook/part-3-next-steps/chapter-11-scripting-redis-with-lua/
5883 """
5885 def _eval(
5886 self,
5887 command: str,
5888 script: str,
5889 numkeys: int,
5890 *keys_and_args: Union[KeyT, EncodableT],
5891 ) -> Union[Awaitable[str], str]:
5892 return self.execute_command(command, script, numkeys, *keys_and_args)
5894 def eval(
5895 self, script: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5896 ) -> Union[Awaitable[str], str]:
5897 """
5898 Execute the Lua ``script``, specifying the ``numkeys`` the script
5899 will touch and the key names and argument values in ``keys_and_args``.
5900 Returns the result of the script.
5902 In practice, use the object returned by ``register_script``. This
5903 function exists purely for Redis API completion.
5905 For more information, see https://redis.io/commands/eval
5906 """
5907 return self._eval("EVAL", script, numkeys, *keys_and_args)
5909 def eval_ro(
5910 self, script: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5911 ) -> Union[Awaitable[str], str]:
5912 """
5913 The read-only variant of the EVAL command
5915 Execute the read-only Lua ``script`` specifying the ``numkeys`` the script
5916 will touch and the key names and argument values in ``keys_and_args``.
5917 Returns the result of the script.
5919 For more information, see https://redis.io/commands/eval_ro
5920 """
5921 return self._eval("EVAL_RO", script, numkeys, *keys_and_args)
5923 def _evalsha(
5924 self,
5925 command: str,
5926 sha: str,
5927 numkeys: int,
5928 *keys_and_args: Union[KeyT, EncodableT],
5929 ) -> Union[Awaitable[str], str]:
5930 return self.execute_command(command, sha, numkeys, *keys_and_args)
5932 def evalsha(
5933 self, sha: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5934 ) -> Union[Awaitable[str], str]:
5935 """
5936 Use the ``sha`` to execute a Lua script already registered via EVAL
5937 or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
5938 key names and argument values in ``keys_and_args``. Returns the result
5939 of the script.
5941 In practice, use the object returned by ``register_script``. This
5942 function exists purely for Redis API completion.
5944 For more information, see https://redis.io/commands/evalsha
5945 """
5946 return self._evalsha("EVALSHA", sha, numkeys, *keys_and_args)
5948 def evalsha_ro(
5949 self, sha: str, numkeys: int, *keys_and_args: Union[KeyT, EncodableT]
5950 ) -> Union[Awaitable[str], str]:
5951 """
5952 The read-only variant of the EVALSHA command
5954 Use the ``sha`` to execute a read-only Lua script already registered via EVAL
5955 or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
5956 key names and argument values in ``keys_and_args``. Returns the result
5957 of the script.
5959 For more information, see https://redis.io/commands/evalsha_ro
5960 """
5961 return self._evalsha("EVALSHA_RO", sha, numkeys, *keys_and_args)
5963 def script_exists(self, *args: str) -> ResponseT:
5964 """
5965 Check if a script exists in the script cache by specifying the SHAs of
5966 each script as ``args``. Returns a list of boolean values indicating if
5967 if each already script exists in the cache_data.
5969 For more information, see https://redis.io/commands/script-exists
5970 """
5971 return self.execute_command("SCRIPT EXISTS", *args)
5973 def script_debug(self, *args) -> None:
5974 raise NotImplementedError(
5975 "SCRIPT DEBUG is intentionally not implemented in the client."
5976 )
5978 def script_flush(
5979 self, sync_type: Union[Literal["SYNC"], Literal["ASYNC"]] = None
5980 ) -> ResponseT:
5981 """Flush all scripts from the script cache_data.
5983 ``sync_type`` is by default SYNC (synchronous) but it can also be
5984 ASYNC.
5986 For more information, see https://redis.io/commands/script-flush
5987 """
5989 # Redis pre 6 had no sync_type.
5990 if sync_type not in ["SYNC", "ASYNC", None]:
5991 raise DataError(
5992 "SCRIPT FLUSH defaults to SYNC in redis > 6.2, or "
5993 "accepts SYNC/ASYNC. For older versions, "
5994 "of redis leave as None."
5995 )
5996 if sync_type is None:
5997 pieces = []
5998 else:
5999 pieces = [sync_type]
6000 return self.execute_command("SCRIPT FLUSH", *pieces)
6002 def script_kill(self) -> ResponseT:
6003 """
6004 Kill the currently executing Lua script
6006 For more information, see https://redis.io/commands/script-kill
6007 """
6008 return self.execute_command("SCRIPT KILL")
6010 def script_load(self, script: ScriptTextT) -> ResponseT:
6011 """
6012 Load a Lua ``script`` into the script cache_data. Returns the SHA.
6014 For more information, see https://redis.io/commands/script-load
6015 """
6016 return self.execute_command("SCRIPT LOAD", script)
6018 def register_script(self: "redis.client.Redis", script: ScriptTextT) -> Script:
6019 """
6020 Register a Lua ``script`` specifying the ``keys`` it will touch.
6021 Returns a Script object that is callable and hides the complexity of
6022 deal with scripts, keys, and shas. This is the preferred way to work
6023 with Lua scripts.
6024 """
6025 return Script(self, script)
6028class AsyncScriptCommands(ScriptCommands):
6029 async def script_debug(self, *args) -> None:
6030 return super().script_debug()
6032 def register_script(
6033 self: "redis.asyncio.client.Redis",
6034 script: ScriptTextT,
6035 ) -> AsyncScript:
6036 """
6037 Register a Lua ``script`` specifying the ``keys`` it will touch.
6038 Returns a Script object that is callable and hides the complexity of
6039 deal with scripts, keys, and shas. This is the preferred way to work
6040 with Lua scripts.
6041 """
6042 return AsyncScript(self, script)
6045class GeoCommands(CommandsProtocol):
6046 """
6047 Redis Geospatial commands.
6048 see: https://redis.com/redis-best-practices/indexing-patterns/geospatial/
6049 """
6051 def geoadd(
6052 self,
6053 name: KeyT,
6054 values: Sequence[EncodableT],
6055 nx: bool = False,
6056 xx: bool = False,
6057 ch: bool = False,
6058 ) -> ResponseT:
6059 """
6060 Add the specified geospatial items to the specified key identified
6061 by the ``name`` argument. The Geospatial items are given as ordered
6062 members of the ``values`` argument, each item or place is formed by
6063 the triad longitude, latitude and name.
6065 Note: You can use ZREM to remove elements.
6067 ``nx`` forces ZADD to only create new elements and not to update
6068 scores for elements that already exist.
6070 ``xx`` forces ZADD to only update scores of elements that already
6071 exist. New elements will not be added.
6073 ``ch`` modifies the return value to be the numbers of elements changed.
6074 Changed elements include new elements that were added and elements
6075 whose scores changed.
6077 For more information, see https://redis.io/commands/geoadd
6078 """
6079 if nx and xx:
6080 raise DataError("GEOADD allows either 'nx' or 'xx', not both")
6081 if len(values) % 3 != 0:
6082 raise DataError("GEOADD requires places with lon, lat and name values")
6083 pieces = [name]
6084 if nx:
6085 pieces.append("NX")
6086 if xx:
6087 pieces.append("XX")
6088 if ch:
6089 pieces.append("CH")
6090 pieces.extend(values)
6091 return self.execute_command("GEOADD", *pieces)
6093 def geodist(
6094 self, name: KeyT, place1: FieldT, place2: FieldT, unit: Optional[str] = None
6095 ) -> ResponseT:
6096 """
6097 Return the distance between ``place1`` and ``place2`` members of the
6098 ``name`` key.
6099 The units must be one of the following : m, km mi, ft. By default
6100 meters are used.
6102 For more information, see https://redis.io/commands/geodist
6103 """
6104 pieces: list[EncodableT] = [name, place1, place2]
6105 if unit and unit not in ("m", "km", "mi", "ft"):
6106 raise DataError("GEODIST invalid unit")
6107 elif unit:
6108 pieces.append(unit)
6109 return self.execute_command("GEODIST", *pieces, keys=[name])
6111 def geohash(self, name: KeyT, *values: FieldT) -> ResponseT:
6112 """
6113 Return the geo hash string for each item of ``values`` members of
6114 the specified key identified by the ``name`` argument.
6116 For more information, see https://redis.io/commands/geohash
6117 """
6118 return self.execute_command("GEOHASH", name, *values, keys=[name])
6120 def geopos(self, name: KeyT, *values: FieldT) -> ResponseT:
6121 """
6122 Return the positions of each item of ``values`` as members of
6123 the specified key identified by the ``name`` argument. Each position
6124 is represented by the pairs lon and lat.
6126 For more information, see https://redis.io/commands/geopos
6127 """
6128 return self.execute_command("GEOPOS", name, *values, keys=[name])
6130 def georadius(
6131 self,
6132 name: KeyT,
6133 longitude: float,
6134 latitude: float,
6135 radius: float,
6136 unit: Optional[str] = None,
6137 withdist: bool = False,
6138 withcoord: bool = False,
6139 withhash: bool = False,
6140 count: Optional[int] = None,
6141 sort: Optional[str] = None,
6142 store: Optional[KeyT] = None,
6143 store_dist: Optional[KeyT] = None,
6144 any: bool = False,
6145 ) -> ResponseT:
6146 """
6147 Return the members of the specified key identified by the
6148 ``name`` argument which are within the borders of the area specified
6149 with the ``latitude`` and ``longitude`` location and the maximum
6150 distance from the center specified by the ``radius`` value.
6152 The units must be one of the following : m, km mi, ft. By default
6154 ``withdist`` indicates to return the distances of each place.
6156 ``withcoord`` indicates to return the latitude and longitude of
6157 each place.
6159 ``withhash`` indicates to return the geohash string of each place.
6161 ``count`` indicates to return the number of elements up to N.
6163 ``sort`` indicates to return the places in a sorted way, ASC for
6164 nearest to fairest and DESC for fairest to nearest.
6166 ``store`` indicates to save the places names in a sorted set named
6167 with a specific key, each element of the destination sorted set is
6168 populated with the score got from the original geo sorted set.
6170 ``store_dist`` indicates to save the places names in a sorted set
6171 named with a specific key, instead of ``store`` the sorted set
6172 destination score is set with the distance.
6174 For more information, see https://redis.io/commands/georadius
6175 """
6176 return self._georadiusgeneric(
6177 "GEORADIUS",
6178 name,
6179 longitude,
6180 latitude,
6181 radius,
6182 unit=unit,
6183 withdist=withdist,
6184 withcoord=withcoord,
6185 withhash=withhash,
6186 count=count,
6187 sort=sort,
6188 store=store,
6189 store_dist=store_dist,
6190 any=any,
6191 )
6193 def georadiusbymember(
6194 self,
6195 name: KeyT,
6196 member: FieldT,
6197 radius: float,
6198 unit: Optional[str] = None,
6199 withdist: bool = False,
6200 withcoord: bool = False,
6201 withhash: bool = False,
6202 count: Optional[int] = None,
6203 sort: Optional[str] = None,
6204 store: Union[KeyT, None] = None,
6205 store_dist: Union[KeyT, None] = None,
6206 any: bool = False,
6207 ) -> ResponseT:
6208 """
6209 This command is exactly like ``georadius`` with the sole difference
6210 that instead of taking, as the center of the area to query, a longitude
6211 and latitude value, it takes the name of a member already existing
6212 inside the geospatial index represented by the sorted set.
6214 For more information, see https://redis.io/commands/georadiusbymember
6215 """
6216 return self._georadiusgeneric(
6217 "GEORADIUSBYMEMBER",
6218 name,
6219 member,
6220 radius,
6221 unit=unit,
6222 withdist=withdist,
6223 withcoord=withcoord,
6224 withhash=withhash,
6225 count=count,
6226 sort=sort,
6227 store=store,
6228 store_dist=store_dist,
6229 any=any,
6230 )
6232 def _georadiusgeneric(
6233 self, command: str, *args: EncodableT, **kwargs: Union[EncodableT, None]
6234 ) -> ResponseT:
6235 pieces = list(args)
6236 if kwargs["unit"] and kwargs["unit"] not in ("m", "km", "mi", "ft"):
6237 raise DataError("GEORADIUS invalid unit")
6238 elif kwargs["unit"]:
6239 pieces.append(kwargs["unit"])
6240 else:
6241 pieces.append("m")
6243 if kwargs["any"] and kwargs["count"] is None:
6244 raise DataError("``any`` can't be provided without ``count``")
6246 for arg_name, byte_repr in (
6247 ("withdist", "WITHDIST"),
6248 ("withcoord", "WITHCOORD"),
6249 ("withhash", "WITHHASH"),
6250 ):
6251 if kwargs[arg_name]:
6252 pieces.append(byte_repr)
6254 if kwargs["count"] is not None:
6255 pieces.extend(["COUNT", kwargs["count"]])
6256 if kwargs["any"]:
6257 pieces.append("ANY")
6259 if kwargs["sort"]:
6260 if kwargs["sort"] == "ASC":
6261 pieces.append("ASC")
6262 elif kwargs["sort"] == "DESC":
6263 pieces.append("DESC")
6264 else:
6265 raise DataError("GEORADIUS invalid sort")
6267 if kwargs["store"] and kwargs["store_dist"]:
6268 raise DataError("GEORADIUS store and store_dist cant be set together")
6270 if kwargs["store"]:
6271 pieces.extend([b"STORE", kwargs["store"]])
6273 if kwargs["store_dist"]:
6274 pieces.extend([b"STOREDIST", kwargs["store_dist"]])
6276 return self.execute_command(command, *pieces, **kwargs)
6278 def geosearch(
6279 self,
6280 name: KeyT,
6281 member: Union[FieldT, None] = None,
6282 longitude: Union[float, None] = None,
6283 latitude: Union[float, None] = None,
6284 unit: str = "m",
6285 radius: Union[float, None] = None,
6286 width: Union[float, None] = None,
6287 height: Union[float, None] = None,
6288 sort: Optional[str] = None,
6289 count: Optional[int] = None,
6290 any: bool = False,
6291 withcoord: bool = False,
6292 withdist: bool = False,
6293 withhash: bool = False,
6294 ) -> ResponseT:
6295 """
6296 Return the members of specified key identified by the
6297 ``name`` argument, which are within the borders of the
6298 area specified by a given shape. This command extends the
6299 GEORADIUS command, so in addition to searching within circular
6300 areas, it supports searching within rectangular areas.
6302 This command should be used in place of the deprecated
6303 GEORADIUS and GEORADIUSBYMEMBER commands.
6305 ``member`` Use the position of the given existing
6306 member in the sorted set. Can't be given with ``longitude``
6307 and ``latitude``.
6309 ``longitude`` and ``latitude`` Use the position given by
6310 this coordinates. Can't be given with ``member``
6311 ``radius`` Similar to GEORADIUS, search inside circular
6312 area according the given radius. Can't be given with
6313 ``height`` and ``width``.
6314 ``height`` and ``width`` Search inside an axis-aligned
6315 rectangle, determined by the given height and width.
6316 Can't be given with ``radius``
6318 ``unit`` must be one of the following : m, km, mi, ft.
6319 `m` for meters (the default value), `km` for kilometers,
6320 `mi` for miles and `ft` for feet.
6322 ``sort`` indicates to return the places in a sorted way,
6323 ASC for nearest to furthest and DESC for furthest to nearest.
6325 ``count`` limit the results to the first count matching items.
6327 ``any`` is set to True, the command will return as soon as
6328 enough matches are found. Can't be provided without ``count``
6330 ``withdist`` indicates to return the distances of each place.
6331 ``withcoord`` indicates to return the latitude and longitude of
6332 each place.
6334 ``withhash`` indicates to return the geohash string of each place.
6336 For more information, see https://redis.io/commands/geosearch
6337 """
6339 return self._geosearchgeneric(
6340 "GEOSEARCH",
6341 name,
6342 member=member,
6343 longitude=longitude,
6344 latitude=latitude,
6345 unit=unit,
6346 radius=radius,
6347 width=width,
6348 height=height,
6349 sort=sort,
6350 count=count,
6351 any=any,
6352 withcoord=withcoord,
6353 withdist=withdist,
6354 withhash=withhash,
6355 store=None,
6356 store_dist=None,
6357 )
6359 def geosearchstore(
6360 self,
6361 dest: KeyT,
6362 name: KeyT,
6363 member: Optional[FieldT] = None,
6364 longitude: Optional[float] = None,
6365 latitude: Optional[float] = None,
6366 unit: str = "m",
6367 radius: Optional[float] = None,
6368 width: Optional[float] = None,
6369 height: Optional[float] = None,
6370 sort: Optional[str] = None,
6371 count: Optional[int] = None,
6372 any: bool = False,
6373 storedist: bool = False,
6374 ) -> ResponseT:
6375 """
6376 This command is like GEOSEARCH, but stores the result in
6377 ``dest``. By default, it stores the results in the destination
6378 sorted set with their geospatial information.
6379 if ``store_dist`` set to True, the command will stores the
6380 items in a sorted set populated with their distance from the
6381 center of the circle or box, as a floating-point number.
6383 For more information, see https://redis.io/commands/geosearchstore
6384 """
6385 return self._geosearchgeneric(
6386 "GEOSEARCHSTORE",
6387 dest,
6388 name,
6389 member=member,
6390 longitude=longitude,
6391 latitude=latitude,
6392 unit=unit,
6393 radius=radius,
6394 width=width,
6395 height=height,
6396 sort=sort,
6397 count=count,
6398 any=any,
6399 withcoord=None,
6400 withdist=None,
6401 withhash=None,
6402 store=None,
6403 store_dist=storedist,
6404 )
6406 def _geosearchgeneric(
6407 self, command: str, *args: EncodableT, **kwargs: Union[EncodableT, None]
6408 ) -> ResponseT:
6409 pieces = list(args)
6411 # FROMMEMBER or FROMLONLAT
6412 if kwargs["member"] is None:
6413 if kwargs["longitude"] is None or kwargs["latitude"] is None:
6414 raise DataError("GEOSEARCH must have member or longitude and latitude")
6415 if kwargs["member"]:
6416 if kwargs["longitude"] or kwargs["latitude"]:
6417 raise DataError(
6418 "GEOSEARCH member and longitude or latitude cant be set together"
6419 )
6420 pieces.extend([b"FROMMEMBER", kwargs["member"]])
6421 if kwargs["longitude"] is not None and kwargs["latitude"] is not None:
6422 pieces.extend([b"FROMLONLAT", kwargs["longitude"], kwargs["latitude"]])
6424 # BYRADIUS or BYBOX
6425 if kwargs["radius"] is None:
6426 if kwargs["width"] is None or kwargs["height"] is None:
6427 raise DataError("GEOSEARCH must have radius or width and height")
6428 if kwargs["unit"] is None:
6429 raise DataError("GEOSEARCH must have unit")
6430 if kwargs["unit"].lower() not in ("m", "km", "mi", "ft"):
6431 raise DataError("GEOSEARCH invalid unit")
6432 if kwargs["radius"]:
6433 if kwargs["width"] or kwargs["height"]:
6434 raise DataError(
6435 "GEOSEARCH radius and width or height cant be set together"
6436 )
6437 pieces.extend([b"BYRADIUS", kwargs["radius"], kwargs["unit"]])
6438 if kwargs["width"] and kwargs["height"]:
6439 pieces.extend([b"BYBOX", kwargs["width"], kwargs["height"], kwargs["unit"]])
6441 # sort
6442 if kwargs["sort"]:
6443 if kwargs["sort"].upper() == "ASC":
6444 pieces.append(b"ASC")
6445 elif kwargs["sort"].upper() == "DESC":
6446 pieces.append(b"DESC")
6447 else:
6448 raise DataError("GEOSEARCH invalid sort")
6450 # count any
6451 if kwargs["count"]:
6452 pieces.extend([b"COUNT", kwargs["count"]])
6453 if kwargs["any"]:
6454 pieces.append(b"ANY")
6455 elif kwargs["any"]:
6456 raise DataError("GEOSEARCH ``any`` can't be provided without count")
6458 # other properties
6459 for arg_name, byte_repr in (
6460 ("withdist", b"WITHDIST"),
6461 ("withcoord", b"WITHCOORD"),
6462 ("withhash", b"WITHHASH"),
6463 ("store_dist", b"STOREDIST"),
6464 ):
6465 if kwargs[arg_name]:
6466 pieces.append(byte_repr)
6468 kwargs["keys"] = [args[0] if command == "GEOSEARCH" else args[1]]
6470 return self.execute_command(command, *pieces, **kwargs)
6473AsyncGeoCommands = GeoCommands
6476class ModuleCommands(CommandsProtocol):
6477 """
6478 Redis Module commands.
6479 see: https://redis.io/topics/modules-intro
6480 """
6482 def module_load(self, path, *args) -> ResponseT:
6483 """
6484 Loads the module from ``path``.
6485 Passes all ``*args`` to the module, during loading.
6486 Raises ``ModuleError`` if a module is not found at ``path``.
6488 For more information, see https://redis.io/commands/module-load
6489 """
6490 return self.execute_command("MODULE LOAD", path, *args)
6492 def module_loadex(
6493 self,
6494 path: str,
6495 options: Optional[List[str]] = None,
6496 args: Optional[List[str]] = None,
6497 ) -> ResponseT:
6498 """
6499 Loads a module from a dynamic library at runtime with configuration directives.
6501 For more information, see https://redis.io/commands/module-loadex
6502 """
6503 pieces = []
6504 if options is not None:
6505 pieces.append("CONFIG")
6506 pieces.extend(options)
6507 if args is not None:
6508 pieces.append("ARGS")
6509 pieces.extend(args)
6511 return self.execute_command("MODULE LOADEX", path, *pieces)
6513 def module_unload(self, name) -> ResponseT:
6514 """
6515 Unloads the module ``name``.
6516 Raises ``ModuleError`` if ``name`` is not in loaded modules.
6518 For more information, see https://redis.io/commands/module-unload
6519 """
6520 return self.execute_command("MODULE UNLOAD", name)
6522 def module_list(self) -> ResponseT:
6523 """
6524 Returns a list of dictionaries containing the name and version of
6525 all loaded modules.
6527 For more information, see https://redis.io/commands/module-list
6528 """
6529 return self.execute_command("MODULE LIST")
6531 def command_info(self) -> None:
6532 raise NotImplementedError(
6533 "COMMAND INFO is intentionally not implemented in the client."
6534 )
6536 def command_count(self) -> ResponseT:
6537 return self.execute_command("COMMAND COUNT")
6539 def command_getkeys(self, *args) -> ResponseT:
6540 return self.execute_command("COMMAND GETKEYS", *args)
6542 def command(self) -> ResponseT:
6543 return self.execute_command("COMMAND")
6546class AsyncModuleCommands(ModuleCommands):
6547 async def command_info(self) -> None:
6548 return super().command_info()
6551class ClusterCommands(CommandsProtocol):
6552 """
6553 Class for Redis Cluster commands
6554 """
6556 def cluster(self, cluster_arg, *args, **kwargs) -> ResponseT:
6557 return self.execute_command(f"CLUSTER {cluster_arg.upper()}", *args, **kwargs)
6559 def readwrite(self, **kwargs) -> ResponseT:
6560 """
6561 Disables read queries for a connection to a Redis Cluster slave node.
6563 For more information, see https://redis.io/commands/readwrite
6564 """
6565 return self.execute_command("READWRITE", **kwargs)
6567 def readonly(self, **kwargs) -> ResponseT:
6568 """
6569 Enables read queries for a connection to a Redis Cluster replica node.
6571 For more information, see https://redis.io/commands/readonly
6572 """
6573 return self.execute_command("READONLY", **kwargs)
6576AsyncClusterCommands = ClusterCommands
6579class FunctionCommands:
6580 """
6581 Redis Function commands
6582 """
6584 def function_load(
6585 self, code: str, replace: Optional[bool] = False
6586 ) -> Union[Awaitable[str], str]:
6587 """
6588 Load a library to Redis.
6589 :param code: the source code (must start with
6590 Shebang statement that provides a metadata about the library)
6591 :param replace: changes the behavior to overwrite the existing library
6592 with the new contents.
6593 Return the library name that was loaded.
6595 For more information, see https://redis.io/commands/function-load
6596 """
6597 pieces = ["REPLACE"] if replace else []
6598 pieces.append(code)
6599 return self.execute_command("FUNCTION LOAD", *pieces)
6601 def function_delete(self, library: str) -> Union[Awaitable[str], str]:
6602 """
6603 Delete the library called ``library`` and all its functions.
6605 For more information, see https://redis.io/commands/function-delete
6606 """
6607 return self.execute_command("FUNCTION DELETE", library)
6609 def function_flush(self, mode: str = "SYNC") -> Union[Awaitable[str], str]:
6610 """
6611 Deletes all the libraries.
6613 For more information, see https://redis.io/commands/function-flush
6614 """
6615 return self.execute_command("FUNCTION FLUSH", mode)
6617 def function_list(
6618 self, library: Optional[str] = "*", withcode: Optional[bool] = False
6619 ) -> Union[Awaitable[List], List]:
6620 """
6621 Return information about the functions and libraries.
6623 Args:
6625 library: specify a pattern for matching library names
6626 withcode: cause the server to include the libraries source implementation
6627 in the reply
6628 """
6629 args = ["LIBRARYNAME", library]
6630 if withcode:
6631 args.append("WITHCODE")
6632 return self.execute_command("FUNCTION LIST", *args)
6634 def _fcall(
6635 self, command: str, function, numkeys: int, *keys_and_args: Any
6636 ) -> Union[Awaitable[str], str]:
6637 return self.execute_command(command, function, numkeys, *keys_and_args)
6639 def fcall(
6640 self, function, numkeys: int, *keys_and_args: Any
6641 ) -> Union[Awaitable[str], str]:
6642 """
6643 Invoke a function.
6645 For more information, see https://redis.io/commands/fcall
6646 """
6647 return self._fcall("FCALL", function, numkeys, *keys_and_args)
6649 def fcall_ro(
6650 self, function, numkeys: int, *keys_and_args: Any
6651 ) -> Union[Awaitable[str], str]:
6652 """
6653 This is a read-only variant of the FCALL command that cannot
6654 execute commands that modify data.
6656 For more information, see https://redis.io/commands/fcall_ro
6657 """
6658 return self._fcall("FCALL_RO", function, numkeys, *keys_and_args)
6660 def function_dump(self) -> Union[Awaitable[str], str]:
6661 """
6662 Return the serialized payload of loaded libraries.
6664 For more information, see https://redis.io/commands/function-dump
6665 """
6666 from redis.client import NEVER_DECODE
6668 options = {}
6669 options[NEVER_DECODE] = []
6671 return self.execute_command("FUNCTION DUMP", **options)
6673 def function_restore(
6674 self, payload: str, policy: Optional[str] = "APPEND"
6675 ) -> Union[Awaitable[str], str]:
6676 """
6677 Restore libraries from the serialized ``payload``.
6678 You can use the optional policy argument to provide a policy
6679 for handling existing libraries.
6681 For more information, see https://redis.io/commands/function-restore
6682 """
6683 return self.execute_command("FUNCTION RESTORE", payload, policy)
6685 def function_kill(self) -> Union[Awaitable[str], str]:
6686 """
6687 Kill a function that is currently executing.
6689 For more information, see https://redis.io/commands/function-kill
6690 """
6691 return self.execute_command("FUNCTION KILL")
6693 def function_stats(self) -> Union[Awaitable[List], List]:
6694 """
6695 Return information about the function that's currently running
6696 and information about the available execution engines.
6698 For more information, see https://redis.io/commands/function-stats
6699 """
6700 return self.execute_command("FUNCTION STATS")
6703AsyncFunctionCommands = FunctionCommands
6706class DataAccessCommands(
6707 BasicKeyCommands,
6708 HyperlogCommands,
6709 HashCommands,
6710 GeoCommands,
6711 ListCommands,
6712 ScanCommands,
6713 SetCommands,
6714 StreamCommands,
6715 SortedSetCommands,
6716):
6717 """
6718 A class containing all of the implemented data access redis commands.
6719 This class is to be used as a mixin for synchronous Redis clients.
6720 """
6723class AsyncDataAccessCommands(
6724 AsyncBasicKeyCommands,
6725 AsyncHyperlogCommands,
6726 AsyncHashCommands,
6727 AsyncGeoCommands,
6728 AsyncListCommands,
6729 AsyncScanCommands,
6730 AsyncSetCommands,
6731 AsyncStreamCommands,
6732 AsyncSortedSetCommands,
6733):
6734 """
6735 A class containing all of the implemented data access redis commands.
6736 This class is to be used as a mixin for asynchronous Redis clients.
6737 """
6740class CoreCommands(
6741 ACLCommands,
6742 ClusterCommands,
6743 DataAccessCommands,
6744 ManagementCommands,
6745 ModuleCommands,
6746 PubSubCommands,
6747 ScriptCommands,
6748 FunctionCommands,
6749):
6750 """
6751 A class containing all of the implemented redis commands. This class is
6752 to be used as a mixin for synchronous Redis clients.
6753 """
6756class AsyncCoreCommands(
6757 AsyncACLCommands,
6758 AsyncClusterCommands,
6759 AsyncDataAccessCommands,
6760 AsyncManagementCommands,
6761 AsyncModuleCommands,
6762 AsyncPubSubCommands,
6763 AsyncScriptCommands,
6764 AsyncFunctionCommands,
6765):
6766 """
6767 A class containing all of the implemented redis commands. This class is
6768 to be used as a mixin for asynchronous Redis clients.
6769 """