coredis 4.24.0__py3-none-any.whl → 5.0.0__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.
Potentially problematic release.
This version of coredis might be problematic. Click here for more details.
- coredis/__init__.py +1 -3
- coredis/_packer.py +10 -10
- coredis/_protocols.py +19 -51
- coredis/_py_311_typing.py +20 -0
- coredis/_py_312_typing.py +17 -0
- coredis/_utils.py +49 -55
- coredis/_version.py +3 -3
- coredis/cache.py +57 -82
- coredis/client/__init__.py +1 -2
- coredis/client/basic.py +129 -56
- coredis/client/cluster.py +147 -70
- coredis/commands/__init__.py +27 -7
- coredis/commands/_key_spec.py +11 -10
- coredis/commands/_utils.py +1 -1
- coredis/commands/_validators.py +30 -20
- coredis/commands/_wrappers.py +19 -99
- coredis/commands/bitfield.py +10 -2
- coredis/commands/constants.py +20 -3
- coredis/commands/core.py +1674 -1251
- coredis/commands/function.py +21 -19
- coredis/commands/monitor.py +0 -71
- coredis/commands/pubsub.py +7 -142
- coredis/commands/request.py +108 -0
- coredis/commands/script.py +21 -22
- coredis/commands/sentinel.py +60 -49
- coredis/connection.py +14 -15
- coredis/exceptions.py +2 -2
- coredis/experimental/__init__.py +0 -4
- coredis/globals.py +3 -0
- coredis/modules/autocomplete.py +28 -30
- coredis/modules/base.py +15 -31
- coredis/modules/filters.py +269 -245
- coredis/modules/graph.py +61 -62
- coredis/modules/json.py +172 -140
- coredis/modules/response/_callbacks/autocomplete.py +5 -4
- coredis/modules/response/_callbacks/graph.py +34 -29
- coredis/modules/response/_callbacks/json.py +5 -3
- coredis/modules/response/_callbacks/search.py +49 -53
- coredis/modules/response/_callbacks/timeseries.py +18 -30
- coredis/modules/response/types.py +1 -5
- coredis/modules/search.py +186 -169
- coredis/modules/timeseries.py +184 -164
- coredis/parser.py +6 -19
- coredis/pipeline.py +477 -521
- coredis/pool/basic.py +7 -7
- coredis/pool/cluster.py +3 -3
- coredis/pool/nodemanager.py +10 -3
- coredis/response/_callbacks/__init__.py +76 -57
- coredis/response/_callbacks/acl.py +0 -3
- coredis/response/_callbacks/cluster.py +25 -16
- coredis/response/_callbacks/command.py +8 -6
- coredis/response/_callbacks/connection.py +4 -3
- coredis/response/_callbacks/geo.py +17 -13
- coredis/response/_callbacks/hash.py +13 -11
- coredis/response/_callbacks/keys.py +9 -5
- coredis/response/_callbacks/module.py +2 -3
- coredis/response/_callbacks/script.py +6 -8
- coredis/response/_callbacks/sentinel.py +21 -17
- coredis/response/_callbacks/server.py +36 -14
- coredis/response/_callbacks/sets.py +3 -4
- coredis/response/_callbacks/sorted_set.py +27 -24
- coredis/response/_callbacks/streams.py +22 -13
- coredis/response/_callbacks/strings.py +7 -6
- coredis/response/_callbacks/vector_sets.py +159 -0
- coredis/response/types.py +13 -4
- coredis/retry.py +12 -13
- coredis/sentinel.py +11 -1
- coredis/stream.py +4 -3
- coredis/tokens.py +348 -16
- coredis/typing.py +432 -81
- {coredis-4.24.0.dist-info → coredis-5.0.0.dist-info}/METADATA +4 -9
- coredis-5.0.0.dist-info/RECORD +95 -0
- coredis/client/keydb.py +0 -336
- coredis/pipeline.pyi +0 -2103
- coredis-4.24.0.dist-info/RECORD +0 -93
- {coredis-4.24.0.dist-info → coredis-5.0.0.dist-info}/WHEEL +0 -0
- {coredis-4.24.0.dist-info → coredis-5.0.0.dist-info}/licenses/LICENSE +0 -0
- {coredis-4.24.0.dist-info → coredis-5.0.0.dist-info}/top_level.txt +0 -0
coredis/commands/sentinel.py
CHANGED
|
@@ -15,45 +15,49 @@ from coredis.response._callbacks.sentinel import (
|
|
|
15
15
|
)
|
|
16
16
|
from coredis.typing import (
|
|
17
17
|
AnyStr,
|
|
18
|
+
RedisValueT,
|
|
18
19
|
ResponseType,
|
|
19
20
|
StringT,
|
|
20
|
-
ValueT,
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
from . import CommandMixin
|
|
24
24
|
from ._wrappers import redis_command
|
|
25
25
|
from .constants import CommandName
|
|
26
|
+
from .request import CommandRequest
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class SentinelCommands(CommandMixin[AnyStr]):
|
|
29
30
|
@redis_command(
|
|
30
31
|
CommandName.SENTINEL_CKQUORUM,
|
|
31
32
|
)
|
|
32
|
-
|
|
33
|
-
return
|
|
33
|
+
def sentinel_ckquorum(self, service_name: StringT) -> CommandRequest[bool]:
|
|
34
|
+
return CommandRequest(
|
|
35
|
+
self,
|
|
34
36
|
CommandName.SENTINEL_CKQUORUM,
|
|
35
37
|
service_name,
|
|
36
38
|
callback=SimpleStringCallback(prefix_match=True),
|
|
37
39
|
)
|
|
38
40
|
|
|
39
41
|
@redis_command(CommandName.SENTINEL_CONFIG_GET, version_introduced="6.2.0")
|
|
40
|
-
|
|
42
|
+
def sentinel_config_get(self, name: RedisValueT) -> CommandRequest[dict[AnyStr, AnyStr]]:
|
|
41
43
|
"""
|
|
42
44
|
Get the current value of a global Sentinel configuration parameter.
|
|
43
45
|
The specified name may be a wildcard, similar to :meth:`config_get`
|
|
44
46
|
"""
|
|
45
|
-
return
|
|
47
|
+
return CommandRequest(
|
|
48
|
+
self,
|
|
46
49
|
CommandName.SENTINEL_CONFIG_GET,
|
|
47
50
|
name,
|
|
48
51
|
callback=DictCallback[AnyStr, AnyStr](),
|
|
49
52
|
)
|
|
50
53
|
|
|
51
54
|
@redis_command(CommandName.SENTINEL_CONFIG_SET, version_introduced="6.2")
|
|
52
|
-
|
|
55
|
+
def sentinel_config_set(self, name: RedisValueT, value: RedisValueT) -> CommandRequest[bool]:
|
|
53
56
|
"""
|
|
54
57
|
Set the value of a global Sentinel configuration parameter
|
|
55
58
|
"""
|
|
56
|
-
return
|
|
59
|
+
return CommandRequest(
|
|
60
|
+
self,
|
|
57
61
|
CommandName.SENTINEL_CONFIG_SET,
|
|
58
62
|
name,
|
|
59
63
|
value,
|
|
@@ -63,14 +67,15 @@ class SentinelCommands(CommandMixin[AnyStr]):
|
|
|
63
67
|
@redis_command(
|
|
64
68
|
CommandName.SENTINEL_GET_MASTER_ADDR_BY_NAME,
|
|
65
69
|
)
|
|
66
|
-
|
|
70
|
+
def sentinel_get_master_addr_by_name(
|
|
67
71
|
self, service_name: StringT
|
|
68
|
-
) -> tuple[str, int] | None:
|
|
72
|
+
) -> CommandRequest[tuple[str, int] | None]:
|
|
69
73
|
"""
|
|
70
74
|
Returns a (host, port) pair for the given :paramref:`service_name`
|
|
71
75
|
"""
|
|
72
76
|
|
|
73
|
-
return
|
|
77
|
+
return CommandRequest(
|
|
78
|
+
self,
|
|
74
79
|
CommandName.SENTINEL_GET_MASTER_ADDR_BY_NAME,
|
|
75
80
|
service_name,
|
|
76
81
|
callback=GetPrimaryCallback(),
|
|
@@ -79,32 +84,33 @@ class SentinelCommands(CommandMixin[AnyStr]):
|
|
|
79
84
|
@redis_command(
|
|
80
85
|
CommandName.SENTINEL_FAILOVER,
|
|
81
86
|
)
|
|
82
|
-
|
|
87
|
+
def sentinel_failover(self, service_name: StringT) -> CommandRequest[bool]:
|
|
83
88
|
"""
|
|
84
89
|
Force a failover as if the master was not reachable, and without asking
|
|
85
90
|
for agreement to other Sentinels
|
|
86
91
|
"""
|
|
87
|
-
return
|
|
88
|
-
CommandName.SENTINEL_FAILOVER, service_name, callback=SimpleStringCallback()
|
|
92
|
+
return CommandRequest(
|
|
93
|
+
self, CommandName.SENTINEL_FAILOVER, service_name, callback=SimpleStringCallback()
|
|
89
94
|
)
|
|
90
95
|
|
|
91
96
|
@redis_command(CommandName.SENTINEL_FLUSHCONFIG)
|
|
92
|
-
|
|
97
|
+
def sentinel_flushconfig(self) -> CommandRequest[bool]:
|
|
93
98
|
"""
|
|
94
99
|
Force Sentinel to rewrite its configuration on disk, including the current Sentinel state.
|
|
95
100
|
"""
|
|
96
|
-
return
|
|
97
|
-
CommandName.SENTINEL_FLUSHCONFIG, callback=SimpleStringCallback()
|
|
101
|
+
return CommandRequest(
|
|
102
|
+
self, CommandName.SENTINEL_FLUSHCONFIG, callback=SimpleStringCallback()
|
|
98
103
|
)
|
|
99
104
|
|
|
100
105
|
@redis_command(CommandName.SENTINEL_INFO_CACHE)
|
|
101
|
-
|
|
106
|
+
def sentinel_infocache(
|
|
102
107
|
self, *nodenames: StringT
|
|
103
|
-
) -> dict[AnyStr, dict[int, dict[str, ResponseType]]]:
|
|
108
|
+
) -> CommandRequest[dict[AnyStr, dict[int, dict[str, ResponseType]]]]:
|
|
104
109
|
"""
|
|
105
110
|
Return cached INFO output from masters and replicas.
|
|
106
111
|
"""
|
|
107
|
-
return
|
|
112
|
+
return CommandRequest(
|
|
113
|
+
self,
|
|
108
114
|
CommandName.SENTINEL_INFO_CACHE,
|
|
109
115
|
*nodenames,
|
|
110
116
|
callback=SentinelInfoCallback[AnyStr](),
|
|
@@ -113,30 +119,31 @@ class SentinelCommands(CommandMixin[AnyStr]):
|
|
|
113
119
|
@redis_command(
|
|
114
120
|
CommandName.SENTINEL_MASTER,
|
|
115
121
|
)
|
|
116
|
-
|
|
122
|
+
def sentinel_master(self, service_name: StringT) -> CommandRequest[dict[str, int | bool | str]]:
|
|
117
123
|
"""Returns a dictionary containing the specified masters state."""
|
|
118
124
|
|
|
119
|
-
return
|
|
120
|
-
CommandName.SENTINEL_MASTER, service_name, callback=PrimaryCallback()
|
|
125
|
+
return CommandRequest(
|
|
126
|
+
self, CommandName.SENTINEL_MASTER, service_name, callback=PrimaryCallback()
|
|
121
127
|
)
|
|
122
128
|
|
|
123
129
|
@redis_command(
|
|
124
130
|
CommandName.SENTINEL_MASTERS,
|
|
125
131
|
)
|
|
126
|
-
|
|
132
|
+
def sentinel_masters(self) -> CommandRequest[dict[str, dict[str, int | bool | str]]]:
|
|
127
133
|
"""Returns a list of dictionaries containing each master's state."""
|
|
128
134
|
|
|
129
|
-
return
|
|
130
|
-
CommandName.SENTINEL_MASTERS, callback=PrimariesCallback()
|
|
131
|
-
)
|
|
135
|
+
return CommandRequest(self, CommandName.SENTINEL_MASTERS, callback=PrimariesCallback())
|
|
132
136
|
|
|
133
137
|
@redis_command(
|
|
134
138
|
CommandName.SENTINEL_MONITOR,
|
|
135
139
|
)
|
|
136
|
-
|
|
140
|
+
def sentinel_monitor(
|
|
141
|
+
self, name: RedisValueT, ip: RedisValueT, port: int, quorum: int
|
|
142
|
+
) -> CommandRequest[bool]:
|
|
137
143
|
"""Adds a new master to Sentinel to be monitored"""
|
|
138
144
|
|
|
139
|
-
return
|
|
145
|
+
return CommandRequest(
|
|
146
|
+
self,
|
|
140
147
|
CommandName.SENTINEL_MONITOR,
|
|
141
148
|
name,
|
|
142
149
|
ip,
|
|
@@ -146,32 +153,31 @@ class SentinelCommands(CommandMixin[AnyStr]):
|
|
|
146
153
|
)
|
|
147
154
|
|
|
148
155
|
@redis_command(CommandName.SENTINEL_MYID, version_introduced="6.2.0")
|
|
149
|
-
|
|
156
|
+
def sentinel_myid(self) -> CommandRequest[AnyStr]:
|
|
150
157
|
"""Return the ID of the Sentinel instance"""
|
|
151
158
|
|
|
152
|
-
return
|
|
153
|
-
CommandName.SENTINEL_MYID, callback=AnyStrCallback[AnyStr]()
|
|
154
|
-
)
|
|
159
|
+
return CommandRequest(self, CommandName.SENTINEL_MYID, callback=AnyStrCallback[AnyStr]())
|
|
155
160
|
|
|
156
161
|
@redis_command(
|
|
157
162
|
CommandName.SENTINEL_REMOVE,
|
|
158
163
|
)
|
|
159
|
-
|
|
164
|
+
def sentinel_remove(self, name: RedisValueT) -> CommandRequest[bool]:
|
|
160
165
|
"""Removes a master from Sentinel's monitoring"""
|
|
161
166
|
|
|
162
|
-
return
|
|
163
|
-
CommandName.SENTINEL_REMOVE, name, callback=SimpleStringCallback()
|
|
167
|
+
return CommandRequest(
|
|
168
|
+
self, CommandName.SENTINEL_REMOVE, name, callback=SimpleStringCallback()
|
|
164
169
|
)
|
|
165
170
|
|
|
166
171
|
@redis_command(
|
|
167
172
|
CommandName.SENTINEL_SENTINELS,
|
|
168
173
|
)
|
|
169
|
-
|
|
174
|
+
def sentinel_sentinels(
|
|
170
175
|
self, service_name: StringT
|
|
171
|
-
) -> tuple[dict[str, int | bool | str], ...]:
|
|
176
|
+
) -> CommandRequest[tuple[dict[str, int | bool | str], ...]]:
|
|
172
177
|
"""Returns a list of sentinels for :paramref:`service_name`"""
|
|
173
178
|
|
|
174
|
-
return
|
|
179
|
+
return CommandRequest(
|
|
180
|
+
self,
|
|
175
181
|
CommandName.SENTINEL_SENTINELS,
|
|
176
182
|
service_name,
|
|
177
183
|
callback=SentinelsStateCallback(),
|
|
@@ -180,10 +186,13 @@ class SentinelCommands(CommandMixin[AnyStr]):
|
|
|
180
186
|
@redis_command(
|
|
181
187
|
CommandName.SENTINEL_SET,
|
|
182
188
|
)
|
|
183
|
-
|
|
189
|
+
def sentinel_set(
|
|
190
|
+
self, name: RedisValueT, option: RedisValueT, value: RedisValueT
|
|
191
|
+
) -> CommandRequest[bool]:
|
|
184
192
|
"""Sets Sentinel monitoring parameters for a given master"""
|
|
185
193
|
|
|
186
|
-
return
|
|
194
|
+
return CommandRequest(
|
|
195
|
+
self,
|
|
187
196
|
CommandName.SENTINEL_SET,
|
|
188
197
|
name,
|
|
189
198
|
option,
|
|
@@ -194,31 +203,32 @@ class SentinelCommands(CommandMixin[AnyStr]):
|
|
|
194
203
|
@redis_command(
|
|
195
204
|
CommandName.SENTINEL_SLAVES,
|
|
196
205
|
)
|
|
197
|
-
|
|
206
|
+
def sentinel_slaves(
|
|
198
207
|
self, service_name: StringT
|
|
199
|
-
) -> tuple[dict[str, int | bool | str], ...]:
|
|
208
|
+
) -> CommandRequest[tuple[dict[str, int | bool | str], ...]]:
|
|
200
209
|
"""Returns a list of slaves for paramref:`service_name`"""
|
|
201
210
|
|
|
202
|
-
return
|
|
203
|
-
CommandName.SENTINEL_SLAVES, service_name, callback=SentinelsStateCallback()
|
|
211
|
+
return CommandRequest(
|
|
212
|
+
self, CommandName.SENTINEL_SLAVES, service_name, callback=SentinelsStateCallback()
|
|
204
213
|
)
|
|
205
214
|
|
|
206
215
|
@redis_command(
|
|
207
216
|
CommandName.SENTINEL_REPLICAS,
|
|
208
217
|
)
|
|
209
|
-
|
|
218
|
+
def sentinel_replicas(
|
|
210
219
|
self, service_name: StringT
|
|
211
|
-
) -> tuple[dict[str, int | bool | str], ...]:
|
|
220
|
+
) -> CommandRequest[tuple[dict[str, int | bool | str], ...]]:
|
|
212
221
|
"""Returns a list of replicas for :paramref:`service_name`"""
|
|
213
222
|
|
|
214
|
-
return
|
|
223
|
+
return CommandRequest(
|
|
224
|
+
self,
|
|
215
225
|
CommandName.SENTINEL_REPLICAS,
|
|
216
226
|
service_name,
|
|
217
227
|
callback=SentinelsStateCallback(),
|
|
218
228
|
)
|
|
219
229
|
|
|
220
230
|
@redis_command(CommandName.SENTINEL_RESET)
|
|
221
|
-
|
|
231
|
+
def sentinel_reset(self, pattern: StringT) -> CommandRequest[int]:
|
|
222
232
|
"""
|
|
223
233
|
Reset all the masters with matching name.
|
|
224
234
|
The pattern argument is a glob-style pattern.
|
|
@@ -226,7 +236,8 @@ class SentinelCommands(CommandMixin[AnyStr]):
|
|
|
226
236
|
failover in progress), and removes every replica and sentinel already
|
|
227
237
|
discovered and associated with the master.
|
|
228
238
|
"""
|
|
229
|
-
return
|
|
239
|
+
return CommandRequest(
|
|
240
|
+
self,
|
|
230
241
|
CommandName.SENTINEL_RESET,
|
|
231
242
|
pattern,
|
|
232
243
|
callback=IntCallback(),
|
coredis/connection.py
CHANGED
|
@@ -12,6 +12,7 @@ import time
|
|
|
12
12
|
import warnings
|
|
13
13
|
import weakref
|
|
14
14
|
from collections import defaultdict, deque
|
|
15
|
+
from contextlib import suppress
|
|
15
16
|
from typing import TYPE_CHECKING, Any, cast
|
|
16
17
|
|
|
17
18
|
import async_timeout
|
|
@@ -38,9 +39,9 @@ from coredis.typing import (
|
|
|
38
39
|
Callable,
|
|
39
40
|
ClassVar,
|
|
40
41
|
Literal,
|
|
42
|
+
RedisValueT,
|
|
41
43
|
ResponseType,
|
|
42
44
|
TypeVar,
|
|
43
|
-
ValueT,
|
|
44
45
|
)
|
|
45
46
|
|
|
46
47
|
R = TypeVar("R")
|
|
@@ -78,7 +79,7 @@ class Request:
|
|
|
78
79
|
@dataclasses.dataclass
|
|
79
80
|
class CommandInvocation:
|
|
80
81
|
command: bytes
|
|
81
|
-
args: tuple[
|
|
82
|
+
args: tuple[RedisValueT, ...]
|
|
82
83
|
decode: bool | None
|
|
83
84
|
encoding: str | None
|
|
84
85
|
|
|
@@ -383,7 +384,7 @@ class BaseConnection(asyncio.BaseProtocol):
|
|
|
383
384
|
relay any tracking notifications to.
|
|
384
385
|
"""
|
|
385
386
|
try:
|
|
386
|
-
params: list[
|
|
387
|
+
params: list[RedisValueT] = (
|
|
387
388
|
[b"ON", b"REDIRECT", client_id] if (enabled and client_id is not None) else [b"OFF"]
|
|
388
389
|
)
|
|
389
390
|
|
|
@@ -440,12 +441,12 @@ class BaseConnection(asyncio.BaseProtocol):
|
|
|
440
441
|
)
|
|
441
442
|
assert isinstance(hello_resp, (list, dict))
|
|
442
443
|
if self.protocol_version == 3:
|
|
443
|
-
resp3 = cast(dict[bytes,
|
|
444
|
+
resp3 = cast(dict[bytes, RedisValueT], hello_resp)
|
|
444
445
|
assert resp3[b"proto"] == 3
|
|
445
446
|
self.server_version = nativestr(resp3[b"version"])
|
|
446
447
|
self.client_id = int(resp3[b"id"])
|
|
447
448
|
else:
|
|
448
|
-
resp = cast(list[
|
|
449
|
+
resp = cast(list[RedisValueT], hello_resp)
|
|
449
450
|
self.server_version = nativestr(resp[3])
|
|
450
451
|
self.client_id = int(resp[7])
|
|
451
452
|
if self.server_version >= "7.2":
|
|
@@ -512,7 +513,7 @@ class BaseConnection(asyncio.BaseProtocol):
|
|
|
512
513
|
|
|
513
514
|
async def fetch_push_message(
|
|
514
515
|
self,
|
|
515
|
-
decode:
|
|
516
|
+
decode: RedisValueT | None = None,
|
|
516
517
|
push_message_types: set[bytes] | None = None,
|
|
517
518
|
block: bool | None = False,
|
|
518
519
|
) -> ResponseType:
|
|
@@ -578,7 +579,7 @@ class BaseConnection(asyncio.BaseProtocol):
|
|
|
578
579
|
async def send_command(
|
|
579
580
|
self,
|
|
580
581
|
command: bytes,
|
|
581
|
-
*args:
|
|
582
|
+
*args: RedisValueT,
|
|
582
583
|
) -> None:
|
|
583
584
|
"""
|
|
584
585
|
Send a command to the redis server
|
|
@@ -594,9 +595,9 @@ class BaseConnection(asyncio.BaseProtocol):
|
|
|
594
595
|
async def create_request(
|
|
595
596
|
self,
|
|
596
597
|
command: bytes,
|
|
597
|
-
*args:
|
|
598
|
+
*args: RedisValueT,
|
|
598
599
|
noreply: bool | None = None,
|
|
599
|
-
decode:
|
|
600
|
+
decode: RedisValueT | None = None,
|
|
600
601
|
encoding: str | None = None,
|
|
601
602
|
raise_exceptions: bool = True,
|
|
602
603
|
timeout: float | None = None,
|
|
@@ -690,17 +691,15 @@ class BaseConnection(asyncio.BaseProtocol):
|
|
|
690
691
|
self.noreply_set = False
|
|
691
692
|
self._parser.on_disconnect()
|
|
692
693
|
if self._transport:
|
|
693
|
-
|
|
694
|
+
with suppress(RuntimeError):
|
|
694
695
|
self._transport.close()
|
|
695
|
-
# Raised if event loop is already closed.
|
|
696
|
-
except RuntimeError: # noqa
|
|
697
|
-
pass
|
|
698
696
|
|
|
699
697
|
disconnect_exc = self._last_error or ConnectionError("connection lost")
|
|
700
698
|
while self._read_waiters:
|
|
701
699
|
waiter = self._read_waiters.pop()
|
|
702
700
|
if not waiter.done():
|
|
703
|
-
|
|
701
|
+
with suppress(RuntimeError):
|
|
702
|
+
waiter.cancel()
|
|
704
703
|
while True:
|
|
705
704
|
try:
|
|
706
705
|
request = self._requests.popleft()
|
|
@@ -818,7 +817,7 @@ class UnixDomainSocketConnection(BaseConnection):
|
|
|
818
817
|
*,
|
|
819
818
|
client_name: str | None = None,
|
|
820
819
|
protocol_version: Literal[2, 3] = 3,
|
|
821
|
-
**_:
|
|
820
|
+
**_: RedisValueT,
|
|
822
821
|
) -> None:
|
|
823
822
|
super().__init__(
|
|
824
823
|
stream_timeout,
|
coredis/exceptions.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
from coredis.typing import
|
|
5
|
+
from coredis.typing import RedisValueT
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class RedisError(Exception):
|
|
@@ -184,7 +184,7 @@ class ClusterCrossSlotError(ResponseError):
|
|
|
184
184
|
self,
|
|
185
185
|
message: str | None = None,
|
|
186
186
|
command: bytes | None = None,
|
|
187
|
-
keys: tuple[
|
|
187
|
+
keys: tuple[RedisValueT, ...] | None = None,
|
|
188
188
|
) -> None:
|
|
189
189
|
super().__init__(message or "Keys in request don't hash to the same slot")
|
|
190
190
|
self.command = command
|
coredis/experimental/__init__.py
CHANGED
coredis/globals.py
CHANGED
|
@@ -13,6 +13,9 @@ READONLY_COMMANDS: set[bytes] = set()
|
|
|
13
13
|
#: Populated by the @redis_command wrapper
|
|
14
14
|
COMMAND_FLAGS: dict[bytes, set[CommandFlag]] = defaultdict(set)
|
|
15
15
|
|
|
16
|
+
#: Populated by the @redis_command wrapper
|
|
17
|
+
CACHEABLE_COMMANDS: set[bytes] = set()
|
|
18
|
+
|
|
16
19
|
#: Populated by ModuleGroupRegistry
|
|
17
20
|
MODULE_GROUPS: set[ModuleGroupRegistry] = set()
|
|
18
21
|
|
coredis/modules/autocomplete.py
CHANGED
|
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from deprecated.sphinx import versionadded
|
|
4
4
|
|
|
5
|
-
from ..commands._wrappers import CacheConfig
|
|
6
5
|
from ..commands.constants import CommandFlag, CommandGroup, CommandName
|
|
6
|
+
from ..commands.request import CommandRequest
|
|
7
7
|
from ..response._callbacks import BoolCallback, IntCallback
|
|
8
8
|
from ..tokens import PrefixToken, PureToken
|
|
9
9
|
from ..typing import AnyStr, CommandArgList, KeyT, StringT
|
|
@@ -24,14 +24,14 @@ class Autocomplete(ModuleGroup[AnyStr]):
|
|
|
24
24
|
version_introduced="1.0.0",
|
|
25
25
|
group=COMMAND_GROUP,
|
|
26
26
|
)
|
|
27
|
-
|
|
27
|
+
def sugadd(
|
|
28
28
|
self,
|
|
29
29
|
key: KeyT,
|
|
30
30
|
string: StringT,
|
|
31
31
|
score: int | float,
|
|
32
32
|
increment_score: bool | None = None,
|
|
33
33
|
payload: StringT | None = None,
|
|
34
|
-
) -> int:
|
|
34
|
+
) -> CommandRequest[int]:
|
|
35
35
|
"""
|
|
36
36
|
Adds a suggestion string to an auto-complete suggestion dictionary
|
|
37
37
|
|
|
@@ -43,14 +43,14 @@ class Autocomplete(ModuleGroup[AnyStr]):
|
|
|
43
43
|
:param payload: Saves an extra payload with the suggestion, that can be
|
|
44
44
|
fetched when calling :meth:`sugget` by using :paramref:`sugget.withpayloads`
|
|
45
45
|
"""
|
|
46
|
-
|
|
46
|
+
command_arguments: CommandArgList = [key, string, score]
|
|
47
47
|
if increment_score:
|
|
48
|
-
|
|
48
|
+
command_arguments.append(PureToken.INCREMENT)
|
|
49
49
|
if payload:
|
|
50
|
-
|
|
50
|
+
command_arguments.extend([PrefixToken.PAYLOAD, payload])
|
|
51
51
|
|
|
52
|
-
return
|
|
53
|
-
CommandName.FT_SUGADD, *
|
|
52
|
+
return self.client.create_request(
|
|
53
|
+
CommandName.FT_SUGADD, *command_arguments, callback=IntCallback()
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
@module_command(
|
|
@@ -58,10 +58,10 @@ class Autocomplete(ModuleGroup[AnyStr]):
|
|
|
58
58
|
module=MODULE,
|
|
59
59
|
version_introduced="1.0.0",
|
|
60
60
|
group=COMMAND_GROUP,
|
|
61
|
-
|
|
61
|
+
cacheable=True,
|
|
62
62
|
flags={CommandFlag.READONLY},
|
|
63
63
|
)
|
|
64
|
-
|
|
64
|
+
def sugget(
|
|
65
65
|
self,
|
|
66
66
|
key: KeyT,
|
|
67
67
|
prefix: StringT,
|
|
@@ -70,7 +70,7 @@ class Autocomplete(ModuleGroup[AnyStr]):
|
|
|
70
70
|
withscores: bool | None = None,
|
|
71
71
|
withpayloads: bool | None = None,
|
|
72
72
|
max_suggestions: int | None = None,
|
|
73
|
-
) -> tuple[AutocompleteSuggestion[AnyStr], ...] | tuple[()]:
|
|
73
|
+
) -> CommandRequest[tuple[AutocompleteSuggestion[AnyStr], ...] | tuple[()]]:
|
|
74
74
|
"""
|
|
75
75
|
Gets completion suggestions for a prefix
|
|
76
76
|
|
|
@@ -82,23 +82,21 @@ class Autocomplete(ModuleGroup[AnyStr]):
|
|
|
82
82
|
:param withpayloads: If True, returns optional payloads saved along with the suggestions.
|
|
83
83
|
:param max_suggestions: Limits the results to a maximum of ``max_suggestions``
|
|
84
84
|
"""
|
|
85
|
-
|
|
85
|
+
command_arguments: CommandArgList = [key, prefix]
|
|
86
86
|
if fuzzy:
|
|
87
|
-
|
|
87
|
+
command_arguments.append(PureToken.FUZZY)
|
|
88
88
|
if withscores:
|
|
89
|
-
|
|
89
|
+
command_arguments.append(PureToken.WITHSCORES)
|
|
90
90
|
if withpayloads:
|
|
91
|
-
|
|
91
|
+
command_arguments.append(PureToken.WITHPAYLOADS)
|
|
92
92
|
if max_suggestions is not None:
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
command_arguments.append(PureToken.MAX)
|
|
94
|
+
command_arguments.append(max_suggestions)
|
|
95
95
|
|
|
96
|
-
return
|
|
96
|
+
return self.client.create_request(
|
|
97
97
|
CommandName.FT_SUGGET,
|
|
98
|
-
*
|
|
99
|
-
callback=AutocompleteCallback[AnyStr](),
|
|
100
|
-
withscores=withscores,
|
|
101
|
-
withpayloads=withpayloads,
|
|
98
|
+
*command_arguments,
|
|
99
|
+
callback=AutocompleteCallback[AnyStr](withscores=withscores, withpayloads=withpayloads),
|
|
102
100
|
)
|
|
103
101
|
|
|
104
102
|
@module_command(
|
|
@@ -107,7 +105,7 @@ class Autocomplete(ModuleGroup[AnyStr]):
|
|
|
107
105
|
version_introduced="1.0.0",
|
|
108
106
|
group=COMMAND_GROUP,
|
|
109
107
|
)
|
|
110
|
-
|
|
108
|
+
def sugdel(self, key: KeyT, string: StringT) -> CommandRequest[bool]:
|
|
111
109
|
"""
|
|
112
110
|
Deletes a string from a suggestion index
|
|
113
111
|
|
|
@@ -115,10 +113,10 @@ class Autocomplete(ModuleGroup[AnyStr]):
|
|
|
115
113
|
:param string: The suggestion string to index.
|
|
116
114
|
|
|
117
115
|
"""
|
|
118
|
-
|
|
116
|
+
command_arguments: CommandArgList = [key, string]
|
|
119
117
|
|
|
120
|
-
return
|
|
121
|
-
CommandName.FT_SUGDEL, *
|
|
118
|
+
return self.client.create_request(
|
|
119
|
+
CommandName.FT_SUGDEL, *command_arguments, callback=BoolCallback()
|
|
122
120
|
)
|
|
123
121
|
|
|
124
122
|
@module_command(
|
|
@@ -127,14 +125,14 @@ class Autocomplete(ModuleGroup[AnyStr]):
|
|
|
127
125
|
version_introduced="1.0.0",
|
|
128
126
|
group=COMMAND_GROUP,
|
|
129
127
|
)
|
|
130
|
-
|
|
128
|
+
def suglen(self, key: KeyT) -> CommandRequest[int]:
|
|
131
129
|
"""
|
|
132
130
|
Gets the size of an auto-complete suggestion dictionary
|
|
133
131
|
|
|
134
132
|
:param key: The key of the suggestion dictionary.
|
|
135
133
|
"""
|
|
136
|
-
|
|
134
|
+
command_arguments: CommandArgList = [key]
|
|
137
135
|
|
|
138
|
-
return
|
|
139
|
-
CommandName.FT_SUGLEN, *
|
|
136
|
+
return self.client.create_request(
|
|
137
|
+
CommandName.FT_SUGLEN, *command_arguments, callback=IntCallback()
|
|
140
138
|
)
|
coredis/modules/base.py
CHANGED
|
@@ -12,24 +12,20 @@ from coredis.config import Config
|
|
|
12
12
|
from .._protocols import AbstractExecutor
|
|
13
13
|
from ..commands._utils import redis_command_link
|
|
14
14
|
from ..commands._wrappers import (
|
|
15
|
-
CacheConfig,
|
|
16
15
|
ClusterCommandConfig,
|
|
17
|
-
CommandCache,
|
|
18
16
|
CommandDetails,
|
|
19
17
|
)
|
|
20
18
|
from ..commands.constants import CommandFlag, CommandGroup, CommandName
|
|
19
|
+
from ..commands.request import CommandRequest
|
|
21
20
|
from ..exceptions import CommandSyntaxError, ModuleCommandNotSupportedError
|
|
22
|
-
from ..globals import COMMAND_FLAGS, MODULE_GROUPS, MODULES, READONLY_COMMANDS
|
|
23
|
-
from ..response._callbacks import NoopCallback
|
|
21
|
+
from ..globals import CACHEABLE_COMMANDS, COMMAND_FLAGS, MODULE_GROUPS, MODULES, READONLY_COMMANDS
|
|
24
22
|
from ..typing import (
|
|
25
23
|
AnyStr,
|
|
26
24
|
Callable,
|
|
27
25
|
ClassVar,
|
|
28
|
-
Coroutine,
|
|
29
26
|
Generic,
|
|
30
27
|
P,
|
|
31
28
|
R,
|
|
32
|
-
ValueT,
|
|
33
29
|
add_runtime_checks,
|
|
34
30
|
)
|
|
35
31
|
|
|
@@ -37,7 +33,7 @@ if TYPE_CHECKING:
|
|
|
37
33
|
import coredis.client
|
|
38
34
|
|
|
39
35
|
|
|
40
|
-
|
|
36
|
+
def ensure_compatibility(
|
|
41
37
|
client: coredis.client.Client[Any],
|
|
42
38
|
module: str,
|
|
43
39
|
command_details: CommandDetails,
|
|
@@ -47,11 +43,12 @@ async def ensure_compatibility(
|
|
|
47
43
|
Config.optimized
|
|
48
44
|
or not command_details.version_introduced
|
|
49
45
|
or not getattr(client, "verify_version", False)
|
|
46
|
+
or not getattr(client, "server_version", None)
|
|
50
47
|
or getattr(client, "noreply", False)
|
|
51
48
|
):
|
|
52
49
|
return
|
|
53
50
|
if command_details.version_introduced:
|
|
54
|
-
module_version =
|
|
51
|
+
module_version = client.get_server_module_version(module)
|
|
55
52
|
if module_version and command_details.version_introduced <= module_version:
|
|
56
53
|
if command_details.arguments and set(command_details.arguments.keys()).intersection(
|
|
57
54
|
kwargs.keys()
|
|
@@ -79,11 +76,11 @@ def module_command(
|
|
|
79
76
|
group: CommandGroup,
|
|
80
77
|
flags: set[CommandFlag] | None = None,
|
|
81
78
|
cluster: ClusterCommandConfig = ClusterCommandConfig(),
|
|
82
|
-
|
|
79
|
+
cacheable: bool | None = None,
|
|
83
80
|
version_introduced: str | None = None,
|
|
84
81
|
version_deprecated: str | None = None,
|
|
85
82
|
arguments: dict[str, dict[str, str]] | None = None,
|
|
86
|
-
) -> Callable[[Callable[P,
|
|
83
|
+
) -> Callable[[Callable[P, CommandRequest[R]]], Callable[P, CommandRequest[R]]]:
|
|
87
84
|
command_details = CommandDetails(
|
|
88
85
|
command_name,
|
|
89
86
|
group,
|
|
@@ -91,22 +88,22 @@ def module_command(
|
|
|
91
88
|
version.Version(version_deprecated) if version_deprecated else None,
|
|
92
89
|
arguments,
|
|
93
90
|
cluster or ClusterCommandConfig(),
|
|
94
|
-
cache_config,
|
|
95
91
|
flags or set(),
|
|
96
92
|
None,
|
|
97
93
|
)
|
|
98
94
|
|
|
99
95
|
def wrapper(
|
|
100
|
-
func: Callable[P,
|
|
101
|
-
) -> Callable[P,
|
|
96
|
+
func: Callable[P, CommandRequest[R]],
|
|
97
|
+
) -> Callable[P, CommandRequest[R]]:
|
|
102
98
|
runtime_checkable = add_runtime_checks(func)
|
|
103
|
-
command_cache = CommandCache(command_name, cache_config)
|
|
104
99
|
if flags and CommandFlag.READONLY in flags:
|
|
105
100
|
READONLY_COMMANDS.add(command_name)
|
|
101
|
+
if cacheable:
|
|
102
|
+
CACHEABLE_COMMANDS.add(command_name)
|
|
106
103
|
COMMAND_FLAGS[command_name] = flags or set()
|
|
107
104
|
|
|
108
105
|
@functools.wraps(func)
|
|
109
|
-
|
|
106
|
+
def wrapped(*args: P.args, **kwargs: P.kwargs) -> CommandRequest[R]:
|
|
110
107
|
from coredis.client import Redis, RedisCluster
|
|
111
108
|
|
|
112
109
|
mg = cast(ModuleGroup[bytes], args[0])
|
|
@@ -114,9 +111,8 @@ def module_command(
|
|
|
114
111
|
is_regular_client = isinstance(client, (Redis, RedisCluster))
|
|
115
112
|
runtime_checking = not getattr(client, "noreply", None) and is_regular_client
|
|
116
113
|
callable = runtime_checkable if runtime_checking else func
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return response
|
|
114
|
+
ensure_compatibility(client, module.NAME, command_details, kwargs)
|
|
115
|
+
return callable(*args, **kwargs)
|
|
120
116
|
|
|
121
117
|
wrapped.__doc__ = textwrap.dedent(wrapped.__doc__ or "")
|
|
122
118
|
if group:
|
|
@@ -147,7 +143,7 @@ Compatibility:
|
|
|
147
143
|
wrapped.__doc__ += f"""
|
|
148
144
|
- :paramref:`{argument}`: New in {module.FULL_NAME} version `{min_version}`
|
|
149
145
|
"""
|
|
150
|
-
if
|
|
146
|
+
if cacheable:
|
|
151
147
|
wrapped.__doc__ += """
|
|
152
148
|
.. hint:: Supports client side caching
|
|
153
149
|
"""
|
|
@@ -264,15 +260,3 @@ class ModuleGroup(Generic[AnyStr], metaclass=ModuleGroupRegistry):
|
|
|
264
260
|
|
|
265
261
|
def __init__(self, client: AbstractExecutor):
|
|
266
262
|
self.client = client
|
|
267
|
-
|
|
268
|
-
async def execute_module_command(
|
|
269
|
-
self,
|
|
270
|
-
command: bytes,
|
|
271
|
-
*args: ValueT,
|
|
272
|
-
callback: Callable[..., R] = NoopCallback(),
|
|
273
|
-
**options: ValueT | None,
|
|
274
|
-
) -> R:
|
|
275
|
-
return cast(
|
|
276
|
-
R,
|
|
277
|
-
await self.client.execute_command(command, *args, callback=callback, **options),
|
|
278
|
-
)
|