Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/redis/commands/core.py: 22%
1804 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 07:09 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 07:09 +0000
1# from __future__ import annotations
3import datetime
4import hashlib
5import warnings
6from typing import (
7 TYPE_CHECKING,
8 Any,
9 AsyncIterator,
10 Awaitable,
11 Callable,
12 Dict,
13 Iterable,
14 Iterator,
15 List,
16 Mapping,
17 Optional,
18 Sequence,
19 Set,
20 Tuple,
21 Union,
22)
24from redis.compat import Literal
25from redis.exceptions import ConnectionError, DataError, NoScriptError, RedisError
26from redis.typing import (
27 AbsExpiryT,
28 AnyKeyT,
29 BitfieldOffsetT,
30 ChannelT,
31 CommandsProtocol,
32 ConsumerT,
33 EncodableT,
34 ExpiryT,
35 FieldT,
36 GroupT,
37 KeysT,
38 KeyT,
39 PatternT,
40 ScriptTextT,
41 StreamIdT,
42 TimeoutSecT,
43 ZScoreBoundT,
44)
46from .helpers import list_or_args
48if TYPE_CHECKING:
49 from redis.asyncio.client import Redis as AsyncRedis
50 from redis.client import Redis
52ResponseT = Union[Awaitable, Any]
55class ACLCommands(CommandsProtocol):
56 """
57 Redis Access Control List (ACL) commands.
58 see: https://redis.io/topics/acl
59 """
61 def acl_cat(self, category: Union[str, None] = None, **kwargs) -> ResponseT:
62 """
63 Returns a list of categories or commands within a category.
65 If ``category`` is not supplied, returns a list of all categories.
66 If ``category`` is supplied, returns a list of all commands within
67 that category.
69 For more information see https://redis.io/commands/acl-cat
70 """
71 pieces: list[EncodableT] = [category] if category else []
72 return self.execute_command("ACL CAT", *pieces, **kwargs)
74 def acl_dryrun(self, username, *args, **kwargs):
75 """
76 Simulate the execution of a given command by a given ``username``.
78 For more information see https://redis.io/commands/acl-dryrun
79 """
80 return self.execute_command("ACL DRYRUN", username, *args, **kwargs)
82 def acl_deluser(self, *username: str, **kwargs) -> ResponseT:
83 """
84 Delete the ACL for the specified ``username``s
86 For more information see https://redis.io/commands/acl-deluser
87 """
88 return self.execute_command("ACL DELUSER", *username, **kwargs)
90 def acl_genpass(self, bits: Union[int, None] = None, **kwargs) -> ResponseT:
91 """Generate a random password value.
92 If ``bits`` is supplied then use this number of bits, rounded to
93 the next multiple of 4.
94 See: https://redis.io/commands/acl-genpass
95 """
96 pieces = []
97 if bits is not None:
98 try:
99 b = int(bits)
100 if b < 0 or b > 4096:
101 raise ValueError
102 except ValueError:
103 raise DataError(
104 "genpass optionally accepts a bits argument, between 0 and 4096."
105 )
106 return self.execute_command("ACL GENPASS", *pieces, **kwargs)
108 def acl_getuser(self, username: str, **kwargs) -> ResponseT:
109 """
110 Get the ACL details for the specified ``username``.
112 If ``username`` does not exist, return None
114 For more information see https://redis.io/commands/acl-getuser
115 """
116 return self.execute_command("ACL GETUSER", username, **kwargs)
118 def acl_help(self, **kwargs) -> ResponseT:
119 """The ACL HELP command returns helpful text describing
120 the different subcommands.
122 For more information see https://redis.io/commands/acl-help
123 """
124 return self.execute_command("ACL HELP", **kwargs)
126 def acl_list(self, **kwargs) -> ResponseT:
127 """
128 Return a list of all ACLs on the server
130 For more information see https://redis.io/commands/acl-list
131 """
132 return self.execute_command("ACL LIST", **kwargs)
134 def acl_log(self, count: Union[int, None] = None, **kwargs) -> ResponseT:
135 """
136 Get ACL logs as a list.
137 :param int count: Get logs[0:count].
138 :rtype: List.
140 For more information see https://redis.io/commands/acl-log
141 """
142 args = []
143 if count is not None:
144 if not isinstance(count, int):
145 raise DataError("ACL LOG count must be an integer")
146 args.append(count)
148 return self.execute_command("ACL LOG", *args, **kwargs)
150 def acl_log_reset(self, **kwargs) -> ResponseT:
151 """
152 Reset ACL logs.
153 :rtype: Boolean.
155 For more information see https://redis.io/commands/acl-log
156 """
157 args = [b"RESET"]
158 return self.execute_command("ACL LOG", *args, **kwargs)
160 def acl_load(self, **kwargs) -> ResponseT:
161 """
162 Load ACL rules from the configured ``aclfile``.
164 Note that the server must be configured with the ``aclfile``
165 directive to be able to load ACL rules from an aclfile.
167 For more information see https://redis.io/commands/acl-load
168 """
169 return self.execute_command("ACL LOAD", **kwargs)
171 def acl_save(self, **kwargs) -> ResponseT:
172 """
173 Save ACL rules to the configured ``aclfile``.
175 Note that the server must be configured with the ``aclfile``
176 directive to be able to save ACL rules to an aclfile.
178 For more information see https://redis.io/commands/acl-save
179 """
180 return self.execute_command("ACL SAVE", **kwargs)
182 def acl_setuser(
183 self,
184 username: str,
185 enabled: bool = False,
186 nopass: bool = False,
187 passwords: Union[str, Iterable[str], None] = None,
188 hashed_passwords: Union[str, Iterable[str], None] = None,
189 categories: Optional[Iterable[str]] = None,
190 commands: Optional[Iterable[str]] = None,
191 keys: Optional[Iterable[KeyT]] = None,
192 channels: Optional[Iterable[ChannelT]] = None,
193 selectors: Optional[Iterable[Tuple[str, KeyT]]] = None,
194 reset: bool = False,
195 reset_keys: bool = False,
196 reset_channels: bool = False,
197 reset_passwords: bool = False,
198 **kwargs,
199 ) -> ResponseT:
200 """
201 Create or update an ACL user.
203 Create or update the ACL for ``username``. If the user already exists,
204 the existing ACL is completely overwritten and replaced with the
205 specified values.
207 ``enabled`` is a boolean indicating whether the user should be allowed
208 to authenticate or not. Defaults to ``False``.
210 ``nopass`` is a boolean indicating whether the can authenticate without
211 a password. This cannot be True if ``passwords`` are also specified.
213 ``passwords`` if specified is a list of plain text passwords
214 to add to or remove from the user. Each password must be prefixed with
215 a '+' to add or a '-' to remove. For convenience, the value of
216 ``passwords`` can be a simple prefixed string when adding or
217 removing a single password.
219 ``hashed_passwords`` if specified is a list of SHA-256 hashed passwords
220 to add to or remove from the user. Each hashed password must be
221 prefixed with a '+' to add or a '-' to remove. For convenience,
222 the value of ``hashed_passwords`` can be a simple prefixed string when
223 adding or removing a single password.
225 ``categories`` if specified is a list of strings representing category
226 permissions. Each string must be prefixed with either a '+' to add the
227 category permission or a '-' to remove the category permission.
229 ``commands`` if specified is a list of strings representing command
230 permissions. Each string must be prefixed with either a '+' to add the
231 command permission or a '-' to remove the command permission.
233 ``keys`` if specified is a list of key patterns to grant the user
234 access to. Keys patterns allow '*' to support wildcard matching. For
235 example, '*' grants access to all keys while 'cache:*' grants access
236 to all keys that are prefixed with 'cache:'. ``keys`` should not be
237 prefixed with a '~'.
239 ``reset`` is a boolean indicating whether the user should be fully
240 reset prior to applying the new ACL. Setting this to True will
241 remove all existing passwords, flags and privileges from the user and
242 then apply the specified rules. If this is False, the user's existing
243 passwords, flags and privileges will be kept and any new specified
244 rules will be applied on top.
246 ``reset_keys`` is a boolean indicating whether the user's key
247 permissions should be reset prior to applying any new key permissions
248 specified in ``keys``. If this is False, the user's existing
249 key permissions will be kept and any new specified key permissions
250 will be applied on top.
252 ``reset_channels`` is a boolean indicating whether the user's channel
253 permissions should be reset prior to applying any new channel permissions
254 specified in ``channels``.If this is False, the user's existing
255 channel permissions will be kept and any new specified channel permissions
256 will be applied on top.
258 ``reset_passwords`` is a boolean indicating whether to remove all
259 existing passwords and the 'nopass' flag from the user prior to
260 applying any new passwords specified in 'passwords' or
261 'hashed_passwords'. If this is False, the user's existing passwords
262 and 'nopass' status will be kept and any new specified passwords
263 or hashed_passwords will be applied on top.
265 For more information see https://redis.io/commands/acl-setuser
266 """
267 encoder = self.get_encoder()
268 pieces: List[EncodableT] = [username]
270 if reset:
271 pieces.append(b"reset")
273 if reset_keys:
274 pieces.append(b"resetkeys")
276 if reset_channels:
277 pieces.append(b"resetchannels")
279 if reset_passwords:
280 pieces.append(b"resetpass")
282 if enabled:
283 pieces.append(b"on")
284 else:
285 pieces.append(b"off")
287 if (passwords or hashed_passwords) and nopass:
288 raise DataError(
289 "Cannot set 'nopass' and supply 'passwords' or 'hashed_passwords'"
290 )
292 if passwords:
293 # as most users will have only one password, allow remove_passwords
294 # to be specified as a simple string or a list
295 passwords = list_or_args(passwords, [])
296 for i, password in enumerate(passwords):
297 password = encoder.encode(password)
298 if password.startswith(b"+"):
299 pieces.append(b">%s" % password[1:])
300 elif password.startswith(b"-"):
301 pieces.append(b"<%s" % password[1:])
302 else:
303 raise DataError(
304 f"Password {i} must be prefixed with a "
305 f'"+" to add or a "-" to remove'
306 )
308 if hashed_passwords:
309 # as most users will have only one password, allow remove_passwords
310 # to be specified as a simple string or a list
311 hashed_passwords = list_or_args(hashed_passwords, [])
312 for i, hashed_password in enumerate(hashed_passwords):
313 hashed_password = encoder.encode(hashed_password)
314 if hashed_password.startswith(b"+"):
315 pieces.append(b"#%s" % hashed_password[1:])
316 elif hashed_password.startswith(b"-"):
317 pieces.append(b"!%s" % hashed_password[1:])
318 else:
319 raise DataError(
320 f"Hashed password {i} must be prefixed with a "
321 f'"+" to add or a "-" to remove'
322 )
324 if nopass:
325 pieces.append(b"nopass")
327 if categories:
328 for category in categories:
329 category = encoder.encode(category)
330 # categories can be prefixed with one of (+@, +, -@, -)
331 if category.startswith(b"+@"):
332 pieces.append(category)
333 elif category.startswith(b"+"):
334 pieces.append(b"+@%s" % category[1:])
335 elif category.startswith(b"-@"):
336 pieces.append(category)
337 elif category.startswith(b"-"):
338 pieces.append(b"-@%s" % category[1:])
339 else:
340 raise DataError(
341 f'Category "{encoder.decode(category, force=True)}" '
342 'must be prefixed with "+" or "-"'
343 )
344 if commands:
345 for cmd in commands:
346 cmd = encoder.encode(cmd)
347 if not cmd.startswith(b"+") and not cmd.startswith(b"-"):
348 raise DataError(
349 f'Command "{encoder.decode(cmd, force=True)}" '
350 'must be prefixed with "+" or "-"'
351 )
352 pieces.append(cmd)
354 if keys:
355 for key in keys:
356 key = encoder.encode(key)
357 if not key.startswith(b"%") and not key.startswith(b"~"):
358 key = b"~%s" % key
359 pieces.append(key)
361 if channels:
362 for channel in channels:
363 channel = encoder.encode(channel)
364 pieces.append(b"&%s" % channel)
366 if selectors:
367 for cmd, key in selectors:
368 cmd = encoder.encode(cmd)
369 if not cmd.startswith(b"+") and not cmd.startswith(b"-"):
370 raise DataError(
371 f'Command "{encoder.decode(cmd, force=True)}" '
372 'must be prefixed with "+" or "-"'
373 )
375 key = encoder.encode(key)
376 if not key.startswith(b"%") and not key.startswith(b"~"):
377 key = b"~%s" % key
379 pieces.append(b"(%s %s)" % (cmd, key))
381 return self.execute_command("ACL SETUSER", *pieces, **kwargs)
383 def acl_users(self, **kwargs) -> ResponseT:
384 """Returns a list of all registered users on the server.
386 For more information see https://redis.io/commands/acl-users
387 """
388 return self.execute_command("ACL USERS", **kwargs)
390 def acl_whoami(self, **kwargs) -> ResponseT:
391 """Get the username for the current connection
393 For more information see https://redis.io/commands/acl-whoami
394 """
395 return self.execute_command("ACL WHOAMI", **kwargs)
398AsyncACLCommands = ACLCommands
401class ManagementCommands(CommandsProtocol):
402 """
403 Redis management commands
404 """
406 def auth(self, password, username=None, **kwargs):
407 """
408 Authenticates the user. If you do not pass username, Redis will try to
409 authenticate for the "default" user. If you do pass username, it will
410 authenticate for the given user.
411 For more information see https://redis.io/commands/auth
412 """
413 pieces = []
414 if username is not None:
415 pieces.append(username)
416 pieces.append(password)
417 return self.execute_command("AUTH", *pieces, **kwargs)
419 def bgrewriteaof(self, **kwargs):
420 """Tell the Redis server to rewrite the AOF file from data in memory.
422 For more information see https://redis.io/commands/bgrewriteaof
423 """
424 return self.execute_command("BGREWRITEAOF", **kwargs)
426 def bgsave(self, schedule: bool = True, **kwargs) -> ResponseT:
427 """
428 Tell the Redis server to save its data to disk. Unlike save(),
429 this method is asynchronous and returns immediately.
431 For more information see https://redis.io/commands/bgsave
432 """
433 pieces = []
434 if schedule:
435 pieces.append("SCHEDULE")
436 return self.execute_command("BGSAVE", *pieces, **kwargs)
438 def role(self) -> ResponseT:
439 """
440 Provide information on the role of a Redis instance in
441 the context of replication, by returning if the instance
442 is currently a master, slave, or sentinel.
444 For more information see https://redis.io/commands/role
445 """
446 return self.execute_command("ROLE")
448 def client_kill(self, address: str, **kwargs) -> ResponseT:
449 """Disconnects the client at ``address`` (ip:port)
451 For more information see https://redis.io/commands/client-kill
452 """
453 return self.execute_command("CLIENT KILL", address, **kwargs)
455 def client_kill_filter(
456 self,
457 _id: Union[str, None] = None,
458 _type: Union[str, None] = None,
459 addr: Union[str, None] = None,
460 skipme: Union[bool, None] = None,
461 laddr: Union[bool, None] = None,
462 user: str = None,
463 **kwargs,
464 ) -> ResponseT:
465 """
466 Disconnects client(s) using a variety of filter options
467 :param _id: Kills a client by its unique ID field
468 :param _type: Kills a client by type where type is one of 'normal',
469 'master', 'slave' or 'pubsub'
470 :param addr: Kills a client by its 'address:port'
471 :param skipme: If True, then the client calling the command
472 will not get killed even if it is identified by one of the filter
473 options. If skipme is not provided, the server defaults to skipme=True
474 :param laddr: Kills a client by its 'local (bind) address:port'
475 :param user: Kills a client for a specific user name
476 """
477 args = []
478 if _type is not None:
479 client_types = ("normal", "master", "slave", "pubsub")
480 if str(_type).lower() not in client_types:
481 raise DataError(f"CLIENT KILL type must be one of {client_types!r}")
482 args.extend((b"TYPE", _type))
483 if skipme is not None:
484 if not isinstance(skipme, bool):
485 raise DataError("CLIENT KILL skipme must be a bool")
486 if skipme:
487 args.extend((b"SKIPME", b"YES"))
488 else:
489 args.extend((b"SKIPME", b"NO"))
490 if _id is not None:
491 args.extend((b"ID", _id))
492 if addr is not None:
493 args.extend((b"ADDR", addr))
494 if laddr is not None:
495 args.extend((b"LADDR", laddr))
496 if user is not None:
497 args.extend((b"USER", user))
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: Union[str, None] = 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.
520 :param _type: optional. one of the client types (normal, master,
521 replica, pubsub)
522 :param client_id: optional. a list of client ids
524 For more information see https://redis.io/commands/client-list
525 """
526 args = []
527 if _type is not None:
528 client_types = ("normal", "master", "replica", "pubsub")
529 if str(_type).lower() not in client_types:
530 raise DataError(f"CLIENT LIST _type must be one of {client_types!r}")
531 args.append(b"TYPE")
532 args.append(_type)
533 if not isinstance(client_id, list):
534 raise DataError("client_id must be a list")
535 if client_id:
536 args.append(b"ID")
537 args.append(" ".join(client_id))
538 return self.execute_command("CLIENT LIST", *args, **kwargs)
540 def client_getname(self, **kwargs) -> ResponseT:
541 """
542 Returns the current connection name
544 For more information see https://redis.io/commands/client-getname
545 """
546 return self.execute_command("CLIENT GETNAME", **kwargs)
548 def client_getredir(self, **kwargs) -> ResponseT:
549 """
550 Returns the ID (an integer) of the client to whom we are
551 redirecting tracking notifications.
553 see: https://redis.io/commands/client-getredir
554 """
555 return self.execute_command("CLIENT GETREDIR", **kwargs)
557 def client_reply(
558 self, reply: Union[Literal["ON"], Literal["OFF"], Literal["SKIP"]], **kwargs
559 ) -> ResponseT:
560 """
561 Enable and disable redis server replies.
562 ``reply`` Must be ON OFF or SKIP,
563 ON - The default most with server replies to commands
564 OFF - Disable server responses to commands
565 SKIP - Skip the response of the immediately following command.
567 Note: When setting OFF or SKIP replies, you will need a client object
568 with a timeout specified in seconds, and will need to catch the
569 TimeoutError.
570 The test_client_reply unit test illustrates this, and
571 conftest.py has a client with a timeout.
573 See https://redis.io/commands/client-reply
574 """
575 replies = ["ON", "OFF", "SKIP"]
576 if reply not in replies:
577 raise DataError(f"CLIENT REPLY must be one of {replies!r}")
578 return self.execute_command("CLIENT REPLY", reply, **kwargs)
580 def client_id(self, **kwargs) -> ResponseT:
581 """
582 Returns the current connection id
584 For more information see https://redis.io/commands/client-id
585 """
586 return self.execute_command("CLIENT ID", **kwargs)
588 def client_tracking_on(
589 self,
590 clientid: Union[int, None] = None,
591 prefix: Sequence[KeyT] = [],
592 bcast: bool = False,
593 optin: bool = False,
594 optout: bool = False,
595 noloop: bool = False,
596 ) -> ResponseT:
597 """
598 Turn on the tracking mode.
599 For more information about the options look at client_tracking func.
601 See https://redis.io/commands/client-tracking
602 """
603 return self.client_tracking(
604 True, clientid, prefix, bcast, optin, optout, noloop
605 )
607 def client_tracking_off(
608 self,
609 clientid: Union[int, None] = None,
610 prefix: Sequence[KeyT] = [],
611 bcast: bool = False,
612 optin: bool = False,
613 optout: bool = False,
614 noloop: bool = False,
615 ) -> ResponseT:
616 """
617 Turn off the tracking mode.
618 For more information about the options look at client_tracking func.
620 See https://redis.io/commands/client-tracking
621 """
622 return self.client_tracking(
623 False, clientid, prefix, bcast, optin, optout, noloop
624 )
626 def client_tracking(
627 self,
628 on: bool = True,
629 clientid: Union[int, None] = None,
630 prefix: Sequence[KeyT] = [],
631 bcast: bool = False,
632 optin: bool = False,
633 optout: bool = False,
634 noloop: bool = False,
635 **kwargs,
636 ) -> ResponseT:
637 """
638 Enables the tracking feature of the Redis server, that is used
639 for server assisted client side caching.
641 ``on`` indicate for tracking on or tracking off. The dafualt is on.
643 ``clientid`` send invalidation messages to the connection with
644 the specified ID.
646 ``bcast`` enable tracking in broadcasting mode. In this mode
647 invalidation messages are reported for all the prefixes
648 specified, regardless of the keys requested by the connection.
650 ``optin`` when broadcasting is NOT active, normally don't track
651 keys in read only commands, unless they are called immediately
652 after a CLIENT CACHING yes command.
654 ``optout`` when broadcasting is NOT active, normally track keys in
655 read only commands, unless they are called immediately after a
656 CLIENT CACHING no command.
658 ``noloop`` don't send notifications about keys modified by this
659 connection itself.
661 ``prefix`` for broadcasting, register a given key prefix, so that
662 notifications will be provided only for keys starting with this string.
664 See https://redis.io/commands/client-tracking
665 """
667 if len(prefix) != 0 and bcast is False:
668 raise DataError("Prefix can only be used with bcast")
670 pieces = ["ON"] if on else ["OFF"]
671 if clientid is not None:
672 pieces.extend(["REDIRECT", clientid])
673 for p in prefix:
674 pieces.extend(["PREFIX", p])
675 if bcast:
676 pieces.append("BCAST")
677 if optin:
678 pieces.append("OPTIN")
679 if optout:
680 pieces.append("OPTOUT")
681 if noloop:
682 pieces.append("NOLOOP")
684 return self.execute_command("CLIENT TRACKING", *pieces)
686 def client_trackinginfo(self, **kwargs) -> ResponseT:
687 """
688 Returns the information about the current client connection's
689 use of the server assisted client side cache.
691 See https://redis.io/commands/client-trackinginfo
692 """
693 return self.execute_command("CLIENT TRACKINGINFO", **kwargs)
695 def client_setname(self, name: str, **kwargs) -> ResponseT:
696 """
697 Sets the current connection name
699 For more information see https://redis.io/commands/client-setname
701 .. note::
702 This method sets client name only for **current** connection.
704 If you want to set a common name for all connections managed
705 by this client, use ``client_name`` constructor argument.
706 """
707 return self.execute_command("CLIENT SETNAME", name, **kwargs)
709 def client_unblock(
710 self, client_id: int, error: bool = False, **kwargs
711 ) -> ResponseT:
712 """
713 Unblocks a connection by its client id.
714 If ``error`` is True, unblocks the client with a special error message.
715 If ``error`` is False (default), the client is unblocked using the
716 regular timeout mechanism.
718 For more information see https://redis.io/commands/client-unblock
719 """
720 args = ["CLIENT UNBLOCK", int(client_id)]
721 if error:
722 args.append(b"ERROR")
723 return self.execute_command(*args, **kwargs)
725 def client_pause(self, timeout: int, all: bool = True, **kwargs) -> ResponseT:
726 """
727 Suspend all the Redis clients for the specified amount of time
728 :param timeout: milliseconds to pause clients
730 For more information see https://redis.io/commands/client-pause
731 :param all: If true (default) all client commands are blocked.
732 otherwise, clients are only blocked if they attempt to execute
733 a write command.
734 For the WRITE mode, some commands have special behavior:
735 EVAL/EVALSHA: Will block client for all scripts.
736 PUBLISH: Will block client.
737 PFCOUNT: Will block client.
738 WAIT: Acknowledgments will be delayed, so this command will
739 appear blocked.
740 """
741 args = ["CLIENT PAUSE", str(timeout)]
742 if not isinstance(timeout, int):
743 raise DataError("CLIENT PAUSE timeout must be an integer")
744 if not all:
745 args.append("WRITE")
746 return self.execute_command(*args, **kwargs)
748 def client_unpause(self, **kwargs) -> ResponseT:
749 """
750 Unpause all redis clients
752 For more information see https://redis.io/commands/client-unpause
753 """
754 return self.execute_command("CLIENT UNPAUSE", **kwargs)
756 def client_no_evict(self, mode: str) -> Union[Awaitable[str], str]:
757 """
758 Sets the client eviction mode for the current connection.
760 For more information see https://redis.io/commands/client-no-evict
761 """
762 return self.execute_command("CLIENT NO-EVICT", mode)
764 def command(self, **kwargs):
765 """
766 Returns dict reply of details about all Redis commands.
768 For more information see https://redis.io/commands/command
769 """
770 return self.execute_command("COMMAND", **kwargs)
772 def command_info(self, **kwargs) -> None:
773 raise NotImplementedError(
774 "COMMAND INFO is intentionally not implemented in the client."
775 )
777 def command_count(self, **kwargs) -> ResponseT:
778 return self.execute_command("COMMAND COUNT", **kwargs)
780 def command_list(
781 self,
782 module: Optional[str] = None,
783 category: Optional[str] = None,
784 pattern: Optional[str] = None,
785 ) -> ResponseT:
786 """
787 Return an array of the server's command names.
788 You can use one of the following filters:
789 ``module``: get the commands that belong to the module
790 ``category``: get the commands in the ACL category
791 ``pattern``: get the commands that match the given pattern
793 For more information see https://redis.io/commands/command-list/
794 """
795 pieces = []
796 if module is not None:
797 pieces.extend(["MODULE", module])
798 if category is not None:
799 pieces.extend(["ACLCAT", category])
800 if pattern is not None:
801 pieces.extend(["PATTERN", pattern])
803 if pieces:
804 pieces.insert(0, "FILTERBY")
806 return self.execute_command("COMMAND LIST", *pieces)
808 def command_getkeysandflags(self, *args: List[str]) -> List[Union[str, List[str]]]:
809 """
810 Returns array of keys from a full Redis command and their usage flags.
812 For more information see https://redis.io/commands/command-getkeysandflags
813 """
814 return self.execute_command("COMMAND GETKEYSANDFLAGS", *args)
816 def command_docs(self, *args):
817 """
818 This function throws a NotImplementedError since it is intentionally
819 not supported.
820 """
821 raise NotImplementedError(
822 "COMMAND DOCS is intentionally not implemented in the client."
823 )
825 def config_get(
826 self, pattern: PatternT = "*", *args: List[PatternT], **kwargs
827 ) -> ResponseT:
828 """
829 Return a dictionary of configuration based on the ``pattern``
831 For more information see https://redis.io/commands/config-get
832 """
833 return self.execute_command("CONFIG GET", pattern, *args, **kwargs)
835 def config_set(
836 self,
837 name: KeyT,
838 value: EncodableT,
839 *args: List[Union[KeyT, EncodableT]],
840 **kwargs,
841 ) -> ResponseT:
842 """Set config item ``name`` with ``value``
844 For more information see https://redis.io/commands/config-set
845 """
846 return self.execute_command("CONFIG SET", name, value, *args, **kwargs)
848 def config_resetstat(self, **kwargs) -> ResponseT:
849 """
850 Reset runtime statistics
852 For more information see https://redis.io/commands/config-resetstat
853 """
854 return self.execute_command("CONFIG RESETSTAT", **kwargs)
856 def config_rewrite(self, **kwargs) -> ResponseT:
857 """
858 Rewrite config file with the minimal change to reflect running config.
860 For more information see https://redis.io/commands/config-rewrite
861 """
862 return self.execute_command("CONFIG REWRITE", **kwargs)
864 def dbsize(self, **kwargs) -> ResponseT:
865 """
866 Returns the number of keys in the current database
868 For more information see https://redis.io/commands/dbsize
869 """
870 return self.execute_command("DBSIZE", **kwargs)
872 def debug_object(self, key: KeyT, **kwargs) -> ResponseT:
873 """
874 Returns version specific meta information about a given key
876 For more information see https://redis.io/commands/debug-object
877 """
878 return self.execute_command("DEBUG OBJECT", key, **kwargs)
880 def debug_segfault(self, **kwargs) -> None:
881 raise NotImplementedError(
882 """
883 DEBUG SEGFAULT is intentionally not implemented in the client.
885 For more information see https://redis.io/commands/debug-segfault
886 """
887 )
889 def echo(self, value: EncodableT, **kwargs) -> ResponseT:
890 """
891 Echo the string back from the server
893 For more information see https://redis.io/commands/echo
894 """
895 return self.execute_command("ECHO", value, **kwargs)
897 def flushall(self, asynchronous: bool = False, **kwargs) -> ResponseT:
898 """
899 Delete all keys in all databases on the current host.
901 ``asynchronous`` indicates whether the operation is
902 executed asynchronously by the server.
904 For more information see https://redis.io/commands/flushall
905 """
906 args = []
907 if asynchronous:
908 args.append(b"ASYNC")
909 return self.execute_command("FLUSHALL", *args, **kwargs)
911 def flushdb(self, asynchronous: bool = False, **kwargs) -> ResponseT:
912 """
913 Delete all keys in the current database.
915 ``asynchronous`` indicates whether the operation is
916 executed asynchronously by the server.
918 For more information see https://redis.io/commands/flushdb
919 """
920 args = []
921 if asynchronous:
922 args.append(b"ASYNC")
923 return self.execute_command("FLUSHDB", *args, **kwargs)
925 def sync(self) -> ResponseT:
926 """
927 Initiates a replication stream from the master.
929 For more information see https://redis.io/commands/sync
930 """
931 from redis.client import NEVER_DECODE
933 options = {}
934 options[NEVER_DECODE] = []
935 return self.execute_command("SYNC", **options)
937 def psync(self, replicationid: str, offset: int):
938 """
939 Initiates a replication stream from the master.
940 Newer version for `sync`.
942 For more information see https://redis.io/commands/sync
943 """
944 from redis.client import NEVER_DECODE
946 options = {}
947 options[NEVER_DECODE] = []
948 return self.execute_command("PSYNC", replicationid, offset, **options)
950 def swapdb(self, first: int, second: int, **kwargs) -> ResponseT:
951 """
952 Swap two databases
954 For more information see https://redis.io/commands/swapdb
955 """
956 return self.execute_command("SWAPDB", first, second, **kwargs)
958 def select(self, index: int, **kwargs) -> ResponseT:
959 """Select the Redis logical database at index.
961 See: https://redis.io/commands/select
962 """
963 return self.execute_command("SELECT", index, **kwargs)
965 def info(
966 self, section: Union[str, None] = None, *args: List[str], **kwargs
967 ) -> ResponseT:
968 """
969 Returns a dictionary containing information about the Redis server
971 The ``section`` option can be used to select a specific section
972 of information
974 The section option is not supported by older versions of Redis Server,
975 and will generate ResponseError
977 For more information see https://redis.io/commands/info
978 """
979 if section is None:
980 return self.execute_command("INFO", **kwargs)
981 else:
982 return self.execute_command("INFO", section, *args, **kwargs)
984 def lastsave(self, **kwargs) -> ResponseT:
985 """
986 Return a Python datetime object representing the last time the
987 Redis database was saved to disk
989 For more information see https://redis.io/commands/lastsave
990 """
991 return self.execute_command("LASTSAVE", **kwargs)
993 def latency_doctor(self):
994 """Raise a NotImplementedError, as the client will not support LATENCY DOCTOR.
995 This funcion is best used within the redis-cli.
997 For more information see https://redis.io/commands/latency-doctor
998 """
999 raise NotImplementedError(
1000 """
1001 LATENCY DOCTOR is intentionally not implemented in the client.
1003 For more information see https://redis.io/commands/latency-doctor
1004 """
1005 )
1007 def latency_graph(self):
1008 """Raise a NotImplementedError, as the client will not support LATENCY GRAPH.
1009 This funcion is best used within the redis-cli.
1011 For more information see https://redis.io/commands/latency-graph.
1012 """
1013 raise NotImplementedError(
1014 """
1015 LATENCY GRAPH is intentionally not implemented in the client.
1017 For more information see https://redis.io/commands/latency-graph
1018 """
1019 )
1021 def lolwut(self, *version_numbers: Union[str, float], **kwargs) -> ResponseT:
1022 """
1023 Get the Redis version and a piece of generative computer art
1025 See: https://redis.io/commands/lolwut
1026 """
1027 if version_numbers:
1028 return self.execute_command("LOLWUT VERSION", *version_numbers, **kwargs)
1029 else:
1030 return self.execute_command("LOLWUT", **kwargs)
1032 def reset(self) -> ResponseT:
1033 """Perform a full reset on the connection's server side contenxt.
1035 See: https://redis.io/commands/reset
1036 """
1037 return self.execute_command("RESET")
1039 def migrate(
1040 self,
1041 host: str,
1042 port: int,
1043 keys: KeysT,
1044 destination_db: int,
1045 timeout: int,
1046 copy: bool = False,
1047 replace: bool = False,
1048 auth: Union[str, None] = None,
1049 **kwargs,
1050 ) -> ResponseT:
1051 """
1052 Migrate 1 or more keys from the current Redis server to a different
1053 server specified by the ``host``, ``port`` and ``destination_db``.
1055 The ``timeout``, specified in milliseconds, indicates the maximum
1056 time the connection between the two servers can be idle before the
1057 command is interrupted.
1059 If ``copy`` is True, the specified ``keys`` are NOT deleted from
1060 the source server.
1062 If ``replace`` is True, this operation will overwrite the keys
1063 on the destination server if they exist.
1065 If ``auth`` is specified, authenticate to the destination server with
1066 the password provided.
1068 For more information see https://redis.io/commands/migrate
1069 """
1070 keys = list_or_args(keys, [])
1071 if not keys:
1072 raise DataError("MIGRATE requires at least one key")
1073 pieces = []
1074 if copy:
1075 pieces.append(b"COPY")
1076 if replace:
1077 pieces.append(b"REPLACE")
1078 if auth:
1079 pieces.append(b"AUTH")
1080 pieces.append(auth)
1081 pieces.append(b"KEYS")
1082 pieces.extend(keys)
1083 return self.execute_command(
1084 "MIGRATE", host, port, "", destination_db, timeout, *pieces, **kwargs
1085 )
1087 def object(self, infotype: str, key: KeyT, **kwargs) -> ResponseT:
1088 """
1089 Return the encoding, idletime, or refcount about the key
1090 """
1091 return self.execute_command(
1092 "OBJECT", infotype, key, infotype=infotype, **kwargs
1093 )
1095 def memory_doctor(self, **kwargs) -> None:
1096 raise NotImplementedError(
1097 """
1098 MEMORY DOCTOR is intentionally not implemented in the client.
1100 For more information see https://redis.io/commands/memory-doctor
1101 """
1102 )
1104 def memory_help(self, **kwargs) -> None:
1105 raise NotImplementedError(
1106 """
1107 MEMORY HELP is intentionally not implemented in the client.
1109 For more information see https://redis.io/commands/memory-help
1110 """
1111 )
1113 def memory_stats(self, **kwargs) -> ResponseT:
1114 """
1115 Return a dictionary of memory stats
1117 For more information see https://redis.io/commands/memory-stats
1118 """
1119 return self.execute_command("MEMORY STATS", **kwargs)
1121 def memory_malloc_stats(self, **kwargs) -> ResponseT:
1122 """
1123 Return an internal statistics report from the memory allocator.
1125 See: https://redis.io/commands/memory-malloc-stats
1126 """
1127 return self.execute_command("MEMORY MALLOC-STATS", **kwargs)
1129 def memory_usage(
1130 self, key: KeyT, samples: Union[int, None] = None, **kwargs
1131 ) -> ResponseT:
1132 """
1133 Return the total memory usage for key, its value and associated
1134 administrative overheads.
1136 For nested data structures, ``samples`` is the number of elements to
1137 sample. If left unspecified, the server's default is 5. Use 0 to sample
1138 all elements.
1140 For more information see https://redis.io/commands/memory-usage
1141 """
1142 args = []
1143 if isinstance(samples, int):
1144 args.extend([b"SAMPLES", samples])
1145 return self.execute_command("MEMORY USAGE", key, *args, **kwargs)
1147 def memory_purge(self, **kwargs) -> ResponseT:
1148 """
1149 Attempts to purge dirty pages for reclamation by allocator
1151 For more information see https://redis.io/commands/memory-purge
1152 """
1153 return self.execute_command("MEMORY PURGE", **kwargs)
1155 def latency_histogram(self, *args):
1156 """
1157 This function throws a NotImplementedError since it is intentionally
1158 not supported.
1159 """
1160 raise NotImplementedError(
1161 "LATENCY HISTOGRAM is intentionally not implemented in the client."
1162 )
1164 def latency_history(self, event: str) -> ResponseT:
1165 """
1166 Returns the raw data of the ``event``'s latency spikes time series.
1168 For more information see https://redis.io/commands/latency-history
1169 """
1170 return self.execute_command("LATENCY HISTORY", event)
1172 def latency_latest(self) -> ResponseT:
1173 """
1174 Reports the latest latency events logged.
1176 For more information see https://redis.io/commands/latency-latest
1177 """
1178 return self.execute_command("LATENCY LATEST")
1180 def latency_reset(self, *events: str) -> ResponseT:
1181 """
1182 Resets the latency spikes time series of all, or only some, events.
1184 For more information see https://redis.io/commands/latency-reset
1185 """
1186 return self.execute_command("LATENCY RESET", *events)
1188 def ping(self, **kwargs) -> ResponseT:
1189 """
1190 Ping the Redis server
1192 For more information see https://redis.io/commands/ping
1193 """
1194 return self.execute_command("PING", **kwargs)
1196 def quit(self, **kwargs) -> ResponseT:
1197 """
1198 Ask the server to close the connection.
1200 For more information see https://redis.io/commands/quit
1201 """
1202 return self.execute_command("QUIT", **kwargs)
1204 def replicaof(self, *args, **kwargs) -> ResponseT:
1205 """
1206 Update the replication settings of a redis replica, on the fly.
1207 Examples of valid arguments include:
1208 NO ONE (set no replication)
1209 host port (set to the host and port of a redis server)
1211 For more information see https://redis.io/commands/replicaof
1212 """
1213 return self.execute_command("REPLICAOF", *args, **kwargs)
1215 def save(self, **kwargs) -> ResponseT:
1216 """
1217 Tell the Redis server to save its data to disk,
1218 blocking until the save is complete
1220 For more information see https://redis.io/commands/save
1221 """
1222 return self.execute_command("SAVE", **kwargs)
1224 def shutdown(
1225 self,
1226 save: bool = False,
1227 nosave: bool = False,
1228 now: bool = False,
1229 force: bool = False,
1230 abort: bool = False,
1231 **kwargs,
1232 ) -> None:
1233 """Shutdown the Redis server. If Redis has persistence configured,
1234 data will be flushed before shutdown.
1235 It is possible to specify modifiers to alter the behavior of the command:
1236 ``save`` will force a DB saving operation even if no save points are configured.
1237 ``nosave`` will prevent a DB saving operation even if one or more save points
1238 are configured.
1239 ``now`` skips waiting for lagging replicas, i.e. it bypasses the first step in
1240 the shutdown sequence.
1241 ``force`` ignores any errors that would normally prevent the server from exiting
1242 ``abort`` cancels an ongoing shutdown and cannot be combined with other flags.
1244 For more information see https://redis.io/commands/shutdown
1245 """
1246 if save and nosave:
1247 raise DataError("SHUTDOWN save and nosave cannot both be set")
1248 args = ["SHUTDOWN"]
1249 if save:
1250 args.append("SAVE")
1251 if nosave:
1252 args.append("NOSAVE")
1253 if now:
1254 args.append("NOW")
1255 if force:
1256 args.append("FORCE")
1257 if abort:
1258 args.append("ABORT")
1259 try:
1260 self.execute_command(*args, **kwargs)
1261 except ConnectionError:
1262 # a ConnectionError here is expected
1263 return
1264 raise RedisError("SHUTDOWN seems to have failed.")
1266 def slaveof(
1267 self, host: Union[str, None] = None, port: Union[int, None] = None, **kwargs
1268 ) -> ResponseT:
1269 """
1270 Set the server to be a replicated slave of the instance identified
1271 by the ``host`` and ``port``. If called without arguments, the
1272 instance is promoted to a master instead.
1274 For more information see https://redis.io/commands/slaveof
1275 """
1276 if host is None and port is None:
1277 return self.execute_command("SLAVEOF", b"NO", b"ONE", **kwargs)
1278 return self.execute_command("SLAVEOF", host, port, **kwargs)
1280 def slowlog_get(self, num: Union[int, None] = None, **kwargs) -> ResponseT:
1281 """
1282 Get the entries from the slowlog. If ``num`` is specified, get the
1283 most recent ``num`` items.
1285 For more information see https://redis.io/commands/slowlog-get
1286 """
1287 from redis.client import NEVER_DECODE
1289 args = ["SLOWLOG GET"]
1290 if num is not None:
1291 args.append(num)
1292 decode_responses = self.get_connection_kwargs().get("decode_responses", False)
1293 if decode_responses is True:
1294 kwargs[NEVER_DECODE] = []
1295 return self.execute_command(*args, **kwargs)
1297 def slowlog_len(self, **kwargs) -> ResponseT:
1298 """
1299 Get the number of items in the slowlog
1301 For more information see https://redis.io/commands/slowlog-len
1302 """
1303 return self.execute_command("SLOWLOG LEN", **kwargs)
1305 def slowlog_reset(self, **kwargs) -> ResponseT:
1306 """
1307 Remove all items in the slowlog
1309 For more information see https://redis.io/commands/slowlog-reset
1310 """
1311 return self.execute_command("SLOWLOG RESET", **kwargs)
1313 def time(self, **kwargs) -> ResponseT:
1314 """
1315 Returns the server time as a 2-item tuple of ints:
1316 (seconds since epoch, microseconds into this second).
1318 For more information see https://redis.io/commands/time
1319 """
1320 return self.execute_command("TIME", **kwargs)
1322 def wait(self, num_replicas: int, timeout: int, **kwargs) -> ResponseT:
1323 """
1324 Redis synchronous replication
1325 That returns the number of replicas that processed the query when
1326 we finally have at least ``num_replicas``, or when the ``timeout`` was
1327 reached.
1329 For more information see https://redis.io/commands/wait
1330 """
1331 return self.execute_command("WAIT", num_replicas, timeout, **kwargs)
1333 def hello(self):
1334 """
1335 This function throws a NotImplementedError since it is intentionally
1336 not supported.
1337 """
1338 raise NotImplementedError(
1339 "HELLO is intentionally not implemented in the client."
1340 )
1342 def failover(self):
1343 """
1344 This function throws a NotImplementedError since it is intentionally
1345 not supported.
1346 """
1347 raise NotImplementedError(
1348 "FAILOVER is intentionally not implemented in the client."
1349 )
1352AsyncManagementCommands = ManagementCommands
1355class AsyncManagementCommands(ManagementCommands):
1356 async def command_info(self, **kwargs) -> None:
1357 return super().command_info(**kwargs)
1359 async def debug_segfault(self, **kwargs) -> None:
1360 return super().debug_segfault(**kwargs)
1362 async def memory_doctor(self, **kwargs) -> None:
1363 return super().memory_doctor(**kwargs)
1365 async def memory_help(self, **kwargs) -> None:
1366 return super().memory_help(**kwargs)
1368 async def shutdown(
1369 self,
1370 save: bool = False,
1371 nosave: bool = False,
1372 now: bool = False,
1373 force: bool = False,
1374 abort: bool = False,
1375 **kwargs,
1376 ) -> None:
1377 """Shutdown the Redis server. If Redis has persistence configured,
1378 data will be flushed before shutdown. If the "save" option is set,
1379 a data flush will be attempted even if there is no persistence
1380 configured. If the "nosave" option is set, no data flush will be
1381 attempted. The "save" and "nosave" options cannot both be set.
1383 For more information see https://redis.io/commands/shutdown
1384 """
1385 if save and nosave:
1386 raise DataError("SHUTDOWN save and nosave cannot both be set")
1387 args = ["SHUTDOWN"]
1388 if save:
1389 args.append("SAVE")
1390 if nosave:
1391 args.append("NOSAVE")
1392 if now:
1393 args.append("NOW")
1394 if force:
1395 args.append("FORCE")
1396 if abort:
1397 args.append("ABORT")
1398 try:
1399 await self.execute_command(*args, **kwargs)
1400 except ConnectionError:
1401 # a ConnectionError here is expected
1402 return
1403 raise RedisError("SHUTDOWN seems to have failed.")
1406class BitFieldOperation:
1407 """
1408 Command builder for BITFIELD commands.
1409 """
1411 def __init__(
1412 self,
1413 client: Union["Redis", "AsyncRedis"],
1414 key: str,
1415 default_overflow: Union[str, None] = None,
1416 ):
1417 self.client = client
1418 self.key = key
1419 self._default_overflow = default_overflow
1420 # for typing purposes, run the following in constructor and in reset()
1421 self.operations: list[tuple[EncodableT, ...]] = []
1422 self._last_overflow = "WRAP"
1423 self.reset()
1425 def reset(self):
1426 """
1427 Reset the state of the instance to when it was constructed
1428 """
1429 self.operations = []
1430 self._last_overflow = "WRAP"
1431 self.overflow(self._default_overflow or self._last_overflow)
1433 def overflow(self, overflow: str):
1434 """
1435 Update the overflow algorithm of successive INCRBY operations
1436 :param overflow: Overflow algorithm, one of WRAP, SAT, FAIL. See the
1437 Redis docs for descriptions of these algorithmsself.
1438 :returns: a :py:class:`BitFieldOperation` instance.
1439 """
1440 overflow = overflow.upper()
1441 if overflow != self._last_overflow:
1442 self._last_overflow = overflow
1443 self.operations.append(("OVERFLOW", overflow))
1444 return self
1446 def incrby(
1447 self,
1448 fmt: str,
1449 offset: BitfieldOffsetT,
1450 increment: int,
1451 overflow: Union[str, None] = None,
1452 ):
1453 """
1454 Increment a bitfield by a given amount.
1455 :param fmt: format-string for the bitfield being updated, e.g. 'u8'
1456 for an unsigned 8-bit integer.
1457 :param offset: offset (in number of bits). If prefixed with a
1458 '#', this is an offset multiplier, e.g. given the arguments
1459 fmt='u8', offset='#2', the offset will be 16.
1460 :param int increment: value to increment the bitfield by.
1461 :param str overflow: overflow algorithm. Defaults to WRAP, but other
1462 acceptable values are SAT and FAIL. See the Redis docs for
1463 descriptions of these algorithms.
1464 :returns: a :py:class:`BitFieldOperation` instance.
1465 """
1466 if overflow is not None:
1467 self.overflow(overflow)
1469 self.operations.append(("INCRBY", fmt, offset, increment))
1470 return self
1472 def get(self, fmt: str, offset: BitfieldOffsetT):
1473 """
1474 Get the value of a given bitfield.
1475 :param fmt: format-string for the bitfield being read, e.g. 'u8' for
1476 an unsigned 8-bit integer.
1477 :param offset: offset (in number of bits). If prefixed with a
1478 '#', this is an offset multiplier, e.g. given the arguments
1479 fmt='u8', offset='#2', the offset will be 16.
1480 :returns: a :py:class:`BitFieldOperation` instance.
1481 """
1482 self.operations.append(("GET", fmt, offset))
1483 return self
1485 def set(self, fmt: str, offset: BitfieldOffsetT, value: int):
1486 """
1487 Set the value of a given bitfield.
1488 :param fmt: format-string for the bitfield being read, e.g. 'u8' for
1489 an unsigned 8-bit integer.
1490 :param offset: offset (in number of bits). If prefixed with a
1491 '#', this is an offset multiplier, e.g. given the arguments
1492 fmt='u8', offset='#2', the offset will be 16.
1493 :param int value: value to set at the given position.
1494 :returns: a :py:class:`BitFieldOperation` instance.
1495 """
1496 self.operations.append(("SET", fmt, offset, value))
1497 return self
1499 @property
1500 def command(self):
1501 cmd = ["BITFIELD", self.key]
1502 for ops in self.operations:
1503 cmd.extend(ops)
1504 return cmd
1506 def execute(self) -> ResponseT:
1507 """
1508 Execute the operation(s) in a single BITFIELD command. The return value
1509 is a list of values corresponding to each operation. If the client
1510 used to create this instance was a pipeline, the list of values
1511 will be present within the pipeline's execute.
1512 """
1513 command = self.command
1514 self.reset()
1515 return self.client.execute_command(*command)
1518class BasicKeyCommands(CommandsProtocol):
1519 """
1520 Redis basic key-based commands
1521 """
1523 def append(self, key: KeyT, value: EncodableT) -> ResponseT:
1524 """
1525 Appends the string ``value`` to the value at ``key``. If ``key``
1526 doesn't already exist, create it with a value of ``value``.
1527 Returns the new length of the value at ``key``.
1529 For more information see https://redis.io/commands/append
1530 """
1531 return self.execute_command("APPEND", key, value)
1533 def bitcount(
1534 self,
1535 key: KeyT,
1536 start: Union[int, None] = None,
1537 end: Union[int, None] = None,
1538 mode: Optional[str] = None,
1539 ) -> ResponseT:
1540 """
1541 Returns the count of set bits in the value of ``key``. Optional
1542 ``start`` and ``end`` parameters indicate which bytes to consider
1544 For more information see https://redis.io/commands/bitcount
1545 """
1546 params = [key]
1547 if start is not None and end is not None:
1548 params.append(start)
1549 params.append(end)
1550 elif (start is not None and end is None) or (end is not None and start is None):
1551 raise DataError("Both start and end must be specified")
1552 if mode is not None:
1553 params.append(mode)
1554 return self.execute_command("BITCOUNT", *params)
1556 def bitfield(
1557 self: Union["Redis", "AsyncRedis"],
1558 key: KeyT,
1559 default_overflow: Union[str, None] = None,
1560 ) -> BitFieldOperation:
1561 """
1562 Return a BitFieldOperation instance to conveniently construct one or
1563 more bitfield operations on ``key``.
1565 For more information see https://redis.io/commands/bitfield
1566 """
1567 return BitFieldOperation(self, key, default_overflow=default_overflow)
1569 def bitfield_ro(
1570 self: Union["Redis", "AsyncRedis"],
1571 key: KeyT,
1572 encoding: str,
1573 offset: BitfieldOffsetT,
1574 items: Optional[list] = None,
1575 ) -> ResponseT:
1576 """
1577 Return an array of the specified bitfield values
1578 where the first value is found using ``encoding`` and ``offset``
1579 parameters and remaining values are result of corresponding
1580 encoding/offset pairs in optional list ``items``
1581 Read-only variant of the BITFIELD command.
1583 For more information see https://redis.io/commands/bitfield_ro
1584 """
1585 params = [key, "GET", encoding, offset]
1587 items = items or []
1588 for encoding, offset in items:
1589 params.extend(["GET", encoding, offset])
1590 return self.execute_command("BITFIELD_RO", *params)
1592 def bitop(self, operation: str, dest: KeyT, *keys: KeyT) -> ResponseT:
1593 """
1594 Perform a bitwise operation using ``operation`` between ``keys`` and
1595 store the result in ``dest``.
1597 For more information see https://redis.io/commands/bitop
1598 """
1599 return self.execute_command("BITOP", operation, dest, *keys)
1601 def bitpos(
1602 self,
1603 key: KeyT,
1604 bit: int,
1605 start: Union[int, None] = None,
1606 end: Union[int, None] = None,
1607 mode: Optional[str] = None,
1608 ) -> ResponseT:
1609 """
1610 Return the position of the first bit set to 1 or 0 in a string.
1611 ``start`` and ``end`` defines search range. The range is interpreted
1612 as a range of bytes and not a range of bits, so start=0 and end=2
1613 means to look at the first three bytes.
1615 For more information see https://redis.io/commands/bitpos
1616 """
1617 if bit not in (0, 1):
1618 raise DataError("bit must be 0 or 1")
1619 params = [key, bit]
1621 start is not None and params.append(start)
1623 if start is not None and end is not None:
1624 params.append(end)
1625 elif start is None and end is not None:
1626 raise DataError("start argument is not set, when end is specified")
1628 if mode is not None:
1629 params.append(mode)
1630 return self.execute_command("BITPOS", *params)
1632 def copy(
1633 self,
1634 source: str,
1635 destination: str,
1636 destination_db: Union[str, None] = None,
1637 replace: bool = False,
1638 ) -> ResponseT:
1639 """
1640 Copy the value stored in the ``source`` key to the ``destination`` key.
1642 ``destination_db`` an alternative destination database. By default,
1643 the ``destination`` key is created in the source Redis database.
1645 ``replace`` whether the ``destination`` key should be removed before
1646 copying the value to it. By default, the value is not copied if
1647 the ``destination`` key already exists.
1649 For more information see https://redis.io/commands/copy
1650 """
1651 params = [source, destination]
1652 if destination_db is not None:
1653 params.extend(["DB", destination_db])
1654 if replace:
1655 params.append("REPLACE")
1656 return self.execute_command("COPY", *params)
1658 def decrby(self, name: KeyT, amount: int = 1) -> ResponseT:
1659 """
1660 Decrements the value of ``key`` by ``amount``. If no key exists,
1661 the value will be initialized as 0 - ``amount``
1663 For more information see https://redis.io/commands/decrby
1664 """
1665 return self.execute_command("DECRBY", name, amount)
1667 decr = decrby
1669 def delete(self, *names: KeyT) -> ResponseT:
1670 """
1671 Delete one or more keys specified by ``names``
1672 """
1673 return self.execute_command("DEL", *names)
1675 def __delitem__(self, name: KeyT):
1676 self.delete(name)
1678 def dump(self, name: KeyT) -> ResponseT:
1679 """
1680 Return a serialized version of the value stored at the specified key.
1681 If key does not exist a nil bulk reply is returned.
1683 For more information see https://redis.io/commands/dump
1684 """
1685 from redis.client import NEVER_DECODE
1687 options = {}
1688 options[NEVER_DECODE] = []
1689 return self.execute_command("DUMP", name, **options)
1691 def exists(self, *names: KeyT) -> ResponseT:
1692 """
1693 Returns the number of ``names`` that exist
1695 For more information see https://redis.io/commands/exists
1696 """
1697 return self.execute_command("EXISTS", *names)
1699 __contains__ = exists
1701 def expire(
1702 self,
1703 name: KeyT,
1704 time: ExpiryT,
1705 nx: bool = False,
1706 xx: bool = False,
1707 gt: bool = False,
1708 lt: bool = False,
1709 ) -> ResponseT:
1710 """
1711 Set an expire flag on key ``name`` for ``time`` seconds with given
1712 ``option``. ``time`` can be represented by an integer or a Python timedelta
1713 object.
1715 Valid options are:
1716 NX -> Set expiry only when the key has no expiry
1717 XX -> Set expiry only when the key has an existing expiry
1718 GT -> Set expiry only when the new expiry is greater than current one
1719 LT -> Set expiry only when the new expiry is less than current one
1721 For more information see https://redis.io/commands/expire
1722 """
1723 if isinstance(time, datetime.timedelta):
1724 time = int(time.total_seconds())
1726 exp_option = list()
1727 if nx:
1728 exp_option.append("NX")
1729 if xx:
1730 exp_option.append("XX")
1731 if gt:
1732 exp_option.append("GT")
1733 if lt:
1734 exp_option.append("LT")
1736 return self.execute_command("EXPIRE", name, time, *exp_option)
1738 def expireat(
1739 self,
1740 name: KeyT,
1741 when: AbsExpiryT,
1742 nx: bool = False,
1743 xx: bool = False,
1744 gt: bool = False,
1745 lt: bool = False,
1746 ) -> ResponseT:
1747 """
1748 Set an expire flag on key ``name`` with given ``option``. ``when``
1749 can be represented as an integer indicating unix time or a Python
1750 datetime object.
1752 Valid options are:
1753 -> NX -- Set expiry only when the key has no expiry
1754 -> XX -- Set expiry only when the key has an existing expiry
1755 -> GT -- Set expiry only when the new expiry is greater than current one
1756 -> LT -- Set expiry only when the new expiry is less than current one
1758 For more information see https://redis.io/commands/expireat
1759 """
1760 if isinstance(when, datetime.datetime):
1761 when = int(when.timestamp())
1763 exp_option = list()
1764 if nx:
1765 exp_option.append("NX")
1766 if xx:
1767 exp_option.append("XX")
1768 if gt:
1769 exp_option.append("GT")
1770 if lt:
1771 exp_option.append("LT")
1773 return self.execute_command("EXPIREAT", name, when, *exp_option)
1775 def expiretime(self, key: str) -> int:
1776 """
1777 Returns the absolute Unix timestamp (since January 1, 1970) in seconds
1778 at which the given key will expire.
1780 For more information see https://redis.io/commands/expiretime
1781 """
1782 return self.execute_command("EXPIRETIME", key)
1784 def get(self, name: KeyT) -> ResponseT:
1785 """
1786 Return the value at key ``name``, or None if the key doesn't exist
1788 For more information see https://redis.io/commands/get
1789 """
1790 return self.execute_command("GET", name)
1792 def getdel(self, name: KeyT) -> ResponseT:
1793 """
1794 Get the value at key ``name`` and delete the key. This command
1795 is similar to GET, except for the fact that it also deletes
1796 the key on success (if and only if the key's value type
1797 is a string).
1799 For more information see https://redis.io/commands/getdel
1800 """
1801 return self.execute_command("GETDEL", name)
1803 def getex(
1804 self,
1805 name: KeyT,
1806 ex: Union[ExpiryT, None] = None,
1807 px: Union[ExpiryT, None] = None,
1808 exat: Union[AbsExpiryT, None] = None,
1809 pxat: Union[AbsExpiryT, None] = None,
1810 persist: bool = False,
1811 ) -> ResponseT:
1812 """
1813 Get the value of key and optionally set its expiration.
1814 GETEX is similar to GET, but is a write command with
1815 additional options. All time parameters can be given as
1816 datetime.timedelta or integers.
1818 ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
1820 ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
1822 ``exat`` sets an expire flag on key ``name`` for ``ex`` seconds,
1823 specified in unix time.
1825 ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,
1826 specified in unix time.
1828 ``persist`` remove the time to live associated with ``name``.
1830 For more information see https://redis.io/commands/getex
1831 """
1833 opset = {ex, px, exat, pxat}
1834 if len(opset) > 2 or len(opset) > 1 and persist:
1835 raise DataError(
1836 "``ex``, ``px``, ``exat``, ``pxat``, "
1837 "and ``persist`` are mutually exclusive."
1838 )
1840 pieces: list[EncodableT] = []
1841 # similar to set command
1842 if ex is not None:
1843 pieces.append("EX")
1844 if isinstance(ex, datetime.timedelta):
1845 ex = int(ex.total_seconds())
1846 pieces.append(ex)
1847 if px is not None:
1848 pieces.append("PX")
1849 if isinstance(px, datetime.timedelta):
1850 px = int(px.total_seconds() * 1000)
1851 pieces.append(px)
1852 # similar to pexpireat command
1853 if exat is not None:
1854 pieces.append("EXAT")
1855 if isinstance(exat, datetime.datetime):
1856 exat = int(exat.timestamp())
1857 pieces.append(exat)
1858 if pxat is not None:
1859 pieces.append("PXAT")
1860 if isinstance(pxat, datetime.datetime):
1861 pxat = int(pxat.timestamp() * 1000)
1862 pieces.append(pxat)
1863 if persist:
1864 pieces.append("PERSIST")
1866 return self.execute_command("GETEX", name, *pieces)
1868 def __getitem__(self, name: KeyT):
1869 """
1870 Return the value at key ``name``, raises a KeyError if the key
1871 doesn't exist.
1872 """
1873 value = self.get(name)
1874 if value is not None:
1875 return value
1876 raise KeyError(name)
1878 def getbit(self, name: KeyT, offset: int) -> ResponseT:
1879 """
1880 Returns an integer indicating the value of ``offset`` in ``name``
1882 For more information see https://redis.io/commands/getbit
1883 """
1884 return self.execute_command("GETBIT", name, offset)
1886 def getrange(self, key: KeyT, start: int, end: int) -> ResponseT:
1887 """
1888 Returns the substring of the string value stored at ``key``,
1889 determined by the offsets ``start`` and ``end`` (both are inclusive)
1891 For more information see https://redis.io/commands/getrange
1892 """
1893 return self.execute_command("GETRANGE", key, start, end)
1895 def getset(self, name: KeyT, value: EncodableT) -> ResponseT:
1896 """
1897 Sets the value at key ``name`` to ``value``
1898 and returns the old value at key ``name`` atomically.
1900 As per Redis 6.2, GETSET is considered deprecated.
1901 Please use SET with GET parameter in new code.
1903 For more information see https://redis.io/commands/getset
1904 """
1905 return self.execute_command("GETSET", name, value)
1907 def incrby(self, name: KeyT, amount: int = 1) -> ResponseT:
1908 """
1909 Increments the value of ``key`` by ``amount``. If no key exists,
1910 the value will be initialized as ``amount``
1912 For more information see https://redis.io/commands/incrby
1913 """
1914 return self.execute_command("INCRBY", name, amount)
1916 incr = incrby
1918 def incrbyfloat(self, name: KeyT, amount: float = 1.0) -> ResponseT:
1919 """
1920 Increments the value at key ``name`` by floating ``amount``.
1921 If no key exists, the value will be initialized as ``amount``
1923 For more information see https://redis.io/commands/incrbyfloat
1924 """
1925 return self.execute_command("INCRBYFLOAT", name, amount)
1927 def keys(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
1928 """
1929 Returns a list of keys matching ``pattern``
1931 For more information see https://redis.io/commands/keys
1932 """
1933 return self.execute_command("KEYS", pattern, **kwargs)
1935 def lmove(
1936 self, first_list: str, second_list: str, src: str = "LEFT", dest: str = "RIGHT"
1937 ) -> ResponseT:
1938 """
1939 Atomically returns and removes the first/last element of a list,
1940 pushing it as the first/last element on the destination list.
1941 Returns the element being popped and pushed.
1943 For more information see https://redis.io/commands/lmove
1944 """
1945 params = [first_list, second_list, src, dest]
1946 return self.execute_command("LMOVE", *params)
1948 def blmove(
1949 self,
1950 first_list: str,
1951 second_list: str,
1952 timeout: int,
1953 src: str = "LEFT",
1954 dest: str = "RIGHT",
1955 ) -> ResponseT:
1956 """
1957 Blocking version of lmove.
1959 For more information see https://redis.io/commands/blmove
1960 """
1961 params = [first_list, second_list, src, dest, timeout]
1962 return self.execute_command("BLMOVE", *params)
1964 def mget(self, keys: KeysT, *args: EncodableT) -> ResponseT:
1965 """
1966 Returns a list of values ordered identically to ``keys``
1968 For more information see https://redis.io/commands/mget
1969 """
1970 from redis.client import EMPTY_RESPONSE
1972 args = list_or_args(keys, args)
1973 options = {}
1974 if not args:
1975 options[EMPTY_RESPONSE] = []
1976 return self.execute_command("MGET", *args, **options)
1978 def mset(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
1979 """
1980 Sets key/values based on a mapping. Mapping is a dictionary of
1981 key/value pairs. Both keys and values should be strings or types that
1982 can be cast to a string via str().
1984 For more information see https://redis.io/commands/mset
1985 """
1986 items = []
1987 for pair in mapping.items():
1988 items.extend(pair)
1989 return self.execute_command("MSET", *items)
1991 def msetnx(self, mapping: Mapping[AnyKeyT, EncodableT]) -> ResponseT:
1992 """
1993 Sets key/values based on a mapping if none of the keys are already set.
1994 Mapping is a dictionary of key/value pairs. Both keys and values
1995 should be strings or types that can be cast to a string via str().
1996 Returns a boolean indicating if the operation was successful.
1998 For more information see https://redis.io/commands/msetnx
1999 """
2000 items = []
2001 for pair in mapping.items():
2002 items.extend(pair)
2003 return self.execute_command("MSETNX", *items)
2005 def move(self, name: KeyT, db: int) -> ResponseT:
2006 """
2007 Moves the key ``name`` to a different Redis database ``db``
2009 For more information see https://redis.io/commands/move
2010 """
2011 return self.execute_command("MOVE", name, db)
2013 def persist(self, name: KeyT) -> ResponseT:
2014 """
2015 Removes an expiration on ``name``
2017 For more information see https://redis.io/commands/persist
2018 """
2019 return self.execute_command("PERSIST", name)
2021 def pexpire(
2022 self,
2023 name: KeyT,
2024 time: ExpiryT,
2025 nx: bool = False,
2026 xx: bool = False,
2027 gt: bool = False,
2028 lt: bool = False,
2029 ) -> ResponseT:
2030 """
2031 Set an expire flag on key ``name`` for ``time`` milliseconds
2032 with given ``option``. ``time`` can be represented by an
2033 integer or a Python timedelta object.
2035 Valid options are:
2036 NX -> Set expiry only when the key has no expiry
2037 XX -> Set expiry only when the key has an existing expiry
2038 GT -> Set expiry only when the new expiry is greater than current one
2039 LT -> Set expiry only when the new expiry is less than current one
2041 For more information see https://redis.io/commands/pexpire
2042 """
2043 if isinstance(time, datetime.timedelta):
2044 time = int(time.total_seconds() * 1000)
2046 exp_option = list()
2047 if nx:
2048 exp_option.append("NX")
2049 if xx:
2050 exp_option.append("XX")
2051 if gt:
2052 exp_option.append("GT")
2053 if lt:
2054 exp_option.append("LT")
2055 return self.execute_command("PEXPIRE", name, time, *exp_option)
2057 def pexpireat(
2058 self,
2059 name: KeyT,
2060 when: AbsExpiryT,
2061 nx: bool = False,
2062 xx: bool = False,
2063 gt: bool = False,
2064 lt: bool = False,
2065 ) -> ResponseT:
2066 """
2067 Set an expire flag on key ``name`` with given ``option``. ``when``
2068 can be represented as an integer representing unix time in
2069 milliseconds (unix time * 1000) or a Python datetime object.
2071 Valid options are:
2072 NX -> Set expiry only when the key has no expiry
2073 XX -> Set expiry only when the key has an existing expiry
2074 GT -> Set expiry only when the new expiry is greater than current one
2075 LT -> Set expiry only when the new expiry is less than current one
2077 For more information see https://redis.io/commands/pexpireat
2078 """
2079 if isinstance(when, datetime.datetime):
2080 when = int(when.timestamp() * 1000)
2081 exp_option = list()
2082 if nx:
2083 exp_option.append("NX")
2084 if xx:
2085 exp_option.append("XX")
2086 if gt:
2087 exp_option.append("GT")
2088 if lt:
2089 exp_option.append("LT")
2090 return self.execute_command("PEXPIREAT", name, when, *exp_option)
2092 def pexpiretime(self, key: str) -> int:
2093 """
2094 Returns the absolute Unix timestamp (since January 1, 1970) in milliseconds
2095 at which the given key will expire.
2097 For more information see https://redis.io/commands/pexpiretime
2098 """
2099 return self.execute_command("PEXPIRETIME", key)
2101 def psetex(self, name: KeyT, time_ms: ExpiryT, value: EncodableT):
2102 """
2103 Set the value of key ``name`` to ``value`` that expires in ``time_ms``
2104 milliseconds. ``time_ms`` can be represented by an integer or a Python
2105 timedelta object
2107 For more information see https://redis.io/commands/psetex
2108 """
2109 if isinstance(time_ms, datetime.timedelta):
2110 time_ms = int(time_ms.total_seconds() * 1000)
2111 return self.execute_command("PSETEX", name, time_ms, value)
2113 def pttl(self, name: KeyT) -> ResponseT:
2114 """
2115 Returns the number of milliseconds until the key ``name`` will expire
2117 For more information see https://redis.io/commands/pttl
2118 """
2119 return self.execute_command("PTTL", name)
2121 def hrandfield(
2122 self, key: str, count: int = None, withvalues: bool = False
2123 ) -> ResponseT:
2124 """
2125 Return a random field from the hash value stored at key.
2127 count: if the argument is positive, return an array of distinct fields.
2128 If called with a negative count, the behavior changes and the command
2129 is allowed to return the same field multiple times. In this case,
2130 the number of returned fields is the absolute value of the
2131 specified count.
2132 withvalues: The optional WITHVALUES modifier changes the reply so it
2133 includes the respective values of the randomly selected hash fields.
2135 For more information see https://redis.io/commands/hrandfield
2136 """
2137 params = []
2138 if count is not None:
2139 params.append(count)
2140 if withvalues:
2141 params.append("WITHVALUES")
2143 return self.execute_command("HRANDFIELD", key, *params)
2145 def randomkey(self, **kwargs) -> ResponseT:
2146 """
2147 Returns the name of a random key
2149 For more information see https://redis.io/commands/randomkey
2150 """
2151 return self.execute_command("RANDOMKEY", **kwargs)
2153 def rename(self, src: KeyT, dst: KeyT) -> ResponseT:
2154 """
2155 Rename key ``src`` to ``dst``
2157 For more information see https://redis.io/commands/rename
2158 """
2159 return self.execute_command("RENAME", src, dst)
2161 def renamenx(self, src: KeyT, dst: KeyT):
2162 """
2163 Rename key ``src`` to ``dst`` if ``dst`` doesn't already exist
2165 For more information see https://redis.io/commands/renamenx
2166 """
2167 return self.execute_command("RENAMENX", src, dst)
2169 def restore(
2170 self,
2171 name: KeyT,
2172 ttl: float,
2173 value: EncodableT,
2174 replace: bool = False,
2175 absttl: bool = False,
2176 idletime: Union[int, None] = None,
2177 frequency: Union[int, None] = None,
2178 ) -> ResponseT:
2179 """
2180 Create a key using the provided serialized value, previously obtained
2181 using DUMP.
2183 ``replace`` allows an existing key on ``name`` to be overridden. If
2184 it's not specified an error is raised on collision.
2186 ``absttl`` if True, specified ``ttl`` should represent an absolute Unix
2187 timestamp in milliseconds in which the key will expire. (Redis 5.0 or
2188 greater).
2190 ``idletime`` Used for eviction, this is the number of seconds the
2191 key must be idle, prior to execution.
2193 ``frequency`` Used for eviction, this is the frequency counter of
2194 the object stored at the key, prior to execution.
2196 For more information see https://redis.io/commands/restore
2197 """
2198 params = [name, ttl, value]
2199 if replace:
2200 params.append("REPLACE")
2201 if absttl:
2202 params.append("ABSTTL")
2203 if idletime is not None:
2204 params.append("IDLETIME")
2205 try:
2206 params.append(int(idletime))
2207 except ValueError:
2208 raise DataError("idletimemust be an integer")
2210 if frequency is not None:
2211 params.append("FREQ")
2212 try:
2213 params.append(int(frequency))
2214 except ValueError:
2215 raise DataError("frequency must be an integer")
2217 return self.execute_command("RESTORE", *params)
2219 def set(
2220 self,
2221 name: KeyT,
2222 value: EncodableT,
2223 ex: Union[ExpiryT, None] = None,
2224 px: Union[ExpiryT, None] = None,
2225 nx: bool = False,
2226 xx: bool = False,
2227 keepttl: bool = False,
2228 get: bool = False,
2229 exat: Union[AbsExpiryT, None] = None,
2230 pxat: Union[AbsExpiryT, None] = None,
2231 ) -> ResponseT:
2232 """
2233 Set the value at key ``name`` to ``value``
2235 ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.
2237 ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.
2239 ``nx`` if set to True, set the value at key ``name`` to ``value`` only
2240 if it does not exist.
2242 ``xx`` if set to True, set the value at key ``name`` to ``value`` only
2243 if it already exists.
2245 ``keepttl`` if True, retain the time to live associated with the key.
2246 (Available since Redis 6.0)
2248 ``get`` if True, set the value at key ``name`` to ``value`` and return
2249 the old value stored at key, or None if the key did not exist.
2250 (Available since Redis 6.2)
2252 ``exat`` sets an expire flag on key ``name`` for ``ex`` seconds,
2253 specified in unix time.
2255 ``pxat`` sets an expire flag on key ``name`` for ``ex`` milliseconds,
2256 specified in unix time.
2258 For more information see https://redis.io/commands/set
2259 """
2260 pieces: list[EncodableT] = [name, value]
2261 options = {}
2262 if ex is not None:
2263 pieces.append("EX")
2264 if isinstance(ex, datetime.timedelta):
2265 pieces.append(int(ex.total_seconds()))
2266 elif isinstance(ex, int):
2267 pieces.append(ex)
2268 elif isinstance(ex, str) and ex.isdigit():
2269 pieces.append(int(ex))
2270 else:
2271 raise DataError("ex must be datetime.timedelta or int")
2272 if px is not None:
2273 pieces.append("PX")
2274 if isinstance(px, datetime.timedelta):
2275 pieces.append(int(px.total_seconds() * 1000))
2276 elif isinstance(px, int):
2277 pieces.append(px)
2278 else:
2279 raise DataError("px must be datetime.timedelta or int")
2280 if exat is not None:
2281 pieces.append("EXAT")
2282 if isinstance(exat, datetime.datetime):
2283 exat = int(exat.timestamp())
2284 pieces.append(exat)
2285 if pxat is not None:
2286 pieces.append("PXAT")
2287 if isinstance(pxat, datetime.datetime):
2288 pxat = int(pxat.timestamp() * 1000)
2289 pieces.append(pxat)
2290 if keepttl:
2291 pieces.append("KEEPTTL")
2293 if nx:
2294 pieces.append("NX")
2295 if xx:
2296 pieces.append("XX")
2298 if get:
2299 pieces.append("GET")
2300 options["get"] = True
2302 return self.execute_command("SET", *pieces, **options)
2304 def __setitem__(self, name: KeyT, value: EncodableT):
2305 self.set(name, value)
2307 def setbit(self, name: KeyT, offset: int, value: int) -> ResponseT:
2308 """
2309 Flag the ``offset`` in ``name`` as ``value``. Returns an integer
2310 indicating the previous value of ``offset``.
2312 For more information see https://redis.io/commands/setbit
2313 """
2314 value = value and 1 or 0
2315 return self.execute_command("SETBIT", name, offset, value)
2317 def setex(self, name: KeyT, time: ExpiryT, value: EncodableT) -> ResponseT:
2318 """
2319 Set the value of key ``name`` to ``value`` that expires in ``time``
2320 seconds. ``time`` can be represented by an integer or a Python
2321 timedelta object.
2323 For more information see https://redis.io/commands/setex
2324 """
2325 if isinstance(time, datetime.timedelta):
2326 time = int(time.total_seconds())
2327 return self.execute_command("SETEX", name, time, value)
2329 def setnx(self, name: KeyT, value: EncodableT) -> ResponseT:
2330 """
2331 Set the value of key ``name`` to ``value`` if key doesn't exist
2333 For more information see https://redis.io/commands/setnx
2334 """
2335 return self.execute_command("SETNX", name, value)
2337 def setrange(self, name: KeyT, offset: int, value: EncodableT) -> ResponseT:
2338 """
2339 Overwrite bytes in the value of ``name`` starting at ``offset`` with
2340 ``value``. If ``offset`` plus the length of ``value`` exceeds the
2341 length of the original value, the new value will be larger than before.
2342 If ``offset`` exceeds the length of the original value, null bytes
2343 will be used to pad between the end of the previous value and the start
2344 of what's being injected.
2346 Returns the length of the new string.
2348 For more information see https://redis.io/commands/setrange
2349 """
2350 return self.execute_command("SETRANGE", name, offset, value)
2352 def stralgo(
2353 self,
2354 algo: Literal["LCS"],
2355 value1: KeyT,
2356 value2: KeyT,
2357 specific_argument: Union[Literal["strings"], Literal["keys"]] = "strings",
2358 len: bool = False,
2359 idx: bool = False,
2360 minmatchlen: Union[int, None] = None,
2361 withmatchlen: bool = False,
2362 **kwargs,
2363 ) -> ResponseT:
2364 """
2365 Implements complex algorithms that operate on strings.
2366 Right now the only algorithm implemented is the LCS algorithm
2367 (longest common substring). However new algorithms could be
2368 implemented in the future.
2370 ``algo`` Right now must be LCS
2371 ``value1`` and ``value2`` Can be two strings or two keys
2372 ``specific_argument`` Specifying if the arguments to the algorithm
2373 will be keys or strings. strings is the default.
2374 ``len`` Returns just the len of the match.
2375 ``idx`` Returns the match positions in each string.
2376 ``minmatchlen`` Restrict the list of matches to the ones of a given
2377 minimal length. Can be provided only when ``idx`` set to True.
2378 ``withmatchlen`` Returns the matches with the len of the match.
2379 Can be provided only when ``idx`` set to True.
2381 For more information see https://redis.io/commands/stralgo
2382 """
2383 # check validity
2384 supported_algo = ["LCS"]
2385 if algo not in supported_algo:
2386 supported_algos_str = ", ".join(supported_algo)
2387 raise DataError(f"The supported algorithms are: {supported_algos_str}")
2388 if specific_argument not in ["keys", "strings"]:
2389 raise DataError("specific_argument can be only keys or strings")
2390 if len and idx:
2391 raise DataError("len and idx cannot be provided together.")
2393 pieces: list[EncodableT] = [algo, specific_argument.upper(), value1, value2]
2394 if len:
2395 pieces.append(b"LEN")
2396 if idx:
2397 pieces.append(b"IDX")
2398 try:
2399 int(minmatchlen)
2400 pieces.extend([b"MINMATCHLEN", minmatchlen])
2401 except TypeError:
2402 pass
2403 if withmatchlen:
2404 pieces.append(b"WITHMATCHLEN")
2406 return self.execute_command(
2407 "STRALGO",
2408 *pieces,
2409 len=len,
2410 idx=idx,
2411 minmatchlen=minmatchlen,
2412 withmatchlen=withmatchlen,
2413 **kwargs,
2414 )
2416 def strlen(self, name: KeyT) -> ResponseT:
2417 """
2418 Return the number of bytes stored in the value of ``name``
2420 For more information see https://redis.io/commands/strlen
2421 """
2422 return self.execute_command("STRLEN", name)
2424 def substr(self, name: KeyT, start: int, end: int = -1) -> ResponseT:
2425 """
2426 Return a substring of the string at key ``name``. ``start`` and ``end``
2427 are 0-based integers specifying the portion of the string to return.
2428 """
2429 return self.execute_command("SUBSTR", name, start, end)
2431 def touch(self, *args: KeyT) -> ResponseT:
2432 """
2433 Alters the last access time of a key(s) ``*args``. A key is ignored
2434 if it does not exist.
2436 For more information see https://redis.io/commands/touch
2437 """
2438 return self.execute_command("TOUCH", *args)
2440 def ttl(self, name: KeyT) -> ResponseT:
2441 """
2442 Returns the number of seconds until the key ``name`` will expire
2444 For more information see https://redis.io/commands/ttl
2445 """
2446 return self.execute_command("TTL", name)
2448 def type(self, name: KeyT) -> ResponseT:
2449 """
2450 Returns the type of key ``name``
2452 For more information see https://redis.io/commands/type
2453 """
2454 return self.execute_command("TYPE", name)
2456 def watch(self, *names: KeyT) -> None:
2457 """
2458 Watches the values at keys ``names``, or None if the key doesn't exist
2460 For more information see https://redis.io/commands/watch
2461 """
2462 warnings.warn(DeprecationWarning("Call WATCH from a Pipeline object"))
2464 def unwatch(self) -> None:
2465 """
2466 Unwatches the value at key ``name``, or None of the key doesn't exist
2468 For more information see https://redis.io/commands/unwatch
2469 """
2470 warnings.warn(DeprecationWarning("Call UNWATCH from a Pipeline object"))
2472 def unlink(self, *names: KeyT) -> ResponseT:
2473 """
2474 Unlink one or more keys specified by ``names``
2476 For more information see https://redis.io/commands/unlink
2477 """
2478 return self.execute_command("UNLINK", *names)
2480 def lcs(
2481 self,
2482 key1: str,
2483 key2: str,
2484 len: Optional[bool] = False,
2485 idx: Optional[bool] = False,
2486 minmatchlen: Optional[int] = 0,
2487 withmatchlen: Optional[bool] = False,
2488 ) -> Union[str, int, list]:
2489 """
2490 Find the longest common subsequence between ``key1`` and ``key2``.
2491 If ``len`` is true the length of the match will will be returned.
2492 If ``idx`` is true the match position in each strings will be returned.
2493 ``minmatchlen`` restrict the list of matches to the ones of
2494 the given ``minmatchlen``.
2495 If ``withmatchlen`` the length of the match also will be returned.
2496 For more information see https://redis.io/commands/lcs
2497 """
2498 pieces = [key1, key2]
2499 if len:
2500 pieces.append("LEN")
2501 if idx:
2502 pieces.append("IDX")
2503 if minmatchlen != 0:
2504 pieces.extend(["MINMATCHLEN", minmatchlen])
2505 if withmatchlen:
2506 pieces.append("WITHMATCHLEN")
2507 return self.execute_command("LCS", *pieces)
2510class AsyncBasicKeyCommands(BasicKeyCommands):
2511 def __delitem__(self, name: KeyT):
2512 raise TypeError("Async Redis client does not support class deletion")
2514 def __contains__(self, name: KeyT):
2515 raise TypeError("Async Redis client does not support class inclusion")
2517 def __getitem__(self, name: KeyT):
2518 raise TypeError("Async Redis client does not support class retrieval")
2520 def __setitem__(self, name: KeyT, value: EncodableT):
2521 raise TypeError("Async Redis client does not support class assignment")
2523 async def watch(self, *names: KeyT) -> None:
2524 return super().watch(*names)
2526 async def unwatch(self) -> None:
2527 return super().unwatch()
2530class ListCommands(CommandsProtocol):
2531 """
2532 Redis commands for List data type.
2533 see: https://redis.io/topics/data-types#lists
2534 """
2536 def blpop(
2537 self, keys: List, timeout: Optional[int] = 0
2538 ) -> Union[Awaitable[list], list]:
2539 """
2540 LPOP a value off of the first non-empty list
2541 named in the ``keys`` list.
2543 If none of the lists in ``keys`` has a value to LPOP, then block
2544 for ``timeout`` seconds, or until a value gets pushed on to one
2545 of the lists.
2547 If timeout is 0, then block indefinitely.
2549 For more information see https://redis.io/commands/blpop
2550 """
2551 if timeout is None:
2552 timeout = 0
2553 keys = list_or_args(keys, None)
2554 keys.append(timeout)
2555 return self.execute_command("BLPOP", *keys)
2557 def brpop(
2558 self, keys: List, timeout: Optional[int] = 0
2559 ) -> Union[Awaitable[list], list]:
2560 """
2561 RPOP a value off of the first non-empty list
2562 named in the ``keys`` list.
2564 If none of the lists in ``keys`` has a value to RPOP, then block
2565 for ``timeout`` seconds, or until a value gets pushed on to one
2566 of the lists.
2568 If timeout is 0, then block indefinitely.
2570 For more information see https://redis.io/commands/brpop
2571 """
2572 if timeout is None:
2573 timeout = 0
2574 keys = list_or_args(keys, None)
2575 keys.append(timeout)
2576 return self.execute_command("BRPOP", *keys)
2578 def brpoplpush(
2579 self, src: str, dst: str, timeout: Optional[int] = 0
2580 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
2581 """
2582 Pop a value off the tail of ``src``, push it on the head of ``dst``
2583 and then return it.
2585 This command blocks until a value is in ``src`` or until ``timeout``
2586 seconds elapse, whichever is first. A ``timeout`` value of 0 blocks
2587 forever.
2589 For more information see https://redis.io/commands/brpoplpush
2590 """
2591 if timeout is None:
2592 timeout = 0
2593 return self.execute_command("BRPOPLPUSH", src, dst, timeout)
2595 def blmpop(
2596 self,
2597 timeout: float,
2598 numkeys: int,
2599 *args: List[str],
2600 direction: str,
2601 count: Optional[int] = 1,
2602 ) -> Optional[list]:
2603 """
2604 Pop ``count`` values (default 1) from first non-empty in the list
2605 of provided key names.
2607 When all lists are empty this command blocks the connection until another
2608 client pushes to it or until the timeout, timeout of 0 blocks indefinitely
2610 For more information see https://redis.io/commands/blmpop
2611 """
2612 args = [timeout, numkeys, *args, direction, "COUNT", count]
2614 return self.execute_command("BLMPOP", *args)
2616 def lmpop(
2617 self,
2618 num_keys: int,
2619 *args: List[str],
2620 direction: str,
2621 count: Optional[int] = 1,
2622 ) -> Union[Awaitable[list], list]:
2623 """
2624 Pop ``count`` values (default 1) first non-empty list key from the list
2625 of args provided key names.
2627 For more information see https://redis.io/commands/lmpop
2628 """
2629 args = [num_keys] + list(args) + [direction]
2630 if count != 1:
2631 args.extend(["COUNT", count])
2633 return self.execute_command("LMPOP", *args)
2635 def lindex(
2636 self, name: str, index: int
2637 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
2638 """
2639 Return the item from list ``name`` at position ``index``
2641 Negative indexes are supported and will return an item at the
2642 end of the list
2644 For more information see https://redis.io/commands/lindex
2645 """
2646 return self.execute_command("LINDEX", name, index)
2648 def linsert(
2649 self, name: str, where: str, refvalue: str, value: str
2650 ) -> Union[Awaitable[int], int]:
2651 """
2652 Insert ``value`` in list ``name`` either immediately before or after
2653 [``where``] ``refvalue``
2655 Returns the new length of the list on success or -1 if ``refvalue``
2656 is not in the list.
2658 For more information see https://redis.io/commands/linsert
2659 """
2660 return self.execute_command("LINSERT", name, where, refvalue, value)
2662 def llen(self, name: str) -> Union[Awaitable[int], int]:
2663 """
2664 Return the length of the list ``name``
2666 For more information see https://redis.io/commands/llen
2667 """
2668 return self.execute_command("LLEN", name)
2670 def lpop(
2671 self,
2672 name: str,
2673 count: Optional[int] = None,
2674 ) -> Union[Awaitable[Union[str, List, None]], Union[str, List, None]]:
2675 """
2676 Removes and returns the first elements of the list ``name``.
2678 By default, the command pops a single element from the beginning of
2679 the list. When provided with the optional ``count`` argument, the reply
2680 will consist of up to count elements, depending on the list's length.
2682 For more information see https://redis.io/commands/lpop
2683 """
2684 if count is not None:
2685 return self.execute_command("LPOP", name, count)
2686 else:
2687 return self.execute_command("LPOP", name)
2689 def lpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2690 """
2691 Push ``values`` onto the head of the list ``name``
2693 For more information see https://redis.io/commands/lpush
2694 """
2695 return self.execute_command("LPUSH", name, *values)
2697 def lpushx(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2698 """
2699 Push ``value`` onto the head of the list ``name`` if ``name`` exists
2701 For more information see https://redis.io/commands/lpushx
2702 """
2703 return self.execute_command("LPUSHX", name, *values)
2705 def lrange(self, name: str, start: int, end: int) -> Union[Awaitable[list], list]:
2706 """
2707 Return a slice of the list ``name`` between
2708 position ``start`` and ``end``
2710 ``start`` and ``end`` can be negative numbers just like
2711 Python slicing notation
2713 For more information see https://redis.io/commands/lrange
2714 """
2715 return self.execute_command("LRANGE", name, start, end)
2717 def lrem(self, name: str, count: int, value: str) -> Union[Awaitable[int], int]:
2718 """
2719 Remove the first ``count`` occurrences of elements equal to ``value``
2720 from the list stored at ``name``.
2722 The count argument influences the operation in the following ways:
2723 count > 0: Remove elements equal to value moving from head to tail.
2724 count < 0: Remove elements equal to value moving from tail to head.
2725 count = 0: Remove all elements equal to value.
2727 For more information see https://redis.io/commands/lrem
2728 """
2729 return self.execute_command("LREM", name, count, value)
2731 def lset(self, name: str, index: int, value: str) -> Union[Awaitable[str], str]:
2732 """
2733 Set element at ``index`` of list ``name`` to ``value``
2735 For more information see https://redis.io/commands/lset
2736 """
2737 return self.execute_command("LSET", name, index, value)
2739 def ltrim(self, name: str, start: int, end: int) -> Union[Awaitable[str], str]:
2740 """
2741 Trim the list ``name``, removing all values not within the slice
2742 between ``start`` and ``end``
2744 ``start`` and ``end`` can be negative numbers just like
2745 Python slicing notation
2747 For more information see https://redis.io/commands/ltrim
2748 """
2749 return self.execute_command("LTRIM", name, start, end)
2751 def rpop(
2752 self,
2753 name: str,
2754 count: Optional[int] = None,
2755 ) -> Union[Awaitable[Union[str, List, None]], Union[str, List, None]]:
2756 """
2757 Removes and returns the last elements of the list ``name``.
2759 By default, the command pops a single element from the end of the list.
2760 When provided with the optional ``count`` argument, the reply will
2761 consist of up to count elements, depending on the list's length.
2763 For more information see https://redis.io/commands/rpop
2764 """
2765 if count is not None:
2766 return self.execute_command("RPOP", name, count)
2767 else:
2768 return self.execute_command("RPOP", name)
2770 def rpoplpush(self, src: str, dst: str) -> Union[Awaitable[str], str]:
2771 """
2772 RPOP a value off of the ``src`` list and atomically LPUSH it
2773 on to the ``dst`` list. Returns the value.
2775 For more information see https://redis.io/commands/rpoplpush
2776 """
2777 return self.execute_command("RPOPLPUSH", src, dst)
2779 def rpush(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
2780 """
2781 Push ``values`` onto the tail of the list ``name``
2783 For more information see https://redis.io/commands/rpush
2784 """
2785 return self.execute_command("RPUSH", name, *values)
2787 def rpushx(self, name: str, value: str) -> Union[Awaitable[int], int]:
2788 """
2789 Push ``value`` onto the tail of the list ``name`` if ``name`` exists
2791 For more information see https://redis.io/commands/rpushx
2792 """
2793 return self.execute_command("RPUSHX", name, value)
2795 def lpos(
2796 self,
2797 name: str,
2798 value: str,
2799 rank: Optional[int] = None,
2800 count: Optional[int] = None,
2801 maxlen: Optional[int] = None,
2802 ) -> Union[str, List, None]:
2803 """
2804 Get position of ``value`` within the list ``name``
2806 If specified, ``rank`` indicates the "rank" of the first element to
2807 return in case there are multiple copies of ``value`` in the list.
2808 By default, LPOS returns the position of the first occurrence of
2809 ``value`` in the list. When ``rank`` 2, LPOS returns the position of
2810 the second ``value`` in the list. If ``rank`` is negative, LPOS
2811 searches the list in reverse. For example, -1 would return the
2812 position of the last occurrence of ``value`` and -2 would return the
2813 position of the next to last occurrence of ``value``.
2815 If specified, ``count`` indicates that LPOS should return a list of
2816 up to ``count`` positions. A ``count`` of 2 would return a list of
2817 up to 2 positions. A ``count`` of 0 returns a list of all positions
2818 matching ``value``. When ``count`` is specified and but ``value``
2819 does not exist in the list, an empty list is returned.
2821 If specified, ``maxlen`` indicates the maximum number of list
2822 elements to scan. A ``maxlen`` of 1000 will only return the
2823 position(s) of items within the first 1000 entries in the list.
2824 A ``maxlen`` of 0 (the default) will scan the entire list.
2826 For more information see https://redis.io/commands/lpos
2827 """
2828 pieces: list[EncodableT] = [name, value]
2829 if rank is not None:
2830 pieces.extend(["RANK", rank])
2832 if count is not None:
2833 pieces.extend(["COUNT", count])
2835 if maxlen is not None:
2836 pieces.extend(["MAXLEN", maxlen])
2838 return self.execute_command("LPOS", *pieces)
2840 def sort(
2841 self,
2842 name: str,
2843 start: Optional[int] = None,
2844 num: Optional[int] = None,
2845 by: Optional[str] = None,
2846 get: Optional[List[str]] = None,
2847 desc: bool = False,
2848 alpha: bool = False,
2849 store: Optional[str] = None,
2850 groups: Optional[bool] = False,
2851 ) -> Union[List, int]:
2852 """
2853 Sort and return the list, set or sorted set at ``name``.
2855 ``start`` and ``num`` allow for paging through the sorted data
2857 ``by`` allows using an external key to weight and sort the items.
2858 Use an "*" to indicate where in the key the item value is located
2860 ``get`` allows for returning items from external keys rather than the
2861 sorted data itself. Use an "*" to indicate where in the key
2862 the item value is located
2864 ``desc`` allows for reversing the sort
2866 ``alpha`` allows for sorting lexicographically rather than numerically
2868 ``store`` allows for storing the result of the sort into
2869 the key ``store``
2871 ``groups`` if set to True and if ``get`` contains at least two
2872 elements, sort will return a list of tuples, each containing the
2873 values fetched from the arguments to ``get``.
2875 For more information see https://redis.io/commands/sort
2876 """
2877 if (start is not None and num is None) or (num is not None and start is None):
2878 raise DataError("``start`` and ``num`` must both be specified")
2880 pieces: list[EncodableT] = [name]
2881 if by is not None:
2882 pieces.extend([b"BY", by])
2883 if start is not None and num is not None:
2884 pieces.extend([b"LIMIT", start, num])
2885 if get is not None:
2886 # If get is a string assume we want to get a single value.
2887 # Otherwise assume it's an interable and we want to get multiple
2888 # values. We can't just iterate blindly because strings are
2889 # iterable.
2890 if isinstance(get, (bytes, str)):
2891 pieces.extend([b"GET", get])
2892 else:
2893 for g in get:
2894 pieces.extend([b"GET", g])
2895 if desc:
2896 pieces.append(b"DESC")
2897 if alpha:
2898 pieces.append(b"ALPHA")
2899 if store is not None:
2900 pieces.extend([b"STORE", store])
2901 if groups:
2902 if not get or isinstance(get, (bytes, str)) or len(get) < 2:
2903 raise DataError(
2904 'when using "groups" the "get" argument '
2905 "must be specified and contain at least "
2906 "two keys"
2907 )
2909 options = {"groups": len(get) if groups else None}
2910 return self.execute_command("SORT", *pieces, **options)
2912 def sort_ro(
2913 self,
2914 key: str,
2915 start: Optional[int] = None,
2916 num: Optional[int] = None,
2917 by: Optional[str] = None,
2918 get: Optional[List[str]] = None,
2919 desc: bool = False,
2920 alpha: bool = False,
2921 ) -> list:
2922 """
2923 Returns the elements contained in the list, set or sorted set at key.
2924 (read-only variant of the SORT command)
2926 ``start`` and ``num`` allow for paging through the sorted data
2928 ``by`` allows using an external key to weight and sort the items.
2929 Use an "*" to indicate where in the key the item value is located
2931 ``get`` allows for returning items from external keys rather than the
2932 sorted data itself. Use an "*" to indicate where in the key
2933 the item value is located
2935 ``desc`` allows for reversing the sort
2937 ``alpha`` allows for sorting lexicographically rather than numerically
2939 For more information see https://redis.io/commands/sort_ro
2940 """
2941 return self.sort(
2942 key, start=start, num=num, by=by, get=get, desc=desc, alpha=alpha
2943 )
2946AsyncListCommands = ListCommands
2949class ScanCommands(CommandsProtocol):
2950 """
2951 Redis SCAN commands.
2952 see: https://redis.io/commands/scan
2953 """
2955 def scan(
2956 self,
2957 cursor: int = 0,
2958 match: Union[PatternT, None] = None,
2959 count: Union[int, None] = None,
2960 _type: Union[str, None] = None,
2961 **kwargs,
2962 ) -> ResponseT:
2963 """
2964 Incrementally return lists of key names. Also return a cursor
2965 indicating the scan position.
2967 ``match`` allows for filtering the keys by pattern
2969 ``count`` provides a hint to Redis about the number of keys to
2970 return per batch.
2972 ``_type`` filters the returned values by a particular Redis type.
2973 Stock Redis instances allow for the following types:
2974 HASH, LIST, SET, STREAM, STRING, ZSET
2975 Additionally, Redis modules can expose other types as well.
2977 For more information see https://redis.io/commands/scan
2978 """
2979 pieces: list[EncodableT] = [cursor]
2980 if match is not None:
2981 pieces.extend([b"MATCH", match])
2982 if count is not None:
2983 pieces.extend([b"COUNT", count])
2984 if _type is not None:
2985 pieces.extend([b"TYPE", _type])
2986 return self.execute_command("SCAN", *pieces, **kwargs)
2988 def scan_iter(
2989 self,
2990 match: Union[PatternT, None] = None,
2991 count: Union[int, None] = None,
2992 _type: Union[str, None] = None,
2993 **kwargs,
2994 ) -> Iterator:
2995 """
2996 Make an iterator using the SCAN command so that the client doesn't
2997 need to remember the cursor position.
2999 ``match`` allows for filtering the keys by pattern
3001 ``count`` provides a hint to Redis about the number of keys to
3002 return per batch.
3004 ``_type`` filters the returned values by a particular Redis type.
3005 Stock Redis instances allow for the following types:
3006 HASH, LIST, SET, STREAM, STRING, ZSET
3007 Additionally, Redis modules can expose other types as well.
3008 """
3009 cursor = "0"
3010 while cursor != 0:
3011 cursor, data = self.scan(
3012 cursor=cursor, match=match, count=count, _type=_type, **kwargs
3013 )
3014 yield from data
3016 def sscan(
3017 self,
3018 name: KeyT,
3019 cursor: int = 0,
3020 match: Union[PatternT, None] = None,
3021 count: Union[int, None] = None,
3022 ) -> ResponseT:
3023 """
3024 Incrementally return lists of elements in a set. Also return a cursor
3025 indicating the scan position.
3027 ``match`` allows for filtering the keys by pattern
3029 ``count`` allows for hint the minimum number of returns
3031 For more information see https://redis.io/commands/sscan
3032 """
3033 pieces: list[EncodableT] = [name, cursor]
3034 if match is not None:
3035 pieces.extend([b"MATCH", match])
3036 if count is not None:
3037 pieces.extend([b"COUNT", count])
3038 return self.execute_command("SSCAN", *pieces)
3040 def sscan_iter(
3041 self,
3042 name: KeyT,
3043 match: Union[PatternT, None] = None,
3044 count: Union[int, None] = None,
3045 ) -> Iterator:
3046 """
3047 Make an iterator using the SSCAN command so that the client doesn't
3048 need to remember the cursor position.
3050 ``match`` allows for filtering the keys by pattern
3052 ``count`` allows for hint the minimum number of returns
3053 """
3054 cursor = "0"
3055 while cursor != 0:
3056 cursor, data = self.sscan(name, cursor=cursor, match=match, count=count)
3057 yield from data
3059 def hscan(
3060 self,
3061 name: KeyT,
3062 cursor: int = 0,
3063 match: Union[PatternT, None] = None,
3064 count: Union[int, None] = None,
3065 ) -> ResponseT:
3066 """
3067 Incrementally return key/value slices in a hash. Also return a cursor
3068 indicating the scan position.
3070 ``match`` allows for filtering the keys by pattern
3072 ``count`` allows for hint the minimum number of returns
3074 For more information see https://redis.io/commands/hscan
3075 """
3076 pieces: list[EncodableT] = [name, cursor]
3077 if match is not None:
3078 pieces.extend([b"MATCH", match])
3079 if count is not None:
3080 pieces.extend([b"COUNT", count])
3081 return self.execute_command("HSCAN", *pieces)
3083 def hscan_iter(
3084 self,
3085 name: str,
3086 match: Union[PatternT, None] = None,
3087 count: Union[int, None] = None,
3088 ) -> Iterator:
3089 """
3090 Make an iterator using the HSCAN command so that the client doesn't
3091 need to remember the cursor position.
3093 ``match`` allows for filtering the keys by pattern
3095 ``count`` allows for hint the minimum number of returns
3096 """
3097 cursor = "0"
3098 while cursor != 0:
3099 cursor, data = self.hscan(name, cursor=cursor, match=match, count=count)
3100 yield from data.items()
3102 def zscan(
3103 self,
3104 name: KeyT,
3105 cursor: int = 0,
3106 match: Union[PatternT, None] = None,
3107 count: Union[int, None] = None,
3108 score_cast_func: Union[type, Callable] = float,
3109 ) -> ResponseT:
3110 """
3111 Incrementally return lists of elements in a sorted set. Also return a
3112 cursor indicating the scan position.
3114 ``match`` allows for filtering the keys by pattern
3116 ``count`` allows for hint the minimum number of returns
3118 ``score_cast_func`` a callable used to cast the score return value
3120 For more information see https://redis.io/commands/zscan
3121 """
3122 pieces = [name, cursor]
3123 if match is not None:
3124 pieces.extend([b"MATCH", match])
3125 if count is not None:
3126 pieces.extend([b"COUNT", count])
3127 options = {"score_cast_func": score_cast_func}
3128 return self.execute_command("ZSCAN", *pieces, **options)
3130 def zscan_iter(
3131 self,
3132 name: KeyT,
3133 match: Union[PatternT, None] = None,
3134 count: Union[int, None] = None,
3135 score_cast_func: Union[type, Callable] = float,
3136 ) -> Iterator:
3137 """
3138 Make an iterator using the ZSCAN command so that the client doesn't
3139 need to remember the cursor position.
3141 ``match`` allows for filtering the keys by pattern
3143 ``count`` allows for hint the minimum number of returns
3145 ``score_cast_func`` a callable used to cast the score return value
3146 """
3147 cursor = "0"
3148 while cursor != 0:
3149 cursor, data = self.zscan(
3150 name,
3151 cursor=cursor,
3152 match=match,
3153 count=count,
3154 score_cast_func=score_cast_func,
3155 )
3156 yield from data
3159class AsyncScanCommands(ScanCommands):
3160 async def scan_iter(
3161 self,
3162 match: Union[PatternT, None] = None,
3163 count: Union[int, None] = None,
3164 _type: Union[str, None] = None,
3165 **kwargs,
3166 ) -> AsyncIterator:
3167 """
3168 Make an iterator using the SCAN command so that the client doesn't
3169 need to remember the cursor position.
3171 ``match`` allows for filtering the keys by pattern
3173 ``count`` provides a hint to Redis about the number of keys to
3174 return per batch.
3176 ``_type`` filters the returned values by a particular Redis type.
3177 Stock Redis instances allow for the following types:
3178 HASH, LIST, SET, STREAM, STRING, ZSET
3179 Additionally, Redis modules can expose other types as well.
3180 """
3181 cursor = "0"
3182 while cursor != 0:
3183 cursor, data = await self.scan(
3184 cursor=cursor, match=match, count=count, _type=_type, **kwargs
3185 )
3186 for d in data:
3187 yield d
3189 async def sscan_iter(
3190 self,
3191 name: KeyT,
3192 match: Union[PatternT, None] = None,
3193 count: Union[int, None] = None,
3194 ) -> AsyncIterator:
3195 """
3196 Make an iterator using the SSCAN command so that the client doesn't
3197 need to remember the cursor position.
3199 ``match`` allows for filtering the keys by pattern
3201 ``count`` allows for hint the minimum number of returns
3202 """
3203 cursor = "0"
3204 while cursor != 0:
3205 cursor, data = await self.sscan(
3206 name, cursor=cursor, match=match, count=count
3207 )
3208 for d in data:
3209 yield d
3211 async def hscan_iter(
3212 self,
3213 name: str,
3214 match: Union[PatternT, None] = None,
3215 count: Union[int, None] = None,
3216 ) -> AsyncIterator:
3217 """
3218 Make an iterator using the HSCAN 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.hscan(
3228 name, cursor=cursor, match=match, count=count
3229 )
3230 for it in data.items():
3231 yield it
3233 async def zscan_iter(
3234 self,
3235 name: KeyT,
3236 match: Union[PatternT, None] = None,
3237 count: Union[int, None] = None,
3238 score_cast_func: Union[type, Callable] = float,
3239 ) -> AsyncIterator:
3240 """
3241 Make an iterator using the ZSCAN 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 ``score_cast_func`` a callable used to cast the score return value
3249 """
3250 cursor = "0"
3251 while cursor != 0:
3252 cursor, data = await self.zscan(
3253 name,
3254 cursor=cursor,
3255 match=match,
3256 count=count,
3257 score_cast_func=score_cast_func,
3258 )
3259 for d in data:
3260 yield d
3263class SetCommands(CommandsProtocol):
3264 """
3265 Redis commands for Set data type.
3266 see: https://redis.io/topics/data-types#sets
3267 """
3269 def sadd(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
3270 """
3271 Add ``value(s)`` to set ``name``
3273 For more information see https://redis.io/commands/sadd
3274 """
3275 return self.execute_command("SADD", name, *values)
3277 def scard(self, name: str) -> Union[Awaitable[int], int]:
3278 """
3279 Return the number of elements in set ``name``
3281 For more information see https://redis.io/commands/scard
3282 """
3283 return self.execute_command("SCARD", name)
3285 def sdiff(self, keys: List, *args: List) -> Union[Awaitable[list], list]:
3286 """
3287 Return the difference of sets specified by ``keys``
3289 For more information see https://redis.io/commands/sdiff
3290 """
3291 args = list_or_args(keys, args)
3292 return self.execute_command("SDIFF", *args)
3294 def sdiffstore(
3295 self, dest: str, keys: List, *args: List
3296 ) -> Union[Awaitable[int], int]:
3297 """
3298 Store the difference of sets specified by ``keys`` into a new
3299 set named ``dest``. Returns the number of keys in the new set.
3301 For more information see https://redis.io/commands/sdiffstore
3302 """
3303 args = list_or_args(keys, args)
3304 return self.execute_command("SDIFFSTORE", dest, *args)
3306 def sinter(self, keys: List, *args: List) -> Union[Awaitable[list], list]:
3307 """
3308 Return the intersection of sets specified by ``keys``
3310 For more information see https://redis.io/commands/sinter
3311 """
3312 args = list_or_args(keys, args)
3313 return self.execute_command("SINTER", *args)
3315 def sintercard(
3316 self, numkeys: int, keys: List[str], limit: int = 0
3317 ) -> Union[Awaitable[int], int]:
3318 """
3319 Return the cardinality of the intersect of multiple sets specified by ``keys`.
3321 When LIMIT provided (defaults to 0 and means unlimited), if the intersection
3322 cardinality reaches limit partway through the computation, the algorithm will
3323 exit and yield limit as the cardinality
3325 For more information see https://redis.io/commands/sintercard
3326 """
3327 args = [numkeys, *keys, "LIMIT", limit]
3328 return self.execute_command("SINTERCARD", *args)
3330 def sinterstore(
3331 self, dest: str, keys: List, *args: List
3332 ) -> Union[Awaitable[int], int]:
3333 """
3334 Store the intersection of sets specified by ``keys`` into a new
3335 set named ``dest``. Returns the number of keys in the new set.
3337 For more information see https://redis.io/commands/sinterstore
3338 """
3339 args = list_or_args(keys, args)
3340 return self.execute_command("SINTERSTORE", dest, *args)
3342 def sismember(self, name: str, value: str) -> Union[Awaitable[bool], bool]:
3343 """
3344 Return a boolean indicating if ``value`` is a member of set ``name``
3346 For more information see https://redis.io/commands/sismember
3347 """
3348 return self.execute_command("SISMEMBER", name, value)
3350 def smembers(self, name: str) -> Union[Awaitable[Set], Set]:
3351 """
3352 Return all members of the set ``name``
3354 For more information see https://redis.io/commands/smembers
3355 """
3356 return self.execute_command("SMEMBERS", name)
3358 def smismember(
3359 self, name: str, values: List, *args: List
3360 ) -> Union[
3361 Awaitable[List[Union[Literal[0], Literal[1]]]],
3362 List[Union[Literal[0], Literal[1]]],
3363 ]:
3364 """
3365 Return whether each value in ``values`` is a member of the set ``name``
3366 as a list of ``int`` in the order of ``values``:
3367 - 1 if the value is a member of the set.
3368 - 0 if the value is not a member of the set or if key does not exist.
3370 For more information see https://redis.io/commands/smismember
3371 """
3372 args = list_or_args(values, args)
3373 return self.execute_command("SMISMEMBER", name, *args)
3375 def smove(self, src: str, dst: str, value: str) -> Union[Awaitable[bool], bool]:
3376 """
3377 Move ``value`` from set ``src`` to set ``dst`` atomically
3379 For more information see https://redis.io/commands/smove
3380 """
3381 return self.execute_command("SMOVE", src, dst, value)
3383 def spop(self, name: str, count: Optional[int] = None) -> Union[str, List, None]:
3384 """
3385 Remove and return a random member of set ``name``
3387 For more information see https://redis.io/commands/spop
3388 """
3389 args = (count is not None) and [count] or []
3390 return self.execute_command("SPOP", name, *args)
3392 def srandmember(
3393 self, name: str, number: Optional[int] = None
3394 ) -> Union[str, List, None]:
3395 """
3396 If ``number`` is None, returns a random member of set ``name``.
3398 If ``number`` is supplied, returns a list of ``number`` random
3399 members of set ``name``. Note this is only available when running
3400 Redis 2.6+.
3402 For more information see https://redis.io/commands/srandmember
3403 """
3404 args = (number is not None) and [number] or []
3405 return self.execute_command("SRANDMEMBER", name, *args)
3407 def srem(self, name: str, *values: FieldT) -> Union[Awaitable[int], int]:
3408 """
3409 Remove ``values`` from set ``name``
3411 For more information see https://redis.io/commands/srem
3412 """
3413 return self.execute_command("SREM", name, *values)
3415 def sunion(self, keys: List, *args: List) -> Union[Awaitable[List], List]:
3416 """
3417 Return the union of sets specified by ``keys``
3419 For more information see https://redis.io/commands/sunion
3420 """
3421 args = list_or_args(keys, args)
3422 return self.execute_command("SUNION", *args)
3424 def sunionstore(
3425 self, dest: str, keys: List, *args: List
3426 ) -> Union[Awaitable[int], int]:
3427 """
3428 Store the union of sets specified by ``keys`` into a new
3429 set named ``dest``. Returns the number of keys in the new set.
3431 For more information see https://redis.io/commands/sunionstore
3432 """
3433 args = list_or_args(keys, args)
3434 return self.execute_command("SUNIONSTORE", dest, *args)
3437AsyncSetCommands = SetCommands
3440class StreamCommands(CommandsProtocol):
3441 """
3442 Redis commands for Stream data type.
3443 see: https://redis.io/topics/streams-intro
3444 """
3446 def xack(self, name: KeyT, groupname: GroupT, *ids: StreamIdT) -> ResponseT:
3447 """
3448 Acknowledges the successful processing of one or more messages.
3449 name: name of the stream.
3450 groupname: name of the consumer group.
3451 *ids: message ids to acknowledge.
3453 For more information see https://redis.io/commands/xack
3454 """
3455 return self.execute_command("XACK", name, groupname, *ids)
3457 def xadd(
3458 self,
3459 name: KeyT,
3460 fields: Dict[FieldT, EncodableT],
3461 id: StreamIdT = "*",
3462 maxlen: Union[int, None] = None,
3463 approximate: bool = True,
3464 nomkstream: bool = False,
3465 minid: Union[StreamIdT, None] = None,
3466 limit: Union[int, None] = None,
3467 ) -> ResponseT:
3468 """
3469 Add to a stream.
3470 name: name of the stream
3471 fields: dict of field/value pairs to insert into the stream
3472 id: Location to insert this record. By default it is appended.
3473 maxlen: truncate old stream members beyond this size.
3474 Can't be specified with minid.
3475 approximate: actual stream length may be slightly more than maxlen
3476 nomkstream: When set to true, do not make a stream
3477 minid: the minimum id in the stream to query.
3478 Can't be specified with maxlen.
3479 limit: specifies the maximum number of entries to retrieve
3481 For more information see https://redis.io/commands/xadd
3482 """
3483 pieces: list[EncodableT] = []
3484 if maxlen is not None and minid is not None:
3485 raise DataError("Only one of ```maxlen``` or ```minid``` may be specified")
3487 if maxlen is not None:
3488 if not isinstance(maxlen, int) or maxlen < 1:
3489 raise DataError("XADD maxlen must be a positive integer")
3490 pieces.append(b"MAXLEN")
3491 if approximate:
3492 pieces.append(b"~")
3493 pieces.append(str(maxlen))
3494 if minid is not None:
3495 pieces.append(b"MINID")
3496 if approximate:
3497 pieces.append(b"~")
3498 pieces.append(minid)
3499 if limit is not None:
3500 pieces.extend([b"LIMIT", limit])
3501 if nomkstream:
3502 pieces.append(b"NOMKSTREAM")
3503 pieces.append(id)
3504 if not isinstance(fields, dict) or len(fields) == 0:
3505 raise DataError("XADD fields must be a non-empty dict")
3506 for pair in fields.items():
3507 pieces.extend(pair)
3508 return self.execute_command("XADD", name, *pieces)
3510 def xautoclaim(
3511 self,
3512 name: KeyT,
3513 groupname: GroupT,
3514 consumername: ConsumerT,
3515 min_idle_time: int,
3516 start_id: StreamIdT = "0-0",
3517 count: Union[int, None] = None,
3518 justid: bool = False,
3519 ) -> ResponseT:
3520 """
3521 Transfers ownership of pending stream entries that match the specified
3522 criteria. Conceptually, equivalent to calling XPENDING and then XCLAIM,
3523 but provides a more straightforward way to deal with message delivery
3524 failures via SCAN-like semantics.
3525 name: name of the stream.
3526 groupname: name of the consumer group.
3527 consumername: name of a consumer that claims the message.
3528 min_idle_time: filter messages that were idle less than this amount of
3529 milliseconds.
3530 start_id: filter messages with equal or greater ID.
3531 count: optional integer, upper limit of the number of entries that the
3532 command attempts to claim. Set to 100 by default.
3533 justid: optional boolean, false by default. Return just an array of IDs
3534 of messages successfully claimed, without returning the actual message
3536 For more information see https://redis.io/commands/xautoclaim
3537 """
3538 try:
3539 if int(min_idle_time) < 0:
3540 raise DataError(
3541 "XAUTOCLAIM min_idle_time must be a nonnegative integer"
3542 )
3543 except TypeError:
3544 pass
3546 kwargs = {}
3547 pieces = [name, groupname, consumername, min_idle_time, start_id]
3549 try:
3550 if int(count) < 0:
3551 raise DataError("XPENDING count must be a integer >= 0")
3552 pieces.extend([b"COUNT", count])
3553 except TypeError:
3554 pass
3555 if justid:
3556 pieces.append(b"JUSTID")
3557 kwargs["parse_justid"] = True
3559 return self.execute_command("XAUTOCLAIM", *pieces, **kwargs)
3561 def xclaim(
3562 self,
3563 name: KeyT,
3564 groupname: GroupT,
3565 consumername: ConsumerT,
3566 min_idle_time: int,
3567 message_ids: Union[List[StreamIdT], Tuple[StreamIdT]],
3568 idle: Union[int, None] = None,
3569 time: Union[int, None] = None,
3570 retrycount: Union[int, None] = None,
3571 force: bool = False,
3572 justid: bool = False,
3573 ) -> ResponseT:
3574 """
3575 Changes the ownership of a pending message.
3576 name: name of the stream.
3577 groupname: name of the consumer group.
3578 consumername: name of a consumer that claims the message.
3579 min_idle_time: filter messages that were idle less than this amount of
3580 milliseconds
3581 message_ids: non-empty list or tuple of message IDs to claim
3582 idle: optional. Set the idle time (last time it was delivered) of the
3583 message in ms
3584 time: optional integer. This is the same as idle but instead of a
3585 relative amount of milliseconds, it sets the idle time to a specific
3586 Unix time (in milliseconds).
3587 retrycount: optional integer. set the retry counter to the specified
3588 value. This counter is incremented every time a message is delivered
3589 again.
3590 force: optional boolean, false by default. Creates the pending message
3591 entry in the PEL even if certain specified IDs are not already in the
3592 PEL assigned to a different client.
3593 justid: optional boolean, false by default. Return just an array of IDs
3594 of messages successfully claimed, without returning the actual message
3596 For more information see https://redis.io/commands/xclaim
3597 """
3598 if not isinstance(min_idle_time, int) or min_idle_time < 0:
3599 raise DataError("XCLAIM min_idle_time must be a non negative integer")
3600 if not isinstance(message_ids, (list, tuple)) or not message_ids:
3601 raise DataError(
3602 "XCLAIM message_ids must be a non empty list or "
3603 "tuple of message IDs to claim"
3604 )
3606 kwargs = {}
3607 pieces: list[EncodableT] = [name, groupname, consumername, str(min_idle_time)]
3608 pieces.extend(list(message_ids))
3610 if idle is not None:
3611 if not isinstance(idle, int):
3612 raise DataError("XCLAIM idle must be an integer")
3613 pieces.extend((b"IDLE", str(idle)))
3614 if time is not None:
3615 if not isinstance(time, int):
3616 raise DataError("XCLAIM time must be an integer")
3617 pieces.extend((b"TIME", str(time)))
3618 if retrycount is not None:
3619 if not isinstance(retrycount, int):
3620 raise DataError("XCLAIM retrycount must be an integer")
3621 pieces.extend((b"RETRYCOUNT", str(retrycount)))
3623 if force:
3624 if not isinstance(force, bool):
3625 raise DataError("XCLAIM force must be a boolean")
3626 pieces.append(b"FORCE")
3627 if justid:
3628 if not isinstance(justid, bool):
3629 raise DataError("XCLAIM justid must be a boolean")
3630 pieces.append(b"JUSTID")
3631 kwargs["parse_justid"] = True
3632 return self.execute_command("XCLAIM", *pieces, **kwargs)
3634 def xdel(self, name: KeyT, *ids: StreamIdT) -> ResponseT:
3635 """
3636 Deletes one or more messages from a stream.
3637 name: name of the stream.
3638 *ids: message ids to delete.
3640 For more information see https://redis.io/commands/xdel
3641 """
3642 return self.execute_command("XDEL", name, *ids)
3644 def xgroup_create(
3645 self,
3646 name: KeyT,
3647 groupname: GroupT,
3648 id: StreamIdT = "$",
3649 mkstream: bool = False,
3650 entries_read: Optional[int] = None,
3651 ) -> ResponseT:
3652 """
3653 Create a new consumer group associated with a stream.
3654 name: name of the stream.
3655 groupname: name of the consumer group.
3656 id: ID of the last item in the stream to consider already delivered.
3658 For more information see https://redis.io/commands/xgroup-create
3659 """
3660 pieces: list[EncodableT] = ["XGROUP CREATE", name, groupname, id]
3661 if mkstream:
3662 pieces.append(b"MKSTREAM")
3663 if entries_read is not None:
3664 pieces.extend(["ENTRIESREAD", entries_read])
3666 return self.execute_command(*pieces)
3668 def xgroup_delconsumer(
3669 self, name: KeyT, groupname: GroupT, consumername: ConsumerT
3670 ) -> ResponseT:
3671 """
3672 Remove a specific consumer from a consumer group.
3673 Returns the number of pending messages that the consumer had before it
3674 was deleted.
3675 name: name of the stream.
3676 groupname: name of the consumer group.
3677 consumername: name of consumer to delete
3679 For more information see https://redis.io/commands/xgroup-delconsumer
3680 """
3681 return self.execute_command("XGROUP DELCONSUMER", name, groupname, consumername)
3683 def xgroup_destroy(self, name: KeyT, groupname: GroupT) -> ResponseT:
3684 """
3685 Destroy a consumer group.
3686 name: name of the stream.
3687 groupname: name of the consumer group.
3689 For more information see https://redis.io/commands/xgroup-destroy
3690 """
3691 return self.execute_command("XGROUP DESTROY", name, groupname)
3693 def xgroup_createconsumer(
3694 self, name: KeyT, groupname: GroupT, consumername: ConsumerT
3695 ) -> ResponseT:
3696 """
3697 Consumers in a consumer group are auto-created every time a new
3698 consumer name is mentioned by some command.
3699 They can be explicitly created by using this command.
3700 name: name of the stream.
3701 groupname: name of the consumer group.
3702 consumername: name of consumer to create.
3704 See: https://redis.io/commands/xgroup-createconsumer
3705 """
3706 return self.execute_command(
3707 "XGROUP CREATECONSUMER", name, groupname, consumername
3708 )
3710 def xgroup_setid(
3711 self,
3712 name: KeyT,
3713 groupname: GroupT,
3714 id: StreamIdT,
3715 entries_read: Optional[int] = None,
3716 ) -> ResponseT:
3717 """
3718 Set the consumer group last delivered ID to something else.
3719 name: name of the stream.
3720 groupname: name of the consumer group.
3721 id: ID of the last item in the stream to consider already delivered.
3723 For more information see https://redis.io/commands/xgroup-setid
3724 """
3725 pieces = [name, groupname, id]
3726 if entries_read is not None:
3727 pieces.extend(["ENTRIESREAD", entries_read])
3728 return self.execute_command("XGROUP SETID", *pieces)
3730 def xinfo_consumers(self, name: KeyT, groupname: GroupT) -> ResponseT:
3731 """
3732 Returns general information about the consumers in the group.
3733 name: name of the stream.
3734 groupname: name of the consumer group.
3736 For more information see https://redis.io/commands/xinfo-consumers
3737 """
3738 return self.execute_command("XINFO CONSUMERS", name, groupname)
3740 def xinfo_groups(self, name: KeyT) -> ResponseT:
3741 """
3742 Returns general information about the consumer groups of the stream.
3743 name: name of the stream.
3745 For more information see https://redis.io/commands/xinfo-groups
3746 """
3747 return self.execute_command("XINFO GROUPS", name)
3749 def xinfo_stream(self, name: KeyT, full: bool = False) -> ResponseT:
3750 """
3751 Returns general information about the stream.
3752 name: name of the stream.
3753 full: optional boolean, false by default. Return full summary
3755 For more information see https://redis.io/commands/xinfo-stream
3756 """
3757 pieces = [name]
3758 options = {}
3759 if full:
3760 pieces.append(b"FULL")
3761 options = {"full": full}
3762 return self.execute_command("XINFO STREAM", *pieces, **options)
3764 def xlen(self, name: KeyT) -> ResponseT:
3765 """
3766 Returns the number of elements in a given stream.
3768 For more information see https://redis.io/commands/xlen
3769 """
3770 return self.execute_command("XLEN", name)
3772 def xpending(self, name: KeyT, groupname: GroupT) -> ResponseT:
3773 """
3774 Returns information about pending messages of a group.
3775 name: name of the stream.
3776 groupname: name of the consumer group.
3778 For more information see https://redis.io/commands/xpending
3779 """
3780 return self.execute_command("XPENDING", name, groupname)
3782 def xpending_range(
3783 self,
3784 name: KeyT,
3785 groupname: GroupT,
3786 min: StreamIdT,
3787 max: StreamIdT,
3788 count: int,
3789 consumername: Union[ConsumerT, None] = None,
3790 idle: Union[int, None] = None,
3791 ) -> ResponseT:
3792 """
3793 Returns information about pending messages, in a range.
3795 name: name of the stream.
3796 groupname: name of the consumer group.
3797 idle: available from version 6.2. filter entries by their
3798 idle-time, given in milliseconds (optional).
3799 min: minimum stream ID.
3800 max: maximum stream ID.
3801 count: number of messages to return
3802 consumername: name of a consumer to filter by (optional).
3803 """
3804 if {min, max, count} == {None}:
3805 if idle is not None or consumername is not None:
3806 raise DataError(
3807 "if XPENDING is provided with idle time"
3808 " or consumername, it must be provided"
3809 " with min, max and count parameters"
3810 )
3811 return self.xpending(name, groupname)
3813 pieces = [name, groupname]
3814 if min is None or max is None or count is None:
3815 raise DataError(
3816 "XPENDING must be provided with min, max "
3817 "and count parameters, or none of them."
3818 )
3819 # idle
3820 try:
3821 if int(idle) < 0:
3822 raise DataError("XPENDING idle must be a integer >= 0")
3823 pieces.extend(["IDLE", idle])
3824 except TypeError:
3825 pass
3826 # count
3827 try:
3828 if int(count) < 0:
3829 raise DataError("XPENDING count must be a integer >= 0")
3830 pieces.extend([min, max, count])
3831 except TypeError:
3832 pass
3833 # consumername
3834 if consumername:
3835 pieces.append(consumername)
3837 return self.execute_command("XPENDING", *pieces, parse_detail=True)
3839 def xrange(
3840 self,
3841 name: KeyT,
3842 min: StreamIdT = "-",
3843 max: StreamIdT = "+",
3844 count: Union[int, None] = None,
3845 ) -> ResponseT:
3846 """
3847 Read stream values within an interval.
3848 name: name of the stream.
3849 start: first stream ID. defaults to '-',
3850 meaning the earliest available.
3851 finish: last stream ID. defaults to '+',
3852 meaning the latest available.
3853 count: if set, only return this many items, beginning with the
3854 earliest available.
3856 For more information see https://redis.io/commands/xrange
3857 """
3858 pieces = [min, max]
3859 if count is not None:
3860 if not isinstance(count, int) or count < 1:
3861 raise DataError("XRANGE count must be a positive integer")
3862 pieces.append(b"COUNT")
3863 pieces.append(str(count))
3865 return self.execute_command("XRANGE", name, *pieces)
3867 def xread(
3868 self,
3869 streams: Dict[KeyT, StreamIdT],
3870 count: Union[int, None] = None,
3871 block: Union[int, None] = None,
3872 ) -> ResponseT:
3873 """
3874 Block and monitor multiple streams for new data.
3875 streams: a dict of stream names to stream IDs, where
3876 IDs indicate the last ID already seen.
3877 count: if set, only return this many items, beginning with the
3878 earliest available.
3879 block: number of milliseconds to wait, if nothing already present.
3881 For more information see https://redis.io/commands/xread
3882 """
3883 pieces = []
3884 if block is not None:
3885 if not isinstance(block, int) or block < 0:
3886 raise DataError("XREAD block must be a non-negative integer")
3887 pieces.append(b"BLOCK")
3888 pieces.append(str(block))
3889 if count is not None:
3890 if not isinstance(count, int) or count < 1:
3891 raise DataError("XREAD count must be a positive integer")
3892 pieces.append(b"COUNT")
3893 pieces.append(str(count))
3894 if not isinstance(streams, dict) or len(streams) == 0:
3895 raise DataError("XREAD streams must be a non empty dict")
3896 pieces.append(b"STREAMS")
3897 keys, values = zip(*streams.items())
3898 pieces.extend(keys)
3899 pieces.extend(values)
3900 return self.execute_command("XREAD", *pieces)
3902 def xreadgroup(
3903 self,
3904 groupname: str,
3905 consumername: str,
3906 streams: Dict[KeyT, StreamIdT],
3907 count: Union[int, None] = None,
3908 block: Union[int, None] = None,
3909 noack: bool = False,
3910 ) -> ResponseT:
3911 """
3912 Read from a stream via a consumer group.
3913 groupname: name of the consumer group.
3914 consumername: name of the requesting consumer.
3915 streams: a dict of stream names to stream IDs, where
3916 IDs indicate the last ID already seen.
3917 count: if set, only return this many items, beginning with the
3918 earliest available.
3919 block: number of milliseconds to wait, if nothing already present.
3920 noack: do not add messages to the PEL
3922 For more information see https://redis.io/commands/xreadgroup
3923 """
3924 pieces: list[EncodableT] = [b"GROUP", groupname, consumername]
3925 if count is not None:
3926 if not isinstance(count, int) or count < 1:
3927 raise DataError("XREADGROUP count must be a positive integer")
3928 pieces.append(b"COUNT")
3929 pieces.append(str(count))
3930 if block is not None:
3931 if not isinstance(block, int) or block < 0:
3932 raise DataError("XREADGROUP block must be a non-negative integer")
3933 pieces.append(b"BLOCK")
3934 pieces.append(str(block))
3935 if noack:
3936 pieces.append(b"NOACK")
3937 if not isinstance(streams, dict) or len(streams) == 0:
3938 raise DataError("XREADGROUP streams must be a non empty dict")
3939 pieces.append(b"STREAMS")
3940 pieces.extend(streams.keys())
3941 pieces.extend(streams.values())
3942 return self.execute_command("XREADGROUP", *pieces)
3944 def xrevrange(
3945 self,
3946 name: KeyT,
3947 max: StreamIdT = "+",
3948 min: StreamIdT = "-",
3949 count: Union[int, None] = None,
3950 ) -> ResponseT:
3951 """
3952 Read stream values within an interval, in reverse order.
3953 name: name of the stream
3954 start: first stream ID. defaults to '+',
3955 meaning the latest available.
3956 finish: last stream ID. defaults to '-',
3957 meaning the earliest available.
3958 count: if set, only return this many items, beginning with the
3959 latest available.
3961 For more information see https://redis.io/commands/xrevrange
3962 """
3963 pieces: list[EncodableT] = [max, min]
3964 if count is not None:
3965 if not isinstance(count, int) or count < 1:
3966 raise DataError("XREVRANGE count must be a positive integer")
3967 pieces.append(b"COUNT")
3968 pieces.append(str(count))
3970 return self.execute_command("XREVRANGE", name, *pieces)
3972 def xtrim(
3973 self,
3974 name: KeyT,
3975 maxlen: Union[int, None] = None,
3976 approximate: bool = True,
3977 minid: Union[StreamIdT, None] = None,
3978 limit: Union[int, None] = None,
3979 ) -> ResponseT:
3980 """
3981 Trims old messages from a stream.
3982 name: name of the stream.
3983 maxlen: truncate old stream messages beyond this size
3984 Can't be specified with minid.
3985 approximate: actual stream length may be slightly more than maxlen
3986 minid: the minimum id in the stream to query
3987 Can't be specified with maxlen.
3988 limit: specifies the maximum number of entries to retrieve
3990 For more information see https://redis.io/commands/xtrim
3991 """
3992 pieces: list[EncodableT] = []
3993 if maxlen is not None and minid is not None:
3994 raise DataError("Only one of ``maxlen`` or ``minid`` may be specified")
3996 if maxlen is None and minid is None:
3997 raise DataError("One of ``maxlen`` or ``minid`` must be specified")
3999 if maxlen is not None:
4000 pieces.append(b"MAXLEN")
4001 if minid is not None:
4002 pieces.append(b"MINID")
4003 if approximate:
4004 pieces.append(b"~")
4005 if maxlen is not None:
4006 pieces.append(maxlen)
4007 if minid is not None:
4008 pieces.append(minid)
4009 if limit is not None:
4010 pieces.append(b"LIMIT")
4011 pieces.append(limit)
4013 return self.execute_command("XTRIM", name, *pieces)
4016AsyncStreamCommands = StreamCommands
4019class SortedSetCommands(CommandsProtocol):
4020 """
4021 Redis commands for Sorted Sets data type.
4022 see: https://redis.io/topics/data-types-intro#redis-sorted-sets
4023 """
4025 def zadd(
4026 self,
4027 name: KeyT,
4028 mapping: Mapping[AnyKeyT, EncodableT],
4029 nx: bool = False,
4030 xx: bool = False,
4031 ch: bool = False,
4032 incr: bool = False,
4033 gt: bool = False,
4034 lt: bool = False,
4035 ) -> ResponseT:
4036 """
4037 Set any number of element-name, score pairs to the key ``name``. Pairs
4038 are specified as a dict of element-names keys to score values.
4040 ``nx`` forces ZADD to only create new elements and not to update
4041 scores for elements that already exist.
4043 ``xx`` forces ZADD to only update scores of elements that already
4044 exist. New elements will not be added.
4046 ``ch`` modifies the return value to be the numbers of elements changed.
4047 Changed elements include new elements that were added and elements
4048 whose scores changed.
4050 ``incr`` modifies ZADD to behave like ZINCRBY. In this mode only a
4051 single element/score pair can be specified and the score is the amount
4052 the existing score will be incremented by. When using this mode the
4053 return value of ZADD will be the new score of the element.
4055 ``LT`` Only update existing elements if the new score is less than
4056 the current score. This flag doesn't prevent adding new elements.
4058 ``GT`` Only update existing elements if the new score is greater than
4059 the current score. This flag doesn't prevent adding new elements.
4061 The return value of ZADD varies based on the mode specified. With no
4062 options, ZADD returns the number of new elements added to the sorted
4063 set.
4065 ``NX``, ``LT``, and ``GT`` are mutually exclusive options.
4067 See: https://redis.io/commands/ZADD
4068 """
4069 if not mapping:
4070 raise DataError("ZADD requires at least one element/score pair")
4071 if nx and xx:
4072 raise DataError("ZADD allows either 'nx' or 'xx', not both")
4073 if gt and lt:
4074 raise DataError("ZADD allows either 'gt' or 'lt', not both")
4075 if incr and len(mapping) != 1:
4076 raise DataError(
4077 "ZADD option 'incr' only works when passing a "
4078 "single element/score pair"
4079 )
4080 if nx and (gt or lt):
4081 raise DataError("Only one of 'nx', 'lt', or 'gr' may be defined.")
4083 pieces: list[EncodableT] = []
4084 options = {}
4085 if nx:
4086 pieces.append(b"NX")
4087 if xx:
4088 pieces.append(b"XX")
4089 if ch:
4090 pieces.append(b"CH")
4091 if incr:
4092 pieces.append(b"INCR")
4093 options["as_score"] = True
4094 if gt:
4095 pieces.append(b"GT")
4096 if lt:
4097 pieces.append(b"LT")
4098 for pair in mapping.items():
4099 pieces.append(pair[1])
4100 pieces.append(pair[0])
4101 return self.execute_command("ZADD", name, *pieces, **options)
4103 def zcard(self, name: KeyT) -> ResponseT:
4104 """
4105 Return the number of elements in the sorted set ``name``
4107 For more information see https://redis.io/commands/zcard
4108 """
4109 return self.execute_command("ZCARD", name)
4111 def zcount(self, name: KeyT, min: ZScoreBoundT, max: ZScoreBoundT) -> ResponseT:
4112 """
4113 Returns the number of elements in the sorted set at key ``name`` with
4114 a score between ``min`` and ``max``.
4116 For more information see https://redis.io/commands/zcount
4117 """
4118 return self.execute_command("ZCOUNT", name, min, max)
4120 def zdiff(self, keys: KeysT, withscores: bool = False) -> ResponseT:
4121 """
4122 Returns the difference between the first and all successive input
4123 sorted sets provided in ``keys``.
4125 For more information see https://redis.io/commands/zdiff
4126 """
4127 pieces = [len(keys), *keys]
4128 if withscores:
4129 pieces.append("WITHSCORES")
4130 return self.execute_command("ZDIFF", *pieces)
4132 def zdiffstore(self, dest: KeyT, keys: KeysT) -> ResponseT:
4133 """
4134 Computes the difference between the first and all successive input
4135 sorted sets provided in ``keys`` and stores the result in ``dest``.
4137 For more information see https://redis.io/commands/zdiffstore
4138 """
4139 pieces = [len(keys), *keys]
4140 return self.execute_command("ZDIFFSTORE", dest, *pieces)
4142 def zincrby(self, name: KeyT, amount: float, value: EncodableT) -> ResponseT:
4143 """
4144 Increment the score of ``value`` in sorted set ``name`` by ``amount``
4146 For more information see https://redis.io/commands/zincrby
4147 """
4148 return self.execute_command("ZINCRBY", name, amount, value)
4150 def zinter(
4151 self, keys: KeysT, aggregate: Union[str, None] = None, withscores: bool = False
4152 ) -> ResponseT:
4153 """
4154 Return the intersect of multiple sorted sets specified by ``keys``.
4155 With the ``aggregate`` option, it is possible to specify how the
4156 results of the union are aggregated. This option defaults to SUM,
4157 where the score of an element is summed across the inputs where it
4158 exists. When this option is set to either MIN or MAX, the resulting
4159 set will contain the minimum or maximum score of an element across
4160 the inputs where it exists.
4162 For more information see https://redis.io/commands/zinter
4163 """
4164 return self._zaggregate("ZINTER", None, keys, aggregate, withscores=withscores)
4166 def zinterstore(
4167 self,
4168 dest: KeyT,
4169 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4170 aggregate: Union[str, None] = None,
4171 ) -> ResponseT:
4172 """
4173 Intersect multiple sorted sets specified by ``keys`` into a new
4174 sorted set, ``dest``. Scores in the destination will be aggregated
4175 based on the ``aggregate``. This option defaults to SUM, where the
4176 score of an element is summed across the inputs where it exists.
4177 When this option is set to either MIN or MAX, the resulting set will
4178 contain the minimum or maximum score of an element across the inputs
4179 where it exists.
4181 For more information see https://redis.io/commands/zinterstore
4182 """
4183 return self._zaggregate("ZINTERSTORE", dest, keys, aggregate)
4185 def zintercard(
4186 self, numkeys: int, keys: List[str], limit: int = 0
4187 ) -> Union[Awaitable[int], int]:
4188 """
4189 Return the cardinality of the intersect of multiple sorted sets
4190 specified by ``keys`.
4191 When LIMIT provided (defaults to 0 and means unlimited), if the intersection
4192 cardinality reaches limit partway through the computation, the algorithm will
4193 exit and yield limit as the cardinality
4195 For more information see https://redis.io/commands/zintercard
4196 """
4197 args = [numkeys, *keys, "LIMIT", limit]
4198 return self.execute_command("ZINTERCARD", *args)
4200 def zlexcount(self, name, min, max):
4201 """
4202 Return the number of items in the sorted set ``name`` between the
4203 lexicographical range ``min`` and ``max``.
4205 For more information see https://redis.io/commands/zlexcount
4206 """
4207 return self.execute_command("ZLEXCOUNT", name, min, max)
4209 def zpopmax(self, name: KeyT, count: Union[int, None] = None) -> ResponseT:
4210 """
4211 Remove and return up to ``count`` members with the highest scores
4212 from the sorted set ``name``.
4214 For more information see https://redis.io/commands/zpopmax
4215 """
4216 args = (count is not None) and [count] or []
4217 options = {"withscores": True}
4218 return self.execute_command("ZPOPMAX", name, *args, **options)
4220 def zpopmin(self, name: KeyT, count: Union[int, None] = None) -> ResponseT:
4221 """
4222 Remove and return up to ``count`` members with the lowest scores
4223 from the sorted set ``name``.
4225 For more information see https://redis.io/commands/zpopmin
4226 """
4227 args = (count is not None) and [count] or []
4228 options = {"withscores": True}
4229 return self.execute_command("ZPOPMIN", name, *args, **options)
4231 def zrandmember(
4232 self, key: KeyT, count: int = None, withscores: bool = False
4233 ) -> ResponseT:
4234 """
4235 Return a random element from the sorted set value stored at key.
4237 ``count`` if the argument is positive, return an array of distinct
4238 fields. If called with a negative count, the behavior changes and
4239 the command is allowed to return the same field multiple times.
4240 In this case, the number of returned fields is the absolute value
4241 of the specified count.
4243 ``withscores`` The optional WITHSCORES modifier changes the reply so it
4244 includes the respective scores of the randomly selected elements from
4245 the sorted set.
4247 For more information see https://redis.io/commands/zrandmember
4248 """
4249 params = []
4250 if count is not None:
4251 params.append(count)
4252 if withscores:
4253 params.append("WITHSCORES")
4255 return self.execute_command("ZRANDMEMBER", key, *params)
4257 def bzpopmax(self, keys: KeysT, timeout: TimeoutSecT = 0) -> ResponseT:
4258 """
4259 ZPOPMAX a value off of the first non-empty sorted set
4260 named in the ``keys`` list.
4262 If none of the sorted sets in ``keys`` has a value to ZPOPMAX,
4263 then block for ``timeout`` seconds, or until a member gets added
4264 to one of the sorted sets.
4266 If timeout is 0, then block indefinitely.
4268 For more information see https://redis.io/commands/bzpopmax
4269 """
4270 if timeout is None:
4271 timeout = 0
4272 keys = list_or_args(keys, None)
4273 keys.append(timeout)
4274 return self.execute_command("BZPOPMAX", *keys)
4276 def bzpopmin(self, keys: KeysT, timeout: TimeoutSecT = 0) -> ResponseT:
4277 """
4278 ZPOPMIN a value off of the first non-empty sorted set
4279 named in the ``keys`` list.
4281 If none of the sorted sets in ``keys`` has a value to ZPOPMIN,
4282 then block for ``timeout`` seconds, or until a member gets added
4283 to one of the sorted sets.
4285 If timeout is 0, then block indefinitely.
4287 For more information see https://redis.io/commands/bzpopmin
4288 """
4289 if timeout is None:
4290 timeout = 0
4291 keys: list[EncodableT] = list_or_args(keys, None)
4292 keys.append(timeout)
4293 return self.execute_command("BZPOPMIN", *keys)
4295 def zmpop(
4296 self,
4297 num_keys: int,
4298 keys: List[str],
4299 min: Optional[bool] = False,
4300 max: Optional[bool] = False,
4301 count: Optional[int] = 1,
4302 ) -> Union[Awaitable[list], list]:
4303 """
4304 Pop ``count`` values (default 1) off of the first non-empty sorted set
4305 named in the ``keys`` list.
4306 For more information see https://redis.io/commands/zmpop
4307 """
4308 args = [num_keys] + keys
4309 if (min and max) or (not min and not max):
4310 raise DataError
4311 elif min:
4312 args.append("MIN")
4313 else:
4314 args.append("MAX")
4315 if count != 1:
4316 args.extend(["COUNT", count])
4318 return self.execute_command("ZMPOP", *args)
4320 def bzmpop(
4321 self,
4322 timeout: float,
4323 numkeys: int,
4324 keys: List[str],
4325 min: Optional[bool] = False,
4326 max: Optional[bool] = False,
4327 count: Optional[int] = 1,
4328 ) -> Optional[list]:
4329 """
4330 Pop ``count`` values (default 1) off of the first non-empty sorted set
4331 named in the ``keys`` list.
4333 If none of the sorted sets in ``keys`` has a value to pop,
4334 then block for ``timeout`` seconds, or until a member gets added
4335 to one of the sorted sets.
4337 If timeout is 0, then block indefinitely.
4339 For more information see https://redis.io/commands/bzmpop
4340 """
4341 args = [timeout, numkeys, *keys]
4342 if (min and max) or (not min and not max):
4343 raise DataError("Either min or max, but not both must be set")
4344 elif min:
4345 args.append("MIN")
4346 else:
4347 args.append("MAX")
4348 args.extend(["COUNT", count])
4350 return self.execute_command("BZMPOP", *args)
4352 def _zrange(
4353 self,
4354 command,
4355 dest: Union[KeyT, None],
4356 name: KeyT,
4357 start: int,
4358 end: int,
4359 desc: bool = False,
4360 byscore: bool = False,
4361 bylex: bool = False,
4362 withscores: bool = False,
4363 score_cast_func: Union[type, Callable, None] = float,
4364 offset: Union[int, None] = None,
4365 num: Union[int, None] = None,
4366 ) -> ResponseT:
4367 if byscore and bylex:
4368 raise DataError("``byscore`` and ``bylex`` can not be specified together.")
4369 if (offset is not None and num is None) or (num is not None and offset is None):
4370 raise DataError("``offset`` and ``num`` must both be specified.")
4371 if bylex and withscores:
4372 raise DataError(
4373 "``withscores`` not supported in combination with ``bylex``."
4374 )
4375 pieces = [command]
4376 if dest:
4377 pieces.append(dest)
4378 pieces.extend([name, start, end])
4379 if byscore:
4380 pieces.append("BYSCORE")
4381 if bylex:
4382 pieces.append("BYLEX")
4383 if desc:
4384 pieces.append("REV")
4385 if offset is not None and num is not None:
4386 pieces.extend(["LIMIT", offset, num])
4387 if withscores:
4388 pieces.append("WITHSCORES")
4389 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4390 return self.execute_command(*pieces, **options)
4392 def zrange(
4393 self,
4394 name: KeyT,
4395 start: int,
4396 end: int,
4397 desc: bool = False,
4398 withscores: bool = False,
4399 score_cast_func: Union[type, Callable] = float,
4400 byscore: bool = False,
4401 bylex: bool = False,
4402 offset: int = None,
4403 num: int = None,
4404 ) -> ResponseT:
4405 """
4406 Return a range of values from sorted set ``name`` between
4407 ``start`` and ``end`` sorted in ascending order.
4409 ``start`` and ``end`` can be negative, indicating the end of the range.
4411 ``desc`` a boolean indicating whether to sort the results in reversed
4412 order.
4414 ``withscores`` indicates to return the scores along with the values.
4415 The return type is a list of (value, score) pairs.
4417 ``score_cast_func`` a callable used to cast the score return value.
4419 ``byscore`` when set to True, returns the range of elements from the
4420 sorted set having scores equal or between ``start`` and ``end``.
4422 ``bylex`` when set to True, returns the range of elements from the
4423 sorted set between the ``start`` and ``end`` lexicographical closed
4424 range intervals.
4425 Valid ``start`` and ``end`` must start with ( or [, in order to specify
4426 whether the range interval is exclusive or inclusive, respectively.
4428 ``offset`` and ``num`` are specified, then return a slice of the range.
4429 Can't be provided when using ``bylex``.
4431 For more information see https://redis.io/commands/zrange
4432 """
4433 # Need to support ``desc`` also when using old redis version
4434 # because it was supported in 3.5.3 (of redis-py)
4435 if not byscore and not bylex and (offset is None and num is None) and desc:
4436 return self.zrevrange(name, start, end, withscores, score_cast_func)
4438 return self._zrange(
4439 "ZRANGE",
4440 None,
4441 name,
4442 start,
4443 end,
4444 desc,
4445 byscore,
4446 bylex,
4447 withscores,
4448 score_cast_func,
4449 offset,
4450 num,
4451 )
4453 def zrevrange(
4454 self,
4455 name: KeyT,
4456 start: int,
4457 end: int,
4458 withscores: bool = False,
4459 score_cast_func: Union[type, Callable] = float,
4460 ) -> ResponseT:
4461 """
4462 Return a range of values from sorted set ``name`` between
4463 ``start`` and ``end`` sorted in descending order.
4465 ``start`` and ``end`` can be negative, indicating the end of the range.
4467 ``withscores`` indicates to return the scores along with the values
4468 The return type is a list of (value, score) pairs
4470 ``score_cast_func`` a callable used to cast the score return value
4472 For more information see https://redis.io/commands/zrevrange
4473 """
4474 pieces = ["ZREVRANGE", name, start, end]
4475 if withscores:
4476 pieces.append(b"WITHSCORES")
4477 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4478 return self.execute_command(*pieces, **options)
4480 def zrangestore(
4481 self,
4482 dest: KeyT,
4483 name: KeyT,
4484 start: int,
4485 end: int,
4486 byscore: bool = False,
4487 bylex: bool = False,
4488 desc: bool = False,
4489 offset: Union[int, None] = None,
4490 num: Union[int, None] = None,
4491 ) -> ResponseT:
4492 """
4493 Stores in ``dest`` the result of a range of values from sorted set
4494 ``name`` between ``start`` and ``end`` sorted in ascending order.
4496 ``start`` and ``end`` can be negative, indicating the end of the range.
4498 ``byscore`` when set to True, returns the range of elements from the
4499 sorted set having scores equal or between ``start`` and ``end``.
4501 ``bylex`` when set to True, returns the range of elements from the
4502 sorted set between the ``start`` and ``end`` lexicographical closed
4503 range intervals.
4504 Valid ``start`` and ``end`` must start with ( or [, in order to specify
4505 whether the range interval is exclusive or inclusive, respectively.
4507 ``desc`` a boolean indicating whether to sort the results in reversed
4508 order.
4510 ``offset`` and ``num`` are specified, then return a slice of the range.
4511 Can't be provided when using ``bylex``.
4513 For more information see https://redis.io/commands/zrangestore
4514 """
4515 return self._zrange(
4516 "ZRANGESTORE",
4517 dest,
4518 name,
4519 start,
4520 end,
4521 desc,
4522 byscore,
4523 bylex,
4524 False,
4525 None,
4526 offset,
4527 num,
4528 )
4530 def zrangebylex(
4531 self,
4532 name: KeyT,
4533 min: EncodableT,
4534 max: EncodableT,
4535 start: Union[int, None] = None,
4536 num: Union[int, None] = None,
4537 ) -> ResponseT:
4538 """
4539 Return the lexicographical range of values from sorted set ``name``
4540 between ``min`` and ``max``.
4542 If ``start`` and ``num`` are specified, then return a slice of the
4543 range.
4545 For more information see https://redis.io/commands/zrangebylex
4546 """
4547 if (start is not None and num is None) or (num is not None and start is None):
4548 raise DataError("``start`` and ``num`` must both be specified")
4549 pieces = ["ZRANGEBYLEX", name, min, max]
4550 if start is not None and num is not None:
4551 pieces.extend([b"LIMIT", start, num])
4552 return self.execute_command(*pieces)
4554 def zrevrangebylex(
4555 self,
4556 name: KeyT,
4557 max: EncodableT,
4558 min: EncodableT,
4559 start: Union[int, None] = None,
4560 num: Union[int, None] = None,
4561 ) -> ResponseT:
4562 """
4563 Return the reversed lexicographical range of values from sorted set
4564 ``name`` between ``max`` and ``min``.
4566 If ``start`` and ``num`` are specified, then return a slice of the
4567 range.
4569 For more information see https://redis.io/commands/zrevrangebylex
4570 """
4571 if (start is not None and num is None) or (num is not None and start is None):
4572 raise DataError("``start`` and ``num`` must both be specified")
4573 pieces = ["ZREVRANGEBYLEX", name, max, min]
4574 if start is not None and num is not None:
4575 pieces.extend(["LIMIT", start, num])
4576 return self.execute_command(*pieces)
4578 def zrangebyscore(
4579 self,
4580 name: KeyT,
4581 min: ZScoreBoundT,
4582 max: ZScoreBoundT,
4583 start: Union[int, None] = None,
4584 num: Union[int, None] = None,
4585 withscores: bool = False,
4586 score_cast_func: Union[type, Callable] = float,
4587 ) -> ResponseT:
4588 """
4589 Return a range of values from the sorted set ``name`` with scores
4590 between ``min`` and ``max``.
4592 If ``start`` and ``num`` are specified, then return a slice
4593 of the range.
4595 ``withscores`` indicates to return the scores along with the values.
4596 The return type is a list of (value, score) pairs
4598 `score_cast_func`` a callable used to cast the score return value
4600 For more information see https://redis.io/commands/zrangebyscore
4601 """
4602 if (start is not None and num is None) or (num is not None and start is None):
4603 raise DataError("``start`` and ``num`` must both be specified")
4604 pieces = ["ZRANGEBYSCORE", name, min, max]
4605 if start is not None and num is not None:
4606 pieces.extend(["LIMIT", start, num])
4607 if withscores:
4608 pieces.append("WITHSCORES")
4609 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4610 return self.execute_command(*pieces, **options)
4612 def zrevrangebyscore(
4613 self,
4614 name: KeyT,
4615 max: ZScoreBoundT,
4616 min: ZScoreBoundT,
4617 start: Union[int, None] = None,
4618 num: Union[int, None] = None,
4619 withscores: bool = False,
4620 score_cast_func: Union[type, Callable] = float,
4621 ):
4622 """
4623 Return a range of values from the sorted set ``name`` with scores
4624 between ``min`` and ``max`` in descending order.
4626 If ``start`` and ``num`` are specified, then return a slice
4627 of the range.
4629 ``withscores`` indicates to return the scores along with the values.
4630 The return type is a list of (value, score) pairs
4632 ``score_cast_func`` a callable used to cast the score return value
4634 For more information see https://redis.io/commands/zrevrangebyscore
4635 """
4636 if (start is not None and num is None) or (num is not None and start is None):
4637 raise DataError("``start`` and ``num`` must both be specified")
4638 pieces = ["ZREVRANGEBYSCORE", name, max, min]
4639 if start is not None and num is not None:
4640 pieces.extend(["LIMIT", start, num])
4641 if withscores:
4642 pieces.append("WITHSCORES")
4643 options = {"withscores": withscores, "score_cast_func": score_cast_func}
4644 return self.execute_command(*pieces, **options)
4646 def zrank(self, name: KeyT, value: EncodableT) -> ResponseT:
4647 """
4648 Returns a 0-based value indicating the rank of ``value`` in sorted set
4649 ``name``
4651 For more information see https://redis.io/commands/zrank
4652 """
4653 return self.execute_command("ZRANK", name, value)
4655 def zrem(self, name: KeyT, *values: FieldT) -> ResponseT:
4656 """
4657 Remove member ``values`` from sorted set ``name``
4659 For more information see https://redis.io/commands/zrem
4660 """
4661 return self.execute_command("ZREM", name, *values)
4663 def zremrangebylex(self, name: KeyT, min: EncodableT, max: EncodableT) -> ResponseT:
4664 """
4665 Remove all elements in the sorted set ``name`` between the
4666 lexicographical range specified by ``min`` and ``max``.
4668 Returns the number of elements removed.
4670 For more information see https://redis.io/commands/zremrangebylex
4671 """
4672 return self.execute_command("ZREMRANGEBYLEX", name, min, max)
4674 def zremrangebyrank(self, name: KeyT, min: int, max: int) -> ResponseT:
4675 """
4676 Remove all elements in the sorted set ``name`` with ranks between
4677 ``min`` and ``max``. Values are 0-based, ordered from smallest score
4678 to largest. Values can be negative indicating the highest scores.
4679 Returns the number of elements removed
4681 For more information see https://redis.io/commands/zremrangebyrank
4682 """
4683 return self.execute_command("ZREMRANGEBYRANK", name, min, max)
4685 def zremrangebyscore(
4686 self, name: KeyT, min: ZScoreBoundT, max: ZScoreBoundT
4687 ) -> ResponseT:
4688 """
4689 Remove all elements in the sorted set ``name`` with scores
4690 between ``min`` and ``max``. Returns the number of elements removed.
4692 For more information see https://redis.io/commands/zremrangebyscore
4693 """
4694 return self.execute_command("ZREMRANGEBYSCORE", name, min, max)
4696 def zrevrank(self, name: KeyT, value: EncodableT) -> ResponseT:
4697 """
4698 Returns a 0-based value indicating the descending rank of
4699 ``value`` in sorted set ``name``
4701 For more information see https://redis.io/commands/zrevrank
4702 """
4703 return self.execute_command("ZREVRANK", name, value)
4705 def zscore(self, name: KeyT, value: EncodableT) -> ResponseT:
4706 """
4707 Return the score of element ``value`` in sorted set ``name``
4709 For more information see https://redis.io/commands/zscore
4710 """
4711 return self.execute_command("ZSCORE", name, value)
4713 def zunion(
4714 self,
4715 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4716 aggregate: Union[str, None] = None,
4717 withscores: bool = False,
4718 ) -> ResponseT:
4719 """
4720 Return the union of multiple sorted sets specified by ``keys``.
4721 ``keys`` can be provided as dictionary of keys and their weights.
4722 Scores will be aggregated based on the ``aggregate``, or SUM if
4723 none is provided.
4725 For more information see https://redis.io/commands/zunion
4726 """
4727 return self._zaggregate("ZUNION", None, keys, aggregate, withscores=withscores)
4729 def zunionstore(
4730 self,
4731 dest: KeyT,
4732 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4733 aggregate: Union[str, None] = None,
4734 ) -> ResponseT:
4735 """
4736 Union multiple sorted sets specified by ``keys`` into
4737 a new sorted set, ``dest``. Scores in the destination will be
4738 aggregated based on the ``aggregate``, or SUM if none is provided.
4740 For more information see https://redis.io/commands/zunionstore
4741 """
4742 return self._zaggregate("ZUNIONSTORE", dest, keys, aggregate)
4744 def zmscore(self, key: KeyT, members: List[str]) -> ResponseT:
4745 """
4746 Returns the scores associated with the specified members
4747 in the sorted set stored at key.
4748 ``members`` should be a list of the member name.
4749 Return type is a list of score.
4750 If the member does not exist, a None will be returned
4751 in corresponding position.
4753 For more information see https://redis.io/commands/zmscore
4754 """
4755 if not members:
4756 raise DataError("ZMSCORE members must be a non-empty list")
4757 pieces = [key] + members
4758 return self.execute_command("ZMSCORE", *pieces)
4760 def _zaggregate(
4761 self,
4762 command: str,
4763 dest: Union[KeyT, None],
4764 keys: Union[Sequence[KeyT], Mapping[AnyKeyT, float]],
4765 aggregate: Union[str, None] = None,
4766 **options,
4767 ) -> ResponseT:
4768 pieces: list[EncodableT] = [command]
4769 if dest is not None:
4770 pieces.append(dest)
4771 pieces.append(len(keys))
4772 if isinstance(keys, dict):
4773 keys, weights = keys.keys(), keys.values()
4774 else:
4775 weights = None
4776 pieces.extend(keys)
4777 if weights:
4778 pieces.append(b"WEIGHTS")
4779 pieces.extend(weights)
4780 if aggregate:
4781 if aggregate.upper() in ["SUM", "MIN", "MAX"]:
4782 pieces.append(b"AGGREGATE")
4783 pieces.append(aggregate)
4784 else:
4785 raise DataError("aggregate can be sum, min or max.")
4786 if options.get("withscores", False):
4787 pieces.append(b"WITHSCORES")
4788 return self.execute_command(*pieces, **options)
4791AsyncSortedSetCommands = SortedSetCommands
4794class HyperlogCommands(CommandsProtocol):
4795 """
4796 Redis commands of HyperLogLogs data type.
4797 see: https://redis.io/topics/data-types-intro#hyperloglogs
4798 """
4800 def pfadd(self, name: KeyT, *values: FieldT) -> ResponseT:
4801 """
4802 Adds the specified elements to the specified HyperLogLog.
4804 For more information see https://redis.io/commands/pfadd
4805 """
4806 return self.execute_command("PFADD", name, *values)
4808 def pfcount(self, *sources: KeyT) -> ResponseT:
4809 """
4810 Return the approximated cardinality of
4811 the set observed by the HyperLogLog at key(s).
4813 For more information see https://redis.io/commands/pfcount
4814 """
4815 return self.execute_command("PFCOUNT", *sources)
4817 def pfmerge(self, dest: KeyT, *sources: KeyT) -> ResponseT:
4818 """
4819 Merge N different HyperLogLogs into a single one.
4821 For more information see https://redis.io/commands/pfmerge
4822 """
4823 return self.execute_command("PFMERGE", dest, *sources)
4826AsyncHyperlogCommands = HyperlogCommands
4829class HashCommands(CommandsProtocol):
4830 """
4831 Redis commands for Hash data type.
4832 see: https://redis.io/topics/data-types-intro#redis-hashes
4833 """
4835 def hdel(self, name: str, *keys: List) -> Union[Awaitable[int], int]:
4836 """
4837 Delete ``keys`` from hash ``name``
4839 For more information see https://redis.io/commands/hdel
4840 """
4841 return self.execute_command("HDEL", name, *keys)
4843 def hexists(self, name: str, key: str) -> Union[Awaitable[bool], bool]:
4844 """
4845 Returns a boolean indicating if ``key`` exists within hash ``name``
4847 For more information see https://redis.io/commands/hexists
4848 """
4849 return self.execute_command("HEXISTS", name, key)
4851 def hget(
4852 self, name: str, key: str
4853 ) -> Union[Awaitable[Optional[str]], Optional[str]]:
4854 """
4855 Return the value of ``key`` within the hash ``name``
4857 For more information see https://redis.io/commands/hget
4858 """
4859 return self.execute_command("HGET", name, key)
4861 def hgetall(self, name: str) -> Union[Awaitable[dict], dict]:
4862 """
4863 Return a Python dict of the hash's name/value pairs
4865 For more information see https://redis.io/commands/hgetall
4866 """
4867 return self.execute_command("HGETALL", name)
4869 def hincrby(
4870 self, name: str, key: str, amount: int = 1
4871 ) -> Union[Awaitable[int], int]:
4872 """
4873 Increment the value of ``key`` in hash ``name`` by ``amount``
4875 For more information see https://redis.io/commands/hincrby
4876 """
4877 return self.execute_command("HINCRBY", name, key, amount)
4879 def hincrbyfloat(
4880 self, name: str, key: str, amount: float = 1.0
4881 ) -> Union[Awaitable[float], float]:
4882 """
4883 Increment the value of ``key`` in hash ``name`` by floating ``amount``
4885 For more information see https://redis.io/commands/hincrbyfloat
4886 """
4887 return self.execute_command("HINCRBYFLOAT", name, key, amount)
4889 def hkeys(self, name: str) -> Union[Awaitable[List], List]:
4890 """
4891 Return the list of keys within hash ``name``
4893 For more information see https://redis.io/commands/hkeys
4894 """
4895 return self.execute_command("HKEYS", name)
4897 def hlen(self, name: str) -> Union[Awaitable[int], int]:
4898 """
4899 Return the number of elements in hash ``name``
4901 For more information see https://redis.io/commands/hlen
4902 """
4903 return self.execute_command("HLEN", name)
4905 def hset(
4906 self,
4907 name: str,
4908 key: Optional[str] = None,
4909 value: Optional[str] = None,
4910 mapping: Optional[dict] = None,
4911 items: Optional[list] = None,
4912 ) -> Union[Awaitable[int], int]:
4913 """
4914 Set ``key`` to ``value`` within hash ``name``,
4915 ``mapping`` accepts a dict of key/value pairs that will be
4916 added to hash ``name``.
4917 ``items`` accepts a list of key/value pairs that will be
4918 added to hash ``name``.
4919 Returns the number of fields that were added.
4921 For more information see https://redis.io/commands/hset
4922 """
4923 if key is None and not mapping and not items:
4924 raise DataError("'hset' with no key value pairs")
4925 items = items or []
4926 if key is not None:
4927 items.extend((key, value))
4928 if mapping:
4929 for pair in mapping.items():
4930 items.extend(pair)
4932 return self.execute_command("HSET", name, *items)
4934 def hsetnx(self, name: str, key: str, value: str) -> Union[Awaitable[bool], bool]:
4935 """
4936 Set ``key`` to ``value`` within hash ``name`` if ``key`` does not
4937 exist. Returns 1 if HSETNX created a field, otherwise 0.
4939 For more information see https://redis.io/commands/hsetnx
4940 """
4941 return self.execute_command("HSETNX", name, key, value)
4943 def hmset(self, name: str, mapping: dict) -> Union[Awaitable[str], str]:
4944 """
4945 Set key to value within hash ``name`` for each corresponding
4946 key and value from the ``mapping`` dict.
4948 For more information see https://redis.io/commands/hmset
4949 """
4950 warnings.warn(
4951 f"{self.__class__.__name__}.hmset() is deprecated. "
4952 f"Use {self.__class__.__name__}.hset() instead.",
4953 DeprecationWarning,
4954 stacklevel=2,
4955 )
4956 if not mapping:
4957 raise DataError("'hmset' with 'mapping' of length 0")
4958 items = []
4959 for pair in mapping.items():
4960 items.extend(pair)
4961 return self.execute_command("HMSET", name, *items)
4963 def hmget(self, name: str, keys: List, *args: List) -> Union[Awaitable[List], List]:
4964 """
4965 Returns a list of values ordered identically to ``keys``
4967 For more information see https://redis.io/commands/hmget
4968 """
4969 args = list_or_args(keys, args)
4970 return self.execute_command("HMGET", name, *args)
4972 def hvals(self, name: str) -> Union[Awaitable[List], List]:
4973 """
4974 Return the list of values within hash ``name``
4976 For more information see https://redis.io/commands/hvals
4977 """
4978 return self.execute_command("HVALS", name)
4980 def hstrlen(self, name: str, key: str) -> Union[Awaitable[int], int]:
4981 """
4982 Return the number of bytes stored in the value of ``key``
4983 within hash ``name``
4985 For more information see https://redis.io/commands/hstrlen
4986 """
4987 return self.execute_command("HSTRLEN", name, key)
4990AsyncHashCommands = HashCommands
4993class Script:
4994 """
4995 An executable Lua script object returned by ``register_script``
4996 """
4998 def __init__(self, registered_client: "Redis", script: ScriptTextT):
4999 self.registered_client = registered_client
5000 self.script = script
5001 # Precalculate and store the SHA1 hex digest of the script.
5003 if isinstance(script, str):
5004 # We need the encoding from the client in order to generate an
5005 # accurate byte representation of the script
5006 try:
5007 encoder = registered_client.connection_pool.get_encoder()
5008 except AttributeError:
5009 # Cluster
5010 encoder = registered_client.get_encoder()
5011 script = encoder.encode(script)
5012 self.sha = hashlib.sha1(script).hexdigest()
5014 def __call__(
5015 self,
5016 keys: Union[Sequence[KeyT], None] = None,
5017 args: Union[Iterable[EncodableT], None] = None,
5018 client: Union["Redis", None] = None,
5019 ):
5020 """Execute the script, passing any required ``args``"""
5021 keys = keys or []
5022 args = args or []
5023 if client is None:
5024 client = self.registered_client
5025 args = tuple(keys) + tuple(args)
5026 # make sure the Redis server knows about the script
5027 from redis.client import Pipeline
5029 if isinstance(client, Pipeline):
5030 # Make sure the pipeline can register the script before executing.
5031 client.scripts.add(self)
5032 try:
5033 return client.evalsha(self.sha, len(keys), *args)
5034 except NoScriptError:
5035 # Maybe the client is pointed to a different server than the client
5036 # that created this instance?
5037 # Overwrite the sha just in case there was a discrepancy.
5038 self.sha = client.script_load(self.script)
5039 return client.evalsha(self.sha, len(keys), *args)
5042class AsyncScript:
5043 """
5044 An executable Lua script object returned by ``register_script``
5045 """
5047 def __init__(self, registered_client: "AsyncRedis", script: ScriptTextT):
5048 self.registered_client = registered_client
5049 self.script = script
5050 # Precalculate and store the SHA1 hex digest of the script.
5052 if isinstance(script, str):
5053 # We need the encoding from the client in order to generate an
5054 # accurate byte representation of the script
5055 try:
5056 encoder = registered_client.connection_pool.get_encoder()
5057 except AttributeError:
5058 # Cluster
5059 encoder = registered_client.get_encoder()
5060 script = encoder.encode(script)
5061 self.sha = hashlib.sha1(script).hexdigest()
5063 async def __call__(
5064 self,
5065 keys: Union[Sequence[KeyT], None] = None,
5066 args: Union[Iterable[EncodableT], None] = None,
5067 client: Union["AsyncRedis", None] = None,
5068 ):
5069 """Execute the script, passing any required ``args``"""
5070 keys = keys or []
5071 args = args or []
5072 if client is None:
5073 client = self.registered_client
5074 args = tuple(keys) + tuple(args)
5075 # make sure the Redis server knows about the script
5076 from redis.asyncio.client import Pipeline
5078 if isinstance(client, Pipeline):
5079 # Make sure the pipeline can register the script before executing.
5080 client.scripts.add(self)
5081 try:
5082 return await client.evalsha(self.sha, len(keys), *args)
5083 except NoScriptError:
5084 # Maybe the client is pointed to a different server than the client
5085 # that created this instance?
5086 # Overwrite the sha just in case there was a discrepancy.
5087 self.sha = await client.script_load(self.script)
5088 return await client.evalsha(self.sha, len(keys), *args)
5091class PubSubCommands(CommandsProtocol):
5092 """
5093 Redis PubSub commands.
5094 see https://redis.io/topics/pubsub
5095 """
5097 def publish(self, channel: ChannelT, message: EncodableT, **kwargs) -> ResponseT:
5098 """
5099 Publish ``message`` on ``channel``.
5100 Returns the number of subscribers the message was delivered to.
5102 For more information see https://redis.io/commands/publish
5103 """
5104 return self.execute_command("PUBLISH", channel, message, **kwargs)
5106 def pubsub_channels(self, pattern: PatternT = "*", **kwargs) -> ResponseT:
5107 """
5108 Return a list of channels that have at least one subscriber
5110 For more information see https://redis.io/commands/pubsub-channels
5111 """
5112 return self.execute_command("PUBSUB CHANNELS", pattern, **kwargs)
5114 def pubsub_numpat(self, **kwargs) -> ResponseT:
5115 """
5116 Returns the number of subscriptions to patterns
5118 For more information see https://redis.io/commands/pubsub-numpat
5119 """
5120 return self.execute_command("PUBSUB NUMPAT", **kwargs)
5122 def pubsub_numsub(self, *args: ChannelT, **kwargs) -> ResponseT:
5123 """
5124 Return a list of (channel, number of subscribers) tuples
5125 for each channel given in ``*args``
5127 For more information see https://redis.io/commands/pubsub-numsub
5128 """
5129 return self.execute_command("PUBSUB NUMSUB", *args, **kwargs)
5132AsyncPubSubCommands = PubSubCommands
5135class ScriptCommands(CommandsProtocol):
5136 """
5137 Redis Lua script commands. see:
5138 https://redis.com/ebook/part-3-next-steps/chapter-11-scripting-redis-with-lua/
5139 """
5141 def _eval(
5142 self, command: str, script: str, numkeys: int, *keys_and_args: list
5143 ) -> Union[Awaitable[str], str]:
5144 return self.execute_command(command, script, numkeys, *keys_and_args)
5146 def eval(
5147 self, script: str, numkeys: int, *keys_and_args: list
5148 ) -> Union[Awaitable[str], str]:
5149 """
5150 Execute the Lua ``script``, specifying the ``numkeys`` the script
5151 will touch and the key names and argument values in ``keys_and_args``.
5152 Returns the result of the script.
5154 In practice, use the object returned by ``register_script``. This
5155 function exists purely for Redis API completion.
5157 For more information see https://redis.io/commands/eval
5158 """
5159 return self._eval("EVAL", script, numkeys, *keys_and_args)
5161 def eval_ro(
5162 self, script: str, numkeys: int, *keys_and_args: list
5163 ) -> Union[Awaitable[str], str]:
5164 """
5165 The read-only variant of the EVAL command
5167 Execute the read-only Lua ``script`` specifying the ``numkeys`` the script
5168 will touch and the key names and argument values in ``keys_and_args``.
5169 Returns the result of the script.
5171 For more information see https://redis.io/commands/eval_ro
5172 """
5173 return self._eval("EVAL_RO", script, numkeys, *keys_and_args)
5175 def _evalsha(
5176 self, command: str, sha: str, numkeys: int, *keys_and_args: list
5177 ) -> Union[Awaitable[str], str]:
5178 return self.execute_command(command, sha, numkeys, *keys_and_args)
5180 def evalsha(
5181 self, sha: str, numkeys: int, *keys_and_args: list
5182 ) -> Union[Awaitable[str], str]:
5183 """
5184 Use the ``sha`` to execute a Lua script already registered via EVAL
5185 or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
5186 key names and argument values in ``keys_and_args``. Returns the result
5187 of the script.
5189 In practice, use the object returned by ``register_script``. This
5190 function exists purely for Redis API completion.
5192 For more information see https://redis.io/commands/evalsha
5193 """
5194 return self._evalsha("EVALSHA", sha, numkeys, *keys_and_args)
5196 def evalsha_ro(
5197 self, sha: str, numkeys: int, *keys_and_args: list
5198 ) -> Union[Awaitable[str], str]:
5199 """
5200 The read-only variant of the EVALSHA command
5202 Use the ``sha`` to execute a read-only Lua script already registered via EVAL
5203 or SCRIPT LOAD. Specify the ``numkeys`` the script will touch and the
5204 key names and argument values in ``keys_and_args``. Returns the result
5205 of the script.
5207 For more information see https://redis.io/commands/evalsha_ro
5208 """
5209 return self._evalsha("EVALSHA_RO", sha, numkeys, *keys_and_args)
5211 def script_exists(self, *args: str) -> ResponseT:
5212 """
5213 Check if a script exists in the script cache by specifying the SHAs of
5214 each script as ``args``. Returns a list of boolean values indicating if
5215 if each already script exists in the cache.
5217 For more information see https://redis.io/commands/script-exists
5218 """
5219 return self.execute_command("SCRIPT EXISTS", *args)
5221 def script_debug(self, *args) -> None:
5222 raise NotImplementedError(
5223 "SCRIPT DEBUG is intentionally not implemented in the client."
5224 )
5226 def script_flush(
5227 self, sync_type: Union[Literal["SYNC"], Literal["ASYNC"]] = None
5228 ) -> ResponseT:
5229 """Flush all scripts from the script cache.
5230 ``sync_type`` is by default SYNC (synchronous) but it can also be
5231 ASYNC.
5232 For more information see https://redis.io/commands/script-flush
5233 """
5235 # Redis pre 6 had no sync_type.
5236 if sync_type not in ["SYNC", "ASYNC", None]:
5237 raise DataError(
5238 "SCRIPT FLUSH defaults to SYNC in redis > 6.2, or "
5239 "accepts SYNC/ASYNC. For older versions, "
5240 "of redis leave as None."
5241 )
5242 if sync_type is None:
5243 pieces = []
5244 else:
5245 pieces = [sync_type]
5246 return self.execute_command("SCRIPT FLUSH", *pieces)
5248 def script_kill(self) -> ResponseT:
5249 """
5250 Kill the currently executing Lua script
5252 For more information see https://redis.io/commands/script-kill
5253 """
5254 return self.execute_command("SCRIPT KILL")
5256 def script_load(self, script: ScriptTextT) -> ResponseT:
5257 """
5258 Load a Lua ``script`` into the script cache. Returns the SHA.
5260 For more information see https://redis.io/commands/script-load
5261 """
5262 return self.execute_command("SCRIPT LOAD", script)
5264 def register_script(self: "Redis", script: ScriptTextT) -> Script:
5265 """
5266 Register a Lua ``script`` specifying the ``keys`` it will touch.
5267 Returns a Script object that is callable and hides the complexity of
5268 deal with scripts, keys, and shas. This is the preferred way to work
5269 with Lua scripts.
5270 """
5271 return Script(self, script)
5274class AsyncScriptCommands(ScriptCommands):
5275 async def script_debug(self, *args) -> None:
5276 return super().script_debug()
5278 def register_script(self: "AsyncRedis", script: ScriptTextT) -> AsyncScript:
5279 """
5280 Register a Lua ``script`` specifying the ``keys`` it will touch.
5281 Returns a Script object that is callable and hides the complexity of
5282 deal with scripts, keys, and shas. This is the preferred way to work
5283 with Lua scripts.
5284 """
5285 return AsyncScript(self, script)
5288class GeoCommands(CommandsProtocol):
5289 """
5290 Redis Geospatial commands.
5291 see: https://redis.com/redis-best-practices/indexing-patterns/geospatial/
5292 """
5294 def geoadd(
5295 self,
5296 name: KeyT,
5297 values: Sequence[EncodableT],
5298 nx: bool = False,
5299 xx: bool = False,
5300 ch: bool = False,
5301 ) -> ResponseT:
5302 """
5303 Add the specified geospatial items to the specified key identified
5304 by the ``name`` argument. The Geospatial items are given as ordered
5305 members of the ``values`` argument, each item or place is formed by
5306 the triad longitude, latitude and name.
5308 Note: You can use ZREM to remove elements.
5310 ``nx`` forces ZADD to only create new elements and not to update
5311 scores for elements that already exist.
5313 ``xx`` forces ZADD to only update scores of elements that already
5314 exist. New elements will not be added.
5316 ``ch`` modifies the return value to be the numbers of elements changed.
5317 Changed elements include new elements that were added and elements
5318 whose scores changed.
5320 For more information see https://redis.io/commands/geoadd
5321 """
5322 if nx and xx:
5323 raise DataError("GEOADD allows either 'nx' or 'xx', not both")
5324 if len(values) % 3 != 0:
5325 raise DataError("GEOADD requires places with lon, lat and name values")
5326 pieces = [name]
5327 if nx:
5328 pieces.append("NX")
5329 if xx:
5330 pieces.append("XX")
5331 if ch:
5332 pieces.append("CH")
5333 pieces.extend(values)
5334 return self.execute_command("GEOADD", *pieces)
5336 def geodist(
5337 self, name: KeyT, place1: FieldT, place2: FieldT, unit: Union[str, None] = None
5338 ) -> ResponseT:
5339 """
5340 Return the distance between ``place1`` and ``place2`` members of the
5341 ``name`` key.
5342 The units must be one of the following : m, km mi, ft. By default
5343 meters are used.
5345 For more information see https://redis.io/commands/geodist
5346 """
5347 pieces: list[EncodableT] = [name, place1, place2]
5348 if unit and unit not in ("m", "km", "mi", "ft"):
5349 raise DataError("GEODIST invalid unit")
5350 elif unit:
5351 pieces.append(unit)
5352 return self.execute_command("GEODIST", *pieces)
5354 def geohash(self, name: KeyT, *values: FieldT) -> ResponseT:
5355 """
5356 Return the geo hash string for each item of ``values`` members of
5357 the specified key identified by the ``name`` argument.
5359 For more information see https://redis.io/commands/geohash
5360 """
5361 return self.execute_command("GEOHASH", name, *values)
5363 def geopos(self, name: KeyT, *values: FieldT) -> ResponseT:
5364 """
5365 Return the positions of each item of ``values`` as members of
5366 the specified key identified by the ``name`` argument. Each position
5367 is represented by the pairs lon and lat.
5369 For more information see https://redis.io/commands/geopos
5370 """
5371 return self.execute_command("GEOPOS", name, *values)
5373 def georadius(
5374 self,
5375 name: KeyT,
5376 longitude: float,
5377 latitude: float,
5378 radius: float,
5379 unit: Union[str, None] = None,
5380 withdist: bool = False,
5381 withcoord: bool = False,
5382 withhash: bool = False,
5383 count: Union[int, None] = None,
5384 sort: Union[str, None] = None,
5385 store: Union[KeyT, None] = None,
5386 store_dist: Union[KeyT, None] = None,
5387 any: bool = False,
5388 ) -> ResponseT:
5389 """
5390 Return the members of the specified key identified by the
5391 ``name`` argument which are within the borders of the area specified
5392 with the ``latitude`` and ``longitude`` location and the maximum
5393 distance from the center specified by the ``radius`` value.
5395 The units must be one of the following : m, km mi, ft. By default
5397 ``withdist`` indicates to return the distances of each place.
5399 ``withcoord`` indicates to return the latitude and longitude of
5400 each place.
5402 ``withhash`` indicates to return the geohash string of each place.
5404 ``count`` indicates to return the number of elements up to N.
5406 ``sort`` indicates to return the places in a sorted way, ASC for
5407 nearest to fairest and DESC for fairest to nearest.
5409 ``store`` indicates to save the places names in a sorted set named
5410 with a specific key, each element of the destination sorted set is
5411 populated with the score got from the original geo sorted set.
5413 ``store_dist`` indicates to save the places names in a sorted set
5414 named with a specific key, instead of ``store`` the sorted set
5415 destination score is set with the distance.
5417 For more information see https://redis.io/commands/georadius
5418 """
5419 return self._georadiusgeneric(
5420 "GEORADIUS",
5421 name,
5422 longitude,
5423 latitude,
5424 radius,
5425 unit=unit,
5426 withdist=withdist,
5427 withcoord=withcoord,
5428 withhash=withhash,
5429 count=count,
5430 sort=sort,
5431 store=store,
5432 store_dist=store_dist,
5433 any=any,
5434 )
5436 def georadiusbymember(
5437 self,
5438 name: KeyT,
5439 member: FieldT,
5440 radius: float,
5441 unit: Union[str, None] = None,
5442 withdist: bool = False,
5443 withcoord: bool = False,
5444 withhash: bool = False,
5445 count: Union[int, None] = None,
5446 sort: Union[str, None] = None,
5447 store: Union[KeyT, None] = None,
5448 store_dist: Union[KeyT, None] = None,
5449 any: bool = False,
5450 ) -> ResponseT:
5451 """
5452 This command is exactly like ``georadius`` with the sole difference
5453 that instead of taking, as the center of the area to query, a longitude
5454 and latitude value, it takes the name of a member already existing
5455 inside the geospatial index represented by the sorted set.
5457 For more information see https://redis.io/commands/georadiusbymember
5458 """
5459 return self._georadiusgeneric(
5460 "GEORADIUSBYMEMBER",
5461 name,
5462 member,
5463 radius,
5464 unit=unit,
5465 withdist=withdist,
5466 withcoord=withcoord,
5467 withhash=withhash,
5468 count=count,
5469 sort=sort,
5470 store=store,
5471 store_dist=store_dist,
5472 any=any,
5473 )
5475 def _georadiusgeneric(
5476 self, command: str, *args: EncodableT, **kwargs: Union[EncodableT, None]
5477 ) -> ResponseT:
5478 pieces = list(args)
5479 if kwargs["unit"] and kwargs["unit"] not in ("m", "km", "mi", "ft"):
5480 raise DataError("GEORADIUS invalid unit")
5481 elif kwargs["unit"]:
5482 pieces.append(kwargs["unit"])
5483 else:
5484 pieces.append("m")
5486 if kwargs["any"] and kwargs["count"] is None:
5487 raise DataError("``any`` can't be provided without ``count``")
5489 for arg_name, byte_repr in (
5490 ("withdist", "WITHDIST"),
5491 ("withcoord", "WITHCOORD"),
5492 ("withhash", "WITHHASH"),
5493 ):
5494 if kwargs[arg_name]:
5495 pieces.append(byte_repr)
5497 if kwargs["count"] is not None:
5498 pieces.extend(["COUNT", kwargs["count"]])
5499 if kwargs["any"]:
5500 pieces.append("ANY")
5502 if kwargs["sort"]:
5503 if kwargs["sort"] == "ASC":
5504 pieces.append("ASC")
5505 elif kwargs["sort"] == "DESC":
5506 pieces.append("DESC")
5507 else:
5508 raise DataError("GEORADIUS invalid sort")
5510 if kwargs["store"] and kwargs["store_dist"]:
5511 raise DataError("GEORADIUS store and store_dist cant be set together")
5513 if kwargs["store"]:
5514 pieces.extend([b"STORE", kwargs["store"]])
5516 if kwargs["store_dist"]:
5517 pieces.extend([b"STOREDIST", kwargs["store_dist"]])
5519 return self.execute_command(command, *pieces, **kwargs)
5521 def geosearch(
5522 self,
5523 name: KeyT,
5524 member: Union[FieldT, None] = None,
5525 longitude: Union[float, None] = None,
5526 latitude: Union[float, None] = None,
5527 unit: str = "m",
5528 radius: Union[float, None] = None,
5529 width: Union[float, None] = None,
5530 height: Union[float, None] = None,
5531 sort: Union[str, None] = None,
5532 count: Union[int, None] = None,
5533 any: bool = False,
5534 withcoord: bool = False,
5535 withdist: bool = False,
5536 withhash: bool = False,
5537 ) -> ResponseT:
5538 """
5539 Return the members of specified key identified by the
5540 ``name`` argument, which are within the borders of the
5541 area specified by a given shape. This command extends the
5542 GEORADIUS command, so in addition to searching within circular
5543 areas, it supports searching within rectangular areas.
5544 This command should be used in place of the deprecated
5545 GEORADIUS and GEORADIUSBYMEMBER commands.
5546 ``member`` Use the position of the given existing
5547 member in the sorted set. Can't be given with ``longitude``
5548 and ``latitude``.
5549 ``longitude`` and ``latitude`` Use the position given by
5550 this coordinates. Can't be given with ``member``
5551 ``radius`` Similar to GEORADIUS, search inside circular
5552 area according the given radius. Can't be given with
5553 ``height`` and ``width``.
5554 ``height`` and ``width`` Search inside an axis-aligned
5555 rectangle, determined by the given height and width.
5556 Can't be given with ``radius``
5557 ``unit`` must be one of the following : m, km, mi, ft.
5558 `m` for meters (the default value), `km` for kilometers,
5559 `mi` for miles and `ft` for feet.
5560 ``sort`` indicates to return the places in a sorted way,
5561 ASC for nearest to furthest and DESC for furthest to nearest.
5562 ``count`` limit the results to the first count matching items.
5563 ``any`` is set to True, the command will return as soon as
5564 enough matches are found. Can't be provided without ``count``
5565 ``withdist`` indicates to return the distances of each place.
5566 ``withcoord`` indicates to return the latitude and longitude of
5567 each place.
5568 ``withhash`` indicates to return the geohash string of each place.
5570 For more information see https://redis.io/commands/geosearch
5571 """
5573 return self._geosearchgeneric(
5574 "GEOSEARCH",
5575 name,
5576 member=member,
5577 longitude=longitude,
5578 latitude=latitude,
5579 unit=unit,
5580 radius=radius,
5581 width=width,
5582 height=height,
5583 sort=sort,
5584 count=count,
5585 any=any,
5586 withcoord=withcoord,
5587 withdist=withdist,
5588 withhash=withhash,
5589 store=None,
5590 store_dist=None,
5591 )
5593 def geosearchstore(
5594 self,
5595 dest: KeyT,
5596 name: KeyT,
5597 member: Union[FieldT, None] = None,
5598 longitude: Union[float, None] = None,
5599 latitude: Union[float, None] = None,
5600 unit: str = "m",
5601 radius: Union[float, None] = None,
5602 width: Union[float, None] = None,
5603 height: Union[float, None] = None,
5604 sort: Union[str, None] = None,
5605 count: Union[int, None] = None,
5606 any: bool = False,
5607 storedist: bool = False,
5608 ) -> ResponseT:
5609 """
5610 This command is like GEOSEARCH, but stores the result in
5611 ``dest``. By default, it stores the results in the destination
5612 sorted set with their geospatial information.
5613 if ``store_dist`` set to True, the command will stores the
5614 items in a sorted set populated with their distance from the
5615 center of the circle or box, as a floating-point number.
5617 For more information see https://redis.io/commands/geosearchstore
5618 """
5619 return self._geosearchgeneric(
5620 "GEOSEARCHSTORE",
5621 dest,
5622 name,
5623 member=member,
5624 longitude=longitude,
5625 latitude=latitude,
5626 unit=unit,
5627 radius=radius,
5628 width=width,
5629 height=height,
5630 sort=sort,
5631 count=count,
5632 any=any,
5633 withcoord=None,
5634 withdist=None,
5635 withhash=None,
5636 store=None,
5637 store_dist=storedist,
5638 )
5640 def _geosearchgeneric(
5641 self, command: str, *args: EncodableT, **kwargs: Union[EncodableT, None]
5642 ) -> ResponseT:
5643 pieces = list(args)
5645 # FROMMEMBER or FROMLONLAT
5646 if kwargs["member"] is None:
5647 if kwargs["longitude"] is None or kwargs["latitude"] is None:
5648 raise DataError("GEOSEARCH must have member or longitude and latitude")
5649 if kwargs["member"]:
5650 if kwargs["longitude"] or kwargs["latitude"]:
5651 raise DataError(
5652 "GEOSEARCH member and longitude or latitude cant be set together"
5653 )
5654 pieces.extend([b"FROMMEMBER", kwargs["member"]])
5655 if kwargs["longitude"] is not None and kwargs["latitude"] is not None:
5656 pieces.extend([b"FROMLONLAT", kwargs["longitude"], kwargs["latitude"]])
5658 # BYRADIUS or BYBOX
5659 if kwargs["radius"] is None:
5660 if kwargs["width"] is None or kwargs["height"] is None:
5661 raise DataError("GEOSEARCH must have radius or width and height")
5662 if kwargs["unit"] is None:
5663 raise DataError("GEOSEARCH must have unit")
5664 if kwargs["unit"].lower() not in ("m", "km", "mi", "ft"):
5665 raise DataError("GEOSEARCH invalid unit")
5666 if kwargs["radius"]:
5667 if kwargs["width"] or kwargs["height"]:
5668 raise DataError(
5669 "GEOSEARCH radius and width or height cant be set together"
5670 )
5671 pieces.extend([b"BYRADIUS", kwargs["radius"], kwargs["unit"]])
5672 if kwargs["width"] and kwargs["height"]:
5673 pieces.extend([b"BYBOX", kwargs["width"], kwargs["height"], kwargs["unit"]])
5675 # sort
5676 if kwargs["sort"]:
5677 if kwargs["sort"].upper() == "ASC":
5678 pieces.append(b"ASC")
5679 elif kwargs["sort"].upper() == "DESC":
5680 pieces.append(b"DESC")
5681 else:
5682 raise DataError("GEOSEARCH invalid sort")
5684 # count any
5685 if kwargs["count"]:
5686 pieces.extend([b"COUNT", kwargs["count"]])
5687 if kwargs["any"]:
5688 pieces.append(b"ANY")
5689 elif kwargs["any"]:
5690 raise DataError("GEOSEARCH ``any`` can't be provided without count")
5692 # other properties
5693 for arg_name, byte_repr in (
5694 ("withdist", b"WITHDIST"),
5695 ("withcoord", b"WITHCOORD"),
5696 ("withhash", b"WITHHASH"),
5697 ("store_dist", b"STOREDIST"),
5698 ):
5699 if kwargs[arg_name]:
5700 pieces.append(byte_repr)
5702 return self.execute_command(command, *pieces, **kwargs)
5705AsyncGeoCommands = GeoCommands
5708class ModuleCommands(CommandsProtocol):
5709 """
5710 Redis Module commands.
5711 see: https://redis.io/topics/modules-intro
5712 """
5714 def module_load(self, path, *args) -> ResponseT:
5715 """
5716 Loads the module from ``path``.
5717 Passes all ``*args`` to the module, during loading.
5718 Raises ``ModuleError`` if a module is not found at ``path``.
5720 For more information see https://redis.io/commands/module-load
5721 """
5722 return self.execute_command("MODULE LOAD", path, *args)
5724 def module_loadex(
5725 self,
5726 path: str,
5727 options: Optional[List[str]] = None,
5728 args: Optional[List[str]] = None,
5729 ) -> ResponseT:
5730 """
5731 Loads a module from a dynamic library at runtime with configuration directives.
5733 For more information see https://redis.io/commands/module-loadex
5734 """
5735 pieces = []
5736 if options is not None:
5737 pieces.append("CONFIG")
5738 pieces.extend(options)
5739 if args is not None:
5740 pieces.append("ARGS")
5741 pieces.extend(args)
5743 return self.execute_command("MODULE LOADEX", path, *pieces)
5745 def module_unload(self, name) -> ResponseT:
5746 """
5747 Unloads the module ``name``.
5748 Raises ``ModuleError`` if ``name`` is not in loaded modules.
5750 For more information see https://redis.io/commands/module-unload
5751 """
5752 return self.execute_command("MODULE UNLOAD", name)
5754 def module_list(self) -> ResponseT:
5755 """
5756 Returns a list of dictionaries containing the name and version of
5757 all loaded modules.
5759 For more information see https://redis.io/commands/module-list
5760 """
5761 return self.execute_command("MODULE LIST")
5763 def command_info(self) -> None:
5764 raise NotImplementedError(
5765 "COMMAND INFO is intentionally not implemented in the client."
5766 )
5768 def command_count(self) -> ResponseT:
5769 return self.execute_command("COMMAND COUNT")
5771 def command_getkeys(self, *args) -> ResponseT:
5772 return self.execute_command("COMMAND GETKEYS", *args)
5774 def command(self) -> ResponseT:
5775 return self.execute_command("COMMAND")
5778class Script:
5779 """
5780 An executable Lua script object returned by ``register_script``
5781 """
5783 def __init__(self, registered_client, script):
5784 self.registered_client = registered_client
5785 self.script = script
5786 # Precalculate and store the SHA1 hex digest of the script.
5788 if isinstance(script, str):
5789 # We need the encoding from the client in order to generate an
5790 # accurate byte representation of the script
5791 encoder = self.get_encoder()
5792 script = encoder.encode(script)
5793 self.sha = hashlib.sha1(script).hexdigest()
5795 def __call__(self, keys=[], args=[], client=None):
5796 "Execute the script, passing any required ``args``"
5797 if client is None:
5798 client = self.registered_client
5799 args = tuple(keys) + tuple(args)
5800 # make sure the Redis server knows about the script
5801 from redis.client import Pipeline
5803 if isinstance(client, Pipeline):
5804 # Make sure the pipeline can register the script before executing.
5805 client.scripts.add(self)
5806 try:
5807 return client.evalsha(self.sha, len(keys), *args)
5808 except NoScriptError:
5809 # Maybe the client is pointed to a different server than the client
5810 # that created this instance?
5811 # Overwrite the sha just in case there was a discrepancy.
5812 self.sha = client.script_load(self.script)
5813 return client.evalsha(self.sha, len(keys), *args)
5815 def get_encoder(self):
5816 """Get the encoder to encode string scripts into bytes."""
5817 try:
5818 return self.registered_client.get_encoder()
5819 except AttributeError:
5820 # DEPRECATED
5821 # In version <=4.1.2, this was the code we used to get the encoder.
5822 # However, after 4.1.2 we added support for scripting in clustered
5823 # redis. ClusteredRedis doesn't have a `.connection_pool` attribute
5824 # so we changed the Script class to use
5825 # `self.registered_client.get_encoder` (see above).
5826 # However, that is technically a breaking change, as consumers who
5827 # use Scripts directly might inject a `registered_client` that
5828 # doesn't have a `.get_encoder` field. This try/except prevents us
5829 # from breaking backward-compatibility. Ideally, it would be
5830 # removed in the next major release.
5831 return self.registered_client.connection_pool.get_encoder()
5834class AsyncModuleCommands(ModuleCommands):
5835 async def command_info(self) -> None:
5836 return super().command_info()
5839class ClusterCommands(CommandsProtocol):
5840 """
5841 Class for Redis Cluster commands
5842 """
5844 def cluster(self, cluster_arg, *args, **kwargs) -> ResponseT:
5845 return self.execute_command(f"CLUSTER {cluster_arg.upper()}", *args, **kwargs)
5847 def readwrite(self, **kwargs) -> ResponseT:
5848 """
5849 Disables read queries for a connection to a Redis Cluster slave node.
5851 For more information see https://redis.io/commands/readwrite
5852 """
5853 return self.execute_command("READWRITE", **kwargs)
5855 def readonly(self, **kwargs) -> ResponseT:
5856 """
5857 Enables read queries for a connection to a Redis Cluster replica node.
5859 For more information see https://redis.io/commands/readonly
5860 """
5861 return self.execute_command("READONLY", **kwargs)
5864AsyncClusterCommands = ClusterCommands
5867class FunctionCommands:
5868 """
5869 Redis Function commands
5870 """
5872 def function_load(
5873 self, code: str, replace: Optional[bool] = False
5874 ) -> Union[Awaitable[str], str]:
5875 """
5876 Load a library to Redis.
5877 :param code: the source code (must start with
5878 Shebang statement that provides a metadata about the library)
5879 :param replace: changes the behavior to overwrite the existing library
5880 with the new contents.
5881 Return the library name that was loaded.
5883 For more information see https://redis.io/commands/function-load
5884 """
5885 pieces = ["REPLACE"] if replace else []
5886 pieces.append(code)
5887 return self.execute_command("FUNCTION LOAD", *pieces)
5889 def function_delete(self, library: str) -> Union[Awaitable[str], str]:
5890 """
5891 Delete the library called ``library`` and all its functions.
5893 For more information see https://redis.io/commands/function-delete
5894 """
5895 return self.execute_command("FUNCTION DELETE", library)
5897 def function_flush(self, mode: str = "SYNC") -> Union[Awaitable[str], str]:
5898 """
5899 Deletes all the libraries.
5901 For more information see https://redis.io/commands/function-flush
5902 """
5903 return self.execute_command("FUNCTION FLUSH", mode)
5905 def function_list(
5906 self, library: Optional[str] = "*", withcode: Optional[bool] = False
5907 ) -> Union[Awaitable[List], List]:
5908 """
5909 Return information about the functions and libraries.
5910 :param library: pecify a pattern for matching library names
5911 :param withcode: cause the server to include the libraries source
5912 implementation in the reply
5913 """
5914 args = ["LIBRARYNAME", library]
5915 if withcode:
5916 args.append("WITHCODE")
5917 return self.execute_command("FUNCTION LIST", *args)
5919 def _fcall(
5920 self, command: str, function, numkeys: int, *keys_and_args: Optional[List]
5921 ) -> Union[Awaitable[str], str]:
5922 return self.execute_command(command, function, numkeys, *keys_and_args)
5924 def fcall(
5925 self, function, numkeys: int, *keys_and_args: Optional[List]
5926 ) -> Union[Awaitable[str], str]:
5927 """
5928 Invoke a function.
5930 For more information see https://redis.io/commands/fcall
5931 """
5932 return self._fcall("FCALL", function, numkeys, *keys_and_args)
5934 def fcall_ro(
5935 self, function, numkeys: int, *keys_and_args: Optional[List]
5936 ) -> Union[Awaitable[str], str]:
5937 """
5938 This is a read-only variant of the FCALL command that cannot
5939 execute commands that modify data.
5941 For more information see https://redis.io/commands/fcal_ro
5942 """
5943 return self._fcall("FCALL_RO", function, numkeys, *keys_and_args)
5945 def function_dump(self) -> Union[Awaitable[str], str]:
5946 """
5947 Return the serialized payload of loaded libraries.
5949 For more information see https://redis.io/commands/function-dump
5950 """
5951 from redis.client import NEVER_DECODE
5953 options = {}
5954 options[NEVER_DECODE] = []
5956 return self.execute_command("FUNCTION DUMP", **options)
5958 def function_restore(
5959 self, payload: str, policy: Optional[str] = "APPEND"
5960 ) -> Union[Awaitable[str], str]:
5961 """
5962 Restore libraries from the serialized ``payload``.
5963 You can use the optional policy argument to provide a policy
5964 for handling existing libraries.
5966 For more information see https://redis.io/commands/function-restore
5967 """
5968 return self.execute_command("FUNCTION RESTORE", payload, policy)
5970 def function_kill(self) -> Union[Awaitable[str], str]:
5971 """
5972 Kill a function that is currently executing.
5974 For more information see https://redis.io/commands/function-kill
5975 """
5976 return self.execute_command("FUNCTION KILL")
5978 def function_stats(self) -> Union[Awaitable[List], List]:
5979 """
5980 Return information about the function that's currently running
5981 and information about the available execution engines.
5983 For more information see https://redis.io/commands/function-stats
5984 """
5985 return self.execute_command("FUNCTION STATS")
5988AsyncFunctionCommands = FunctionCommands
5991class DataAccessCommands(
5992 BasicKeyCommands,
5993 HyperlogCommands,
5994 HashCommands,
5995 GeoCommands,
5996 ListCommands,
5997 ScanCommands,
5998 SetCommands,
5999 StreamCommands,
6000 SortedSetCommands,
6001):
6002 """
6003 A class containing all of the implemented data access redis commands.
6004 This class is to be used as a mixin for synchronous Redis clients.
6005 """
6008class AsyncDataAccessCommands(
6009 AsyncBasicKeyCommands,
6010 AsyncHyperlogCommands,
6011 AsyncHashCommands,
6012 AsyncGeoCommands,
6013 AsyncListCommands,
6014 AsyncScanCommands,
6015 AsyncSetCommands,
6016 AsyncStreamCommands,
6017 AsyncSortedSetCommands,
6018):
6019 """
6020 A class containing all of the implemented data access redis commands.
6021 This class is to be used as a mixin for asynchronous Redis clients.
6022 """
6025class CoreCommands(
6026 ACLCommands,
6027 ClusterCommands,
6028 DataAccessCommands,
6029 ManagementCommands,
6030 ModuleCommands,
6031 PubSubCommands,
6032 ScriptCommands,
6033 FunctionCommands,
6034):
6035 """
6036 A class containing all of the implemented redis commands. This class is
6037 to be used as a mixin for synchronous Redis clients.
6038 """
6041class AsyncCoreCommands(
6042 AsyncACLCommands,
6043 AsyncClusterCommands,
6044 AsyncDataAccessCommands,
6045 AsyncManagementCommands,
6046 AsyncModuleCommands,
6047 AsyncPubSubCommands,
6048 AsyncScriptCommands,
6049 AsyncFunctionCommands,
6050):
6051 """
6052 A class containing all of the implemented redis commands. This class is
6053 to be used as a mixin for asynchronous Redis clients.
6054 """