redis 5.3.0b4__py3-none-any.whl → 6.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redis/__init__.py +2 -11
- redis/_parsers/base.py +14 -2
- redis/asyncio/client.py +21 -13
- redis/asyncio/cluster.py +79 -56
- redis/asyncio/connection.py +40 -11
- redis/asyncio/lock.py +26 -5
- redis/asyncio/sentinel.py +9 -1
- redis/asyncio/utils.py +1 -1
- redis/auth/token.py +6 -2
- redis/backoff.py +15 -0
- redis/client.py +80 -59
- redis/cluster.py +114 -52
- redis/commands/cluster.py +1 -11
- redis/commands/core.py +218 -206
- redis/commands/helpers.py +0 -70
- redis/commands/redismodules.py +0 -20
- redis/commands/search/aggregation.py +3 -1
- redis/commands/search/commands.py +41 -14
- redis/commands/search/dialect.py +3 -0
- redis/commands/search/profile_information.py +14 -0
- redis/commands/search/query.py +5 -1
- redis/connection.py +48 -23
- redis/exceptions.py +4 -1
- redis/lock.py +24 -4
- redis/ocsp.py +2 -1
- redis/sentinel.py +1 -1
- redis/typing.py +1 -1
- redis/utils.py +107 -1
- {redis-5.3.0b4.dist-info → redis-6.0.0b1.dist-info}/METADATA +57 -23
- {redis-5.3.0b4.dist-info → redis-6.0.0b1.dist-info}/RECORD +33 -40
- {redis-5.3.0b4.dist-info → redis-6.0.0b1.dist-info}/WHEEL +1 -2
- redis/commands/graph/__init__.py +0 -263
- redis/commands/graph/commands.py +0 -313
- redis/commands/graph/edge.py +0 -91
- redis/commands/graph/exceptions.py +0 -3
- redis/commands/graph/execution_plan.py +0 -211
- redis/commands/graph/node.py +0 -88
- redis/commands/graph/path.py +0 -78
- redis/commands/graph/query_result.py +0 -588
- redis-5.3.0b4.dist-info/top_level.txt +0 -1
- /redis/commands/search/{indexDefinition.py → index_definition.py} +0 -0
- {redis-5.3.0b4.dist-info → redis-6.0.0b1.dist-info/licenses}/LICENSE +0 -0
redis/commands/core.py
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
import datetime
|
|
4
4
|
import hashlib
|
|
5
5
|
import warnings
|
|
6
|
+
from enum import Enum
|
|
6
7
|
from typing import (
|
|
7
8
|
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
8
10
|
AsyncIterator,
|
|
9
11
|
Awaitable,
|
|
10
12
|
Callable,
|
|
@@ -35,6 +37,7 @@ from redis.typing import (
|
|
|
35
37
|
GroupT,
|
|
36
38
|
KeysT,
|
|
37
39
|
KeyT,
|
|
40
|
+
Number,
|
|
38
41
|
PatternT,
|
|
39
42
|
ResponseT,
|
|
40
43
|
ScriptTextT,
|
|
@@ -42,6 +45,10 @@ from redis.typing import (
|
|
|
42
45
|
TimeoutSecT,
|
|
43
46
|
ZScoreBoundT,
|
|
44
47
|
)
|
|
48
|
+
from redis.utils import (
|
|
49
|
+
deprecated_function,
|
|
50
|
+
extract_expire_flags,
|
|
51
|
+
)
|
|
45
52
|
|
|
46
53
|
from .helpers import list_or_args
|
|
47
54
|
|
|
@@ -528,7 +535,7 @@ class ManagementCommands(CommandsProtocol):
|
|
|
528
535
|
raise DataError("client_id must be a list")
|
|
529
536
|
if client_id:
|
|
530
537
|
args.append(b"ID")
|
|
531
|
-
args
|
|
538
|
+
args += client_id
|
|
532
539
|
return self.execute_command("CLIENT LIST", *args, **kwargs)
|
|
533
540
|
|
|
534
541
|
def client_getname(self, **kwargs) -> ResponseT:
|
|
@@ -1835,10 +1842,10 @@ class BasicKeyCommands(CommandsProtocol):
|
|
|
1835
1842
|
def getex(
|
|
1836
1843
|
self,
|
|
1837
1844
|
name: KeyT,
|
|
1838
|
-
ex:
|
|
1839
|
-
px:
|
|
1840
|
-
exat:
|
|
1841
|
-
pxat:
|
|
1845
|
+
ex: Optional[ExpiryT] = None,
|
|
1846
|
+
px: Optional[ExpiryT] = None,
|
|
1847
|
+
exat: Optional[AbsExpiryT] = None,
|
|
1848
|
+
pxat: Optional[AbsExpiryT] = None,
|
|
1842
1849
|
persist: bool = False,
|
|
1843
1850
|
) -> ResponseT:
|
|
1844
1851
|
"""
|
|
@@ -1861,7 +1868,6 @@ class BasicKeyCommands(CommandsProtocol):
|
|
|
1861
1868
|
|
|
1862
1869
|
For more information see https://redis.io/commands/getex
|
|
1863
1870
|
"""
|
|
1864
|
-
|
|
1865
1871
|
opset = {ex, px, exat, pxat}
|
|
1866
1872
|
if len(opset) > 2 or len(opset) > 1 and persist:
|
|
1867
1873
|
raise DataError(
|
|
@@ -1869,33 +1875,12 @@ class BasicKeyCommands(CommandsProtocol):
|
|
|
1869
1875
|
"and ``persist`` are mutually exclusive."
|
|
1870
1876
|
)
|
|
1871
1877
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
if ex is not None:
|
|
1875
|
-
pieces.append("EX")
|
|
1876
|
-
if isinstance(ex, datetime.timedelta):
|
|
1877
|
-
ex = int(ex.total_seconds())
|
|
1878
|
-
pieces.append(ex)
|
|
1879
|
-
if px is not None:
|
|
1880
|
-
pieces.append("PX")
|
|
1881
|
-
if isinstance(px, datetime.timedelta):
|
|
1882
|
-
px = int(px.total_seconds() * 1000)
|
|
1883
|
-
pieces.append(px)
|
|
1884
|
-
# similar to pexpireat command
|
|
1885
|
-
if exat is not None:
|
|
1886
|
-
pieces.append("EXAT")
|
|
1887
|
-
if isinstance(exat, datetime.datetime):
|
|
1888
|
-
exat = int(exat.timestamp())
|
|
1889
|
-
pieces.append(exat)
|
|
1890
|
-
if pxat is not None:
|
|
1891
|
-
pieces.append("PXAT")
|
|
1892
|
-
if isinstance(pxat, datetime.datetime):
|
|
1893
|
-
pxat = int(pxat.timestamp() * 1000)
|
|
1894
|
-
pieces.append(pxat)
|
|
1878
|
+
exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
|
|
1879
|
+
|
|
1895
1880
|
if persist:
|
|
1896
|
-
|
|
1881
|
+
exp_options.append("PERSIST")
|
|
1897
1882
|
|
|
1898
|
-
return self.execute_command("GETEX", name, *
|
|
1883
|
+
return self.execute_command("GETEX", name, *exp_options)
|
|
1899
1884
|
|
|
1900
1885
|
def __getitem__(self, name: KeyT):
|
|
1901
1886
|
"""
|
|
@@ -2253,14 +2238,14 @@ class BasicKeyCommands(CommandsProtocol):
|
|
|
2253
2238
|
self,
|
|
2254
2239
|
name: KeyT,
|
|
2255
2240
|
value: EncodableT,
|
|
2256
|
-
ex:
|
|
2257
|
-
px:
|
|
2241
|
+
ex: Optional[ExpiryT] = None,
|
|
2242
|
+
px: Optional[ExpiryT] = None,
|
|
2258
2243
|
nx: bool = False,
|
|
2259
2244
|
xx: bool = False,
|
|
2260
2245
|
keepttl: bool = False,
|
|
2261
2246
|
get: bool = False,
|
|
2262
|
-
exat:
|
|
2263
|
-
pxat:
|
|
2247
|
+
exat: Optional[AbsExpiryT] = None,
|
|
2248
|
+
pxat: Optional[AbsExpiryT] = None,
|
|
2264
2249
|
) -> ResponseT:
|
|
2265
2250
|
"""
|
|
2266
2251
|
Set the value at key ``name`` to ``value``
|
|
@@ -2290,36 +2275,21 @@ class BasicKeyCommands(CommandsProtocol):
|
|
|
2290
2275
|
|
|
2291
2276
|
For more information see https://redis.io/commands/set
|
|
2292
2277
|
"""
|
|
2278
|
+
opset = {ex, px, exat, pxat}
|
|
2279
|
+
if len(opset) > 2 or len(opset) > 1 and keepttl:
|
|
2280
|
+
raise DataError(
|
|
2281
|
+
"``ex``, ``px``, ``exat``, ``pxat``, "
|
|
2282
|
+
"and ``keepttl`` are mutually exclusive."
|
|
2283
|
+
)
|
|
2284
|
+
|
|
2285
|
+
if nx and xx:
|
|
2286
|
+
raise DataError("``nx`` and ``xx`` are mutually exclusive.")
|
|
2287
|
+
|
|
2293
2288
|
pieces: list[EncodableT] = [name, value]
|
|
2294
2289
|
options = {}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
pieces.append(int(ex.total_seconds()))
|
|
2299
|
-
elif isinstance(ex, int):
|
|
2300
|
-
pieces.append(ex)
|
|
2301
|
-
elif isinstance(ex, str) and ex.isdigit():
|
|
2302
|
-
pieces.append(int(ex))
|
|
2303
|
-
else:
|
|
2304
|
-
raise DataError("ex must be datetime.timedelta or int")
|
|
2305
|
-
if px is not None:
|
|
2306
|
-
pieces.append("PX")
|
|
2307
|
-
if isinstance(px, datetime.timedelta):
|
|
2308
|
-
pieces.append(int(px.total_seconds() * 1000))
|
|
2309
|
-
elif isinstance(px, int):
|
|
2310
|
-
pieces.append(px)
|
|
2311
|
-
else:
|
|
2312
|
-
raise DataError("px must be datetime.timedelta or int")
|
|
2313
|
-
if exat is not None:
|
|
2314
|
-
pieces.append("EXAT")
|
|
2315
|
-
if isinstance(exat, datetime.datetime):
|
|
2316
|
-
exat = int(exat.timestamp())
|
|
2317
|
-
pieces.append(exat)
|
|
2318
|
-
if pxat is not None:
|
|
2319
|
-
pieces.append("PXAT")
|
|
2320
|
-
if isinstance(pxat, datetime.datetime):
|
|
2321
|
-
pxat = int(pxat.timestamp() * 1000)
|
|
2322
|
-
pieces.append(pxat)
|
|
2290
|
+
|
|
2291
|
+
pieces.extend(extract_expire_flags(ex, px, exat, pxat))
|
|
2292
|
+
|
|
2323
2293
|
if keepttl:
|
|
2324
2294
|
pieces.append("KEEPTTL")
|
|
2325
2295
|
|
|
@@ -2496,7 +2466,7 @@ class BasicKeyCommands(CommandsProtocol):
|
|
|
2496
2466
|
|
|
2497
2467
|
def unwatch(self) -> None:
|
|
2498
2468
|
"""
|
|
2499
|
-
Unwatches
|
|
2469
|
+
Unwatches all previously watched keys for a transaction
|
|
2500
2470
|
|
|
2501
2471
|
For more information see https://redis.io/commands/unwatch
|
|
2502
2472
|
"""
|
|
@@ -2567,7 +2537,7 @@ class ListCommands(CommandsProtocol):
|
|
|
2567
2537
|
"""
|
|
2568
2538
|
|
|
2569
2539
|
def blpop(
|
|
2570
|
-
self, keys: List, timeout: Optional[
|
|
2540
|
+
self, keys: List, timeout: Optional[Number] = 0
|
|
2571
2541
|
) -> Union[Awaitable[list], list]:
|
|
2572
2542
|
"""
|
|
2573
2543
|
LPOP a value off of the first non-empty list
|
|
@@ -2588,7 +2558,7 @@ class ListCommands(CommandsProtocol):
|
|
|
2588
2558
|
return self.execute_command("BLPOP", *keys)
|
|
2589
2559
|
|
|
2590
2560
|
def brpop(
|
|
2591
|
-
self, keys: List, timeout: Optional[
|
|
2561
|
+
self, keys: List, timeout: Optional[Number] = 0
|
|
2592
2562
|
) -> Union[Awaitable[list], list]:
|
|
2593
2563
|
"""
|
|
2594
2564
|
RPOP a value off of the first non-empty list
|
|
@@ -2609,7 +2579,7 @@ class ListCommands(CommandsProtocol):
|
|
|
2609
2579
|
return self.execute_command("BRPOP", *keys)
|
|
2610
2580
|
|
|
2611
2581
|
def brpoplpush(
|
|
2612
|
-
self, src: str, dst: str, timeout: Optional[
|
|
2582
|
+
self, src: str, dst: str, timeout: Optional[Number] = 0
|
|
2613
2583
|
) -> Union[Awaitable[Optional[str]], Optional[str]]:
|
|
2614
2584
|
"""
|
|
2615
2585
|
Pop a value off the tail of ``src``, push it on the head of ``dst``
|
|
@@ -3413,7 +3383,9 @@ class SetCommands(CommandsProtocol):
|
|
|
3413
3383
|
"""
|
|
3414
3384
|
return self.execute_command("SMEMBERS", name, keys=[name])
|
|
3415
3385
|
|
|
3416
|
-
def smismember(
|
|
3386
|
+
def smismember(
|
|
3387
|
+
self, name: str, values: List, *args: List
|
|
3388
|
+
) -> Union[
|
|
3417
3389
|
Awaitable[List[Union[Literal[0], Literal[1]]]],
|
|
3418
3390
|
List[Union[Literal[0], Literal[1]]],
|
|
3419
3391
|
]:
|
|
@@ -4160,8 +4132,7 @@ class SortedSetCommands(CommandsProtocol):
|
|
|
4160
4132
|
raise DataError("ZADD allows either 'gt' or 'lt', not both")
|
|
4161
4133
|
if incr and len(mapping) != 1:
|
|
4162
4134
|
raise DataError(
|
|
4163
|
-
"ZADD option 'incr' only works when passing a "
|
|
4164
|
-
"single element/score pair"
|
|
4135
|
+
"ZADD option 'incr' only works when passing a single element/score pair"
|
|
4165
4136
|
)
|
|
4166
4137
|
if nx and (gt or lt):
|
|
4167
4138
|
raise DataError("Only one of 'nx', 'lt', or 'gr' may be defined.")
|
|
@@ -4937,6 +4908,16 @@ class HyperlogCommands(CommandsProtocol):
|
|
|
4937
4908
|
AsyncHyperlogCommands = HyperlogCommands
|
|
4938
4909
|
|
|
4939
4910
|
|
|
4911
|
+
class HashDataPersistOptions(Enum):
|
|
4912
|
+
# set the value for each provided key to each
|
|
4913
|
+
# provided value only if all do not already exist.
|
|
4914
|
+
FNX = "FNX"
|
|
4915
|
+
|
|
4916
|
+
# set the value for each provided key to each
|
|
4917
|
+
# provided value only if all already exist.
|
|
4918
|
+
FXX = "FXX"
|
|
4919
|
+
|
|
4920
|
+
|
|
4940
4921
|
class HashCommands(CommandsProtocol):
|
|
4941
4922
|
"""
|
|
4942
4923
|
Redis commands for Hash data type.
|
|
@@ -4977,6 +4958,80 @@ class HashCommands(CommandsProtocol):
|
|
|
4977
4958
|
"""
|
|
4978
4959
|
return self.execute_command("HGETALL", name, keys=[name])
|
|
4979
4960
|
|
|
4961
|
+
def hgetdel(
|
|
4962
|
+
self, name: str, *keys: str
|
|
4963
|
+
) -> Union[
|
|
4964
|
+
Awaitable[Optional[List[Union[str, bytes]]]], Optional[List[Union[str, bytes]]]
|
|
4965
|
+
]:
|
|
4966
|
+
"""
|
|
4967
|
+
Return the value of ``key`` within the hash ``name`` and
|
|
4968
|
+
delete the field in the hash.
|
|
4969
|
+
This command is similar to HGET, except for the fact that it also deletes
|
|
4970
|
+
the key on success from the hash with the provided ```name```.
|
|
4971
|
+
|
|
4972
|
+
Available since Redis 8.0
|
|
4973
|
+
For more information see https://redis.io/commands/hgetdel
|
|
4974
|
+
"""
|
|
4975
|
+
if len(keys) == 0:
|
|
4976
|
+
raise DataError("'hgetdel' should have at least one key provided")
|
|
4977
|
+
|
|
4978
|
+
return self.execute_command("HGETDEL", name, "FIELDS", len(keys), *keys)
|
|
4979
|
+
|
|
4980
|
+
def hgetex(
|
|
4981
|
+
self,
|
|
4982
|
+
name: KeyT,
|
|
4983
|
+
*keys: str,
|
|
4984
|
+
ex: Optional[ExpiryT] = None,
|
|
4985
|
+
px: Optional[ExpiryT] = None,
|
|
4986
|
+
exat: Optional[AbsExpiryT] = None,
|
|
4987
|
+
pxat: Optional[AbsExpiryT] = None,
|
|
4988
|
+
persist: bool = False,
|
|
4989
|
+
) -> Union[
|
|
4990
|
+
Awaitable[Optional[List[Union[str, bytes]]]], Optional[List[Union[str, bytes]]]
|
|
4991
|
+
]:
|
|
4992
|
+
"""
|
|
4993
|
+
Return the values of ``key`` and ``keys`` within the hash ``name``
|
|
4994
|
+
and optionally set their expiration.
|
|
4995
|
+
|
|
4996
|
+
``ex`` sets an expire flag on ``kyes`` for ``ex`` seconds.
|
|
4997
|
+
|
|
4998
|
+
``px`` sets an expire flag on ``keys`` for ``px`` milliseconds.
|
|
4999
|
+
|
|
5000
|
+
``exat`` sets an expire flag on ``keys`` for ``ex`` seconds,
|
|
5001
|
+
specified in unix time.
|
|
5002
|
+
|
|
5003
|
+
``pxat`` sets an expire flag on ``keys`` for ``ex`` milliseconds,
|
|
5004
|
+
specified in unix time.
|
|
5005
|
+
|
|
5006
|
+
``persist`` remove the time to live associated with the ``keys``.
|
|
5007
|
+
|
|
5008
|
+
Available since Redis 8.0
|
|
5009
|
+
For more information see https://redis.io/commands/hgetex
|
|
5010
|
+
"""
|
|
5011
|
+
if not keys:
|
|
5012
|
+
raise DataError("'hgetex' should have at least one key provided")
|
|
5013
|
+
|
|
5014
|
+
opset = {ex, px, exat, pxat}
|
|
5015
|
+
if len(opset) > 2 or len(opset) > 1 and persist:
|
|
5016
|
+
raise DataError(
|
|
5017
|
+
"``ex``, ``px``, ``exat``, ``pxat``, "
|
|
5018
|
+
"and ``persist`` are mutually exclusive."
|
|
5019
|
+
)
|
|
5020
|
+
|
|
5021
|
+
exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
|
|
5022
|
+
|
|
5023
|
+
if persist:
|
|
5024
|
+
exp_options.append("PERSIST")
|
|
5025
|
+
|
|
5026
|
+
return self.execute_command(
|
|
5027
|
+
"HGETEX",
|
|
5028
|
+
name,
|
|
5029
|
+
*exp_options,
|
|
5030
|
+
"FIELDS",
|
|
5031
|
+
len(keys),
|
|
5032
|
+
*keys,
|
|
5033
|
+
)
|
|
5034
|
+
|
|
4980
5035
|
def hincrby(
|
|
4981
5036
|
self, name: str, key: str, amount: int = 1
|
|
4982
5037
|
) -> Union[Awaitable[int], int]:
|
|
@@ -5031,8 +5086,10 @@ class HashCommands(CommandsProtocol):
|
|
|
5031
5086
|
|
|
5032
5087
|
For more information see https://redis.io/commands/hset
|
|
5033
5088
|
"""
|
|
5089
|
+
|
|
5034
5090
|
if key is None and not mapping and not items:
|
|
5035
5091
|
raise DataError("'hset' with no key value pairs")
|
|
5092
|
+
|
|
5036
5093
|
pieces = []
|
|
5037
5094
|
if items:
|
|
5038
5095
|
pieces.extend(items)
|
|
@@ -5044,6 +5101,89 @@ class HashCommands(CommandsProtocol):
|
|
|
5044
5101
|
|
|
5045
5102
|
return self.execute_command("HSET", name, *pieces)
|
|
5046
5103
|
|
|
5104
|
+
def hsetex(
|
|
5105
|
+
self,
|
|
5106
|
+
name: str,
|
|
5107
|
+
key: Optional[str] = None,
|
|
5108
|
+
value: Optional[str] = None,
|
|
5109
|
+
mapping: Optional[dict] = None,
|
|
5110
|
+
items: Optional[list] = None,
|
|
5111
|
+
ex: Optional[ExpiryT] = None,
|
|
5112
|
+
px: Optional[ExpiryT] = None,
|
|
5113
|
+
exat: Optional[AbsExpiryT] = None,
|
|
5114
|
+
pxat: Optional[AbsExpiryT] = None,
|
|
5115
|
+
data_persist_option: Optional[HashDataPersistOptions] = None,
|
|
5116
|
+
keepttl: bool = False,
|
|
5117
|
+
) -> Union[Awaitable[int], int]:
|
|
5118
|
+
"""
|
|
5119
|
+
Set ``key`` to ``value`` within hash ``name``
|
|
5120
|
+
|
|
5121
|
+
``mapping`` accepts a dict of key/value pairs that will be
|
|
5122
|
+
added to hash ``name``.
|
|
5123
|
+
|
|
5124
|
+
``items`` accepts a list of key/value pairs that will be
|
|
5125
|
+
added to hash ``name``.
|
|
5126
|
+
|
|
5127
|
+
``ex`` sets an expire flag on ``keys`` for ``ex`` seconds.
|
|
5128
|
+
|
|
5129
|
+
``px`` sets an expire flag on ``keys`` for ``px`` milliseconds.
|
|
5130
|
+
|
|
5131
|
+
``exat`` sets an expire flag on ``keys`` for ``ex`` seconds,
|
|
5132
|
+
specified in unix time.
|
|
5133
|
+
|
|
5134
|
+
``pxat`` sets an expire flag on ``keys`` for ``ex`` milliseconds,
|
|
5135
|
+
specified in unix time.
|
|
5136
|
+
|
|
5137
|
+
``data_persist_option`` can be set to ``FNX`` or ``FXX`` to control the
|
|
5138
|
+
behavior of the command.
|
|
5139
|
+
``FNX`` will set the value for each provided key to each
|
|
5140
|
+
provided value only if all do not already exist.
|
|
5141
|
+
``FXX`` will set the value for each provided key to each
|
|
5142
|
+
provided value only if all already exist.
|
|
5143
|
+
|
|
5144
|
+
``keepttl`` if True, retain the time to live associated with the keys.
|
|
5145
|
+
|
|
5146
|
+
Returns the number of fields that were added.
|
|
5147
|
+
|
|
5148
|
+
Available since Redis 8.0
|
|
5149
|
+
For more information see https://redis.io/commands/hsetex
|
|
5150
|
+
"""
|
|
5151
|
+
if key is None and not mapping and not items:
|
|
5152
|
+
raise DataError("'hsetex' with no key value pairs")
|
|
5153
|
+
|
|
5154
|
+
if items and len(items) % 2 != 0:
|
|
5155
|
+
raise DataError(
|
|
5156
|
+
"'hsetex' with odd number of items. "
|
|
5157
|
+
"'items' must contain a list of key/value pairs."
|
|
5158
|
+
)
|
|
5159
|
+
|
|
5160
|
+
opset = {ex, px, exat, pxat}
|
|
5161
|
+
if len(opset) > 2 or len(opset) > 1 and keepttl:
|
|
5162
|
+
raise DataError(
|
|
5163
|
+
"``ex``, ``px``, ``exat``, ``pxat``, "
|
|
5164
|
+
"and ``keepttl`` are mutually exclusive."
|
|
5165
|
+
)
|
|
5166
|
+
|
|
5167
|
+
exp_options: list[EncodableT] = extract_expire_flags(ex, px, exat, pxat)
|
|
5168
|
+
if data_persist_option:
|
|
5169
|
+
exp_options.append(data_persist_option.value)
|
|
5170
|
+
|
|
5171
|
+
if keepttl:
|
|
5172
|
+
exp_options.append("KEEPTTL")
|
|
5173
|
+
|
|
5174
|
+
pieces = []
|
|
5175
|
+
if items:
|
|
5176
|
+
pieces.extend(items)
|
|
5177
|
+
if key is not None:
|
|
5178
|
+
pieces.extend((key, value))
|
|
5179
|
+
if mapping:
|
|
5180
|
+
for pair in mapping.items():
|
|
5181
|
+
pieces.extend(pair)
|
|
5182
|
+
|
|
5183
|
+
return self.execute_command(
|
|
5184
|
+
"HSETEX", name, *exp_options, "FIELDS", int(len(pieces) / 2), *pieces
|
|
5185
|
+
)
|
|
5186
|
+
|
|
5047
5187
|
def hsetnx(self, name: str, key: str, value: str) -> Union[Awaitable[bool], bool]:
|
|
5048
5188
|
"""
|
|
5049
5189
|
Set ``key`` to ``value`` within hash ``name`` if ``key`` does not
|
|
@@ -5053,6 +5193,11 @@ class HashCommands(CommandsProtocol):
|
|
|
5053
5193
|
"""
|
|
5054
5194
|
return self.execute_command("HSETNX", name, key, value)
|
|
5055
5195
|
|
|
5196
|
+
@deprecated_function(
|
|
5197
|
+
version="4.0.0",
|
|
5198
|
+
reason="Use 'hset' instead.",
|
|
5199
|
+
name="hmset",
|
|
5200
|
+
)
|
|
5056
5201
|
def hmset(self, name: str, mapping: dict) -> Union[Awaitable[str], str]:
|
|
5057
5202
|
"""
|
|
5058
5203
|
Set key to value within hash ``name`` for each corresponding
|
|
@@ -5060,12 +5205,6 @@ class HashCommands(CommandsProtocol):
|
|
|
5060
5205
|
|
|
5061
5206
|
For more information see https://redis.io/commands/hmset
|
|
5062
5207
|
"""
|
|
5063
|
-
warnings.warn(
|
|
5064
|
-
f"{self.__class__.__name__}.hmset() is deprecated. "
|
|
5065
|
-
f"Use {self.__class__.__name__}.hset() instead.",
|
|
5066
|
-
DeprecationWarning,
|
|
5067
|
-
stacklevel=2,
|
|
5068
|
-
)
|
|
5069
5208
|
if not mapping:
|
|
5070
5209
|
raise DataError("'hmset' with 'mapping' of length 0")
|
|
5071
5210
|
items = []
|
|
@@ -6396,12 +6535,12 @@ class FunctionCommands:
|
|
|
6396
6535
|
return self.execute_command("FUNCTION LIST", *args)
|
|
6397
6536
|
|
|
6398
6537
|
def _fcall(
|
|
6399
|
-
self, command: str, function, numkeys: int, *keys_and_args:
|
|
6538
|
+
self, command: str, function, numkeys: int, *keys_and_args: Any
|
|
6400
6539
|
) -> Union[Awaitable[str], str]:
|
|
6401
6540
|
return self.execute_command(command, function, numkeys, *keys_and_args)
|
|
6402
6541
|
|
|
6403
6542
|
def fcall(
|
|
6404
|
-
self, function, numkeys: int, *keys_and_args:
|
|
6543
|
+
self, function, numkeys: int, *keys_and_args: Any
|
|
6405
6544
|
) -> Union[Awaitable[str], str]:
|
|
6406
6545
|
"""
|
|
6407
6546
|
Invoke a function.
|
|
@@ -6411,7 +6550,7 @@ class FunctionCommands:
|
|
|
6411
6550
|
return self._fcall("FCALL", function, numkeys, *keys_and_args)
|
|
6412
6551
|
|
|
6413
6552
|
def fcall_ro(
|
|
6414
|
-
self, function, numkeys: int, *keys_and_args:
|
|
6553
|
+
self, function, numkeys: int, *keys_and_args: Any
|
|
6415
6554
|
) -> Union[Awaitable[str], str]:
|
|
6416
6555
|
"""
|
|
6417
6556
|
This is a read-only variant of the FCALL command that cannot
|
|
@@ -6467,131 +6606,6 @@ class FunctionCommands:
|
|
|
6467
6606
|
AsyncFunctionCommands = FunctionCommands
|
|
6468
6607
|
|
|
6469
6608
|
|
|
6470
|
-
class GearsCommands:
|
|
6471
|
-
def tfunction_load(
|
|
6472
|
-
self, lib_code: str, replace: bool = False, config: Union[str, None] = None
|
|
6473
|
-
) -> ResponseT:
|
|
6474
|
-
"""
|
|
6475
|
-
Load a new library to RedisGears.
|
|
6476
|
-
|
|
6477
|
-
``lib_code`` - the library code.
|
|
6478
|
-
``config`` - a string representation of a JSON object
|
|
6479
|
-
that will be provided to the library on load time,
|
|
6480
|
-
for more information refer to
|
|
6481
|
-
https://github.com/RedisGears/RedisGears/blob/master/docs/function_advance_topics.md#library-configuration
|
|
6482
|
-
``replace`` - an optional argument, instructs RedisGears to replace the
|
|
6483
|
-
function if its already exists
|
|
6484
|
-
|
|
6485
|
-
For more information see https://redis.io/commands/tfunction-load/
|
|
6486
|
-
"""
|
|
6487
|
-
pieces = []
|
|
6488
|
-
if replace:
|
|
6489
|
-
pieces.append("REPLACE")
|
|
6490
|
-
if config is not None:
|
|
6491
|
-
pieces.extend(["CONFIG", config])
|
|
6492
|
-
pieces.append(lib_code)
|
|
6493
|
-
return self.execute_command("TFUNCTION LOAD", *pieces)
|
|
6494
|
-
|
|
6495
|
-
def tfunction_delete(self, lib_name: str) -> ResponseT:
|
|
6496
|
-
"""
|
|
6497
|
-
Delete a library from RedisGears.
|
|
6498
|
-
|
|
6499
|
-
``lib_name`` the library name to delete.
|
|
6500
|
-
|
|
6501
|
-
For more information see https://redis.io/commands/tfunction-delete/
|
|
6502
|
-
"""
|
|
6503
|
-
return self.execute_command("TFUNCTION DELETE", lib_name)
|
|
6504
|
-
|
|
6505
|
-
def tfunction_list(
|
|
6506
|
-
self,
|
|
6507
|
-
with_code: bool = False,
|
|
6508
|
-
verbose: int = 0,
|
|
6509
|
-
lib_name: Union[str, None] = None,
|
|
6510
|
-
) -> ResponseT:
|
|
6511
|
-
"""
|
|
6512
|
-
List the functions with additional information about each function.
|
|
6513
|
-
|
|
6514
|
-
``with_code`` Show libraries code.
|
|
6515
|
-
``verbose`` output verbosity level, higher number will increase verbosity level
|
|
6516
|
-
``lib_name`` specifying a library name (can be used multiple times to show multiple libraries in a single command) # noqa
|
|
6517
|
-
|
|
6518
|
-
For more information see https://redis.io/commands/tfunction-list/
|
|
6519
|
-
"""
|
|
6520
|
-
pieces = []
|
|
6521
|
-
if with_code:
|
|
6522
|
-
pieces.append("WITHCODE")
|
|
6523
|
-
if verbose >= 1 and verbose <= 3:
|
|
6524
|
-
pieces.append("v" * verbose)
|
|
6525
|
-
else:
|
|
6526
|
-
raise DataError("verbose can be 1, 2 or 3")
|
|
6527
|
-
if lib_name is not None:
|
|
6528
|
-
pieces.append("LIBRARY")
|
|
6529
|
-
pieces.append(lib_name)
|
|
6530
|
-
|
|
6531
|
-
return self.execute_command("TFUNCTION LIST", *pieces)
|
|
6532
|
-
|
|
6533
|
-
def _tfcall(
|
|
6534
|
-
self,
|
|
6535
|
-
lib_name: str,
|
|
6536
|
-
func_name: str,
|
|
6537
|
-
keys: KeysT = None,
|
|
6538
|
-
_async: bool = False,
|
|
6539
|
-
*args: List,
|
|
6540
|
-
) -> ResponseT:
|
|
6541
|
-
pieces = [f"{lib_name}.{func_name}"]
|
|
6542
|
-
if keys is not None:
|
|
6543
|
-
pieces.append(len(keys))
|
|
6544
|
-
pieces.extend(keys)
|
|
6545
|
-
else:
|
|
6546
|
-
pieces.append(0)
|
|
6547
|
-
if args is not None:
|
|
6548
|
-
pieces.extend(args)
|
|
6549
|
-
if _async:
|
|
6550
|
-
return self.execute_command("TFCALLASYNC", *pieces)
|
|
6551
|
-
return self.execute_command("TFCALL", *pieces)
|
|
6552
|
-
|
|
6553
|
-
def tfcall(
|
|
6554
|
-
self,
|
|
6555
|
-
lib_name: str,
|
|
6556
|
-
func_name: str,
|
|
6557
|
-
keys: KeysT = None,
|
|
6558
|
-
*args: List,
|
|
6559
|
-
) -> ResponseT:
|
|
6560
|
-
"""
|
|
6561
|
-
Invoke a function.
|
|
6562
|
-
|
|
6563
|
-
``lib_name`` - the library name contains the function.
|
|
6564
|
-
``func_name`` - the function name to run.
|
|
6565
|
-
``keys`` - the keys that will be touched by the function.
|
|
6566
|
-
``args`` - Additional argument to pass to the function.
|
|
6567
|
-
|
|
6568
|
-
For more information see https://redis.io/commands/tfcall/
|
|
6569
|
-
"""
|
|
6570
|
-
return self._tfcall(lib_name, func_name, keys, False, *args)
|
|
6571
|
-
|
|
6572
|
-
def tfcall_async(
|
|
6573
|
-
self,
|
|
6574
|
-
lib_name: str,
|
|
6575
|
-
func_name: str,
|
|
6576
|
-
keys: KeysT = None,
|
|
6577
|
-
*args: List,
|
|
6578
|
-
) -> ResponseT:
|
|
6579
|
-
"""
|
|
6580
|
-
Invoke an async function (coroutine).
|
|
6581
|
-
|
|
6582
|
-
``lib_name`` - the library name contains the function.
|
|
6583
|
-
``func_name`` - the function name to run.
|
|
6584
|
-
``keys`` - the keys that will be touched by the function.
|
|
6585
|
-
``args`` - Additional argument to pass to the function.
|
|
6586
|
-
|
|
6587
|
-
For more information see https://redis.io/commands/tfcall/
|
|
6588
|
-
"""
|
|
6589
|
-
return self._tfcall(lib_name, func_name, keys, True, *args)
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
AsyncGearsCommands = GearsCommands
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
6609
|
class DataAccessCommands(
|
|
6596
6610
|
BasicKeyCommands,
|
|
6597
6611
|
HyperlogCommands,
|
|
@@ -6635,7 +6649,6 @@ class CoreCommands(
|
|
|
6635
6649
|
PubSubCommands,
|
|
6636
6650
|
ScriptCommands,
|
|
6637
6651
|
FunctionCommands,
|
|
6638
|
-
GearsCommands,
|
|
6639
6652
|
):
|
|
6640
6653
|
"""
|
|
6641
6654
|
A class containing all of the implemented redis commands. This class is
|
|
@@ -6652,7 +6665,6 @@ class AsyncCoreCommands(
|
|
|
6652
6665
|
AsyncPubSubCommands,
|
|
6653
6666
|
AsyncScriptCommands,
|
|
6654
6667
|
AsyncFunctionCommands,
|
|
6655
|
-
AsyncGearsCommands,
|
|
6656
6668
|
):
|
|
6657
6669
|
"""
|
|
6658
6670
|
A class containing all of the implemented redis commands. This class is
|
redis/commands/helpers.py
CHANGED
|
@@ -79,29 +79,6 @@ def parse_list_to_dict(response):
|
|
|
79
79
|
return res
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def parse_to_dict(response):
|
|
83
|
-
if response is None:
|
|
84
|
-
return {}
|
|
85
|
-
|
|
86
|
-
res = {}
|
|
87
|
-
for det in response:
|
|
88
|
-
if not isinstance(det, list) or not det:
|
|
89
|
-
continue
|
|
90
|
-
if len(det) == 1:
|
|
91
|
-
res[det[0]] = True
|
|
92
|
-
elif isinstance(det[1], list):
|
|
93
|
-
res[det[0]] = parse_list_to_dict(det[1])
|
|
94
|
-
else:
|
|
95
|
-
try: # try to set the attribute. may be provided without value
|
|
96
|
-
try: # try to convert the value to float
|
|
97
|
-
res[det[0]] = float(det[1])
|
|
98
|
-
except (TypeError, ValueError):
|
|
99
|
-
res[det[0]] = det[1]
|
|
100
|
-
except IndexError:
|
|
101
|
-
pass
|
|
102
|
-
return res
|
|
103
|
-
|
|
104
|
-
|
|
105
82
|
def random_string(length=10):
|
|
106
83
|
"""
|
|
107
84
|
Returns a random N character long string.
|
|
@@ -111,26 +88,6 @@ def random_string(length=10):
|
|
|
111
88
|
)
|
|
112
89
|
|
|
113
90
|
|
|
114
|
-
def quote_string(v):
|
|
115
|
-
"""
|
|
116
|
-
RedisGraph strings must be quoted,
|
|
117
|
-
quote_string wraps given v with quotes incase
|
|
118
|
-
v is a string.
|
|
119
|
-
"""
|
|
120
|
-
|
|
121
|
-
if isinstance(v, bytes):
|
|
122
|
-
v = v.decode()
|
|
123
|
-
elif not isinstance(v, str):
|
|
124
|
-
return v
|
|
125
|
-
if len(v) == 0:
|
|
126
|
-
return '""'
|
|
127
|
-
|
|
128
|
-
v = v.replace("\\", "\\\\")
|
|
129
|
-
v = v.replace('"', '\\"')
|
|
130
|
-
|
|
131
|
-
return f'"{v}"'
|
|
132
|
-
|
|
133
|
-
|
|
134
91
|
def decode_dict_keys(obj):
|
|
135
92
|
"""Decode the keys of the given dictionary with utf-8."""
|
|
136
93
|
newobj = copy.copy(obj)
|
|
@@ -141,33 +98,6 @@ def decode_dict_keys(obj):
|
|
|
141
98
|
return newobj
|
|
142
99
|
|
|
143
100
|
|
|
144
|
-
def stringify_param_value(value):
|
|
145
|
-
"""
|
|
146
|
-
Turn a parameter value into a string suitable for the params header of
|
|
147
|
-
a Cypher command.
|
|
148
|
-
You may pass any value that would be accepted by `json.dumps()`.
|
|
149
|
-
|
|
150
|
-
Ways in which output differs from that of `str()`:
|
|
151
|
-
* Strings are quoted.
|
|
152
|
-
* None --> "null".
|
|
153
|
-
* In dictionaries, keys are _not_ quoted.
|
|
154
|
-
|
|
155
|
-
:param value: The parameter value to be turned into a string.
|
|
156
|
-
:return: string
|
|
157
|
-
"""
|
|
158
|
-
|
|
159
|
-
if isinstance(value, str):
|
|
160
|
-
return quote_string(value)
|
|
161
|
-
elif value is None:
|
|
162
|
-
return "null"
|
|
163
|
-
elif isinstance(value, (list, tuple)):
|
|
164
|
-
return f'[{",".join(map(stringify_param_value, value))}]'
|
|
165
|
-
elif isinstance(value, dict):
|
|
166
|
-
return f'{{{",".join(f"{k}:{stringify_param_value(v)}" for k, v in value.items())}}}' # noqa
|
|
167
|
-
else:
|
|
168
|
-
return str(value)
|
|
169
|
-
|
|
170
|
-
|
|
171
101
|
def get_protocol_version(client):
|
|
172
102
|
if isinstance(client, redis.Redis) or isinstance(client, redis.asyncio.Redis):
|
|
173
103
|
return client.connection_pool.connection_kwargs.get("protocol")
|