coredis 4.23.1__py3-none-any.whl → 5.0.0rc1__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.

Files changed (77) hide show
  1. coredis/__init__.py +1 -3
  2. coredis/_packer.py +10 -10
  3. coredis/_protocols.py +23 -32
  4. coredis/_py_311_typing.py +20 -0
  5. coredis/_py_312_typing.py +17 -0
  6. coredis/_utils.py +49 -51
  7. coredis/_version.py +3 -3
  8. coredis/cache.py +57 -82
  9. coredis/client/__init__.py +1 -2
  10. coredis/client/basic.py +129 -56
  11. coredis/client/cluster.py +147 -70
  12. coredis/commands/__init__.py +27 -7
  13. coredis/commands/_key_spec.py +11 -10
  14. coredis/commands/_utils.py +1 -1
  15. coredis/commands/_validators.py +30 -20
  16. coredis/commands/_wrappers.py +19 -99
  17. coredis/commands/bitfield.py +10 -2
  18. coredis/commands/constants.py +20 -3
  19. coredis/commands/core.py +1627 -1246
  20. coredis/commands/function.py +29 -22
  21. coredis/commands/monitor.py +0 -71
  22. coredis/commands/pubsub.py +7 -142
  23. coredis/commands/request.py +108 -0
  24. coredis/commands/script.py +9 -9
  25. coredis/commands/sentinel.py +60 -49
  26. coredis/connection.py +14 -15
  27. coredis/exceptions.py +2 -2
  28. coredis/experimental/__init__.py +0 -4
  29. coredis/globals.py +3 -0
  30. coredis/modules/autocomplete.py +28 -30
  31. coredis/modules/base.py +15 -31
  32. coredis/modules/filters.py +269 -245
  33. coredis/modules/graph.py +61 -62
  34. coredis/modules/json.py +172 -140
  35. coredis/modules/response/_callbacks/autocomplete.py +5 -4
  36. coredis/modules/response/_callbacks/graph.py +34 -29
  37. coredis/modules/response/_callbacks/json.py +5 -3
  38. coredis/modules/response/_callbacks/search.py +49 -53
  39. coredis/modules/response/_callbacks/timeseries.py +18 -30
  40. coredis/modules/response/types.py +1 -5
  41. coredis/modules/search.py +186 -169
  42. coredis/modules/timeseries.py +184 -164
  43. coredis/parser.py +6 -19
  44. coredis/pipeline.py +391 -422
  45. coredis/pool/basic.py +7 -7
  46. coredis/pool/cluster.py +3 -3
  47. coredis/pool/nodemanager.py +10 -3
  48. coredis/response/_callbacks/__init__.py +76 -57
  49. coredis/response/_callbacks/acl.py +0 -3
  50. coredis/response/_callbacks/cluster.py +25 -16
  51. coredis/response/_callbacks/command.py +8 -6
  52. coredis/response/_callbacks/connection.py +4 -3
  53. coredis/response/_callbacks/geo.py +17 -13
  54. coredis/response/_callbacks/hash.py +13 -11
  55. coredis/response/_callbacks/keys.py +9 -5
  56. coredis/response/_callbacks/module.py +2 -3
  57. coredis/response/_callbacks/script.py +6 -8
  58. coredis/response/_callbacks/sentinel.py +21 -17
  59. coredis/response/_callbacks/server.py +36 -14
  60. coredis/response/_callbacks/sets.py +3 -4
  61. coredis/response/_callbacks/sorted_set.py +27 -24
  62. coredis/response/_callbacks/streams.py +22 -13
  63. coredis/response/_callbacks/strings.py +7 -6
  64. coredis/response/_callbacks/vector_sets.py +126 -0
  65. coredis/response/types.py +13 -4
  66. coredis/sentinel.py +1 -1
  67. coredis/stream.py +4 -3
  68. coredis/tokens.py +343 -16
  69. coredis/typing.py +432 -79
  70. {coredis-4.23.1.dist-info → coredis-5.0.0rc1.dist-info}/METADATA +4 -5
  71. coredis-5.0.0rc1.dist-info/RECORD +95 -0
  72. coredis/client/keydb.py +0 -336
  73. coredis/pipeline.pyi +0 -2103
  74. coredis-4.23.1.dist-info/RECORD +0 -93
  75. {coredis-4.23.1.dist-info → coredis-5.0.0rc1.dist-info}/WHEEL +0 -0
  76. {coredis-4.23.1.dist-info → coredis-5.0.0rc1.dist-info}/licenses/LICENSE +0 -0
  77. {coredis-4.23.1.dist-info → coredis-5.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -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
- async def sentinel_ckquorum(self, service_name: StringT) -> bool:
33
- return await self.execute_command(
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
- async def sentinel_config_get(self, name: ValueT) -> dict[AnyStr, AnyStr]:
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 await self.execute_command(
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
- async def sentinel_config_set(self, name: ValueT, value: ValueT) -> bool:
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 await self.execute_command(
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
- async def sentinel_get_master_addr_by_name(
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 await self.execute_command(
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
- async def sentinel_failover(self, service_name: StringT) -> bool:
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 await self.execute_command(
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
- async def sentinel_flushconfig(self) -> bool:
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 await self.execute_command(
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
- async def sentinel_infocache(
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 await self.execute_command(
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
- async def sentinel_master(self, service_name: StringT) -> dict[str, int | bool | str]:
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 await self.execute_command(
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
- async def sentinel_masters(self) -> dict[str, dict[str, int | bool | str]]:
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 await self.execute_command(
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
- async def sentinel_monitor(self, name: ValueT, ip: ValueT, port: int, quorum: int) -> bool:
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 await self.execute_command(
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
- async def sentinel_myid(self) -> AnyStr:
156
+ def sentinel_myid(self) -> CommandRequest[AnyStr]:
150
157
  """Return the ID of the Sentinel instance"""
151
158
 
152
- return await self.execute_command(
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
- async def sentinel_remove(self, name: ValueT) -> bool:
164
+ def sentinel_remove(self, name: RedisValueT) -> CommandRequest[bool]:
160
165
  """Removes a master from Sentinel's monitoring"""
161
166
 
162
- return await self.execute_command(
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
- async def sentinel_sentinels(
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 await self.execute_command(
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
- async def sentinel_set(self, name: ValueT, option: ValueT, value: ValueT) -> bool:
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 await self.execute_command(
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
- async def sentinel_slaves(
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 await self.execute_command(
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
- async def sentinel_replicas(
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 await self.execute_command(
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
- async def sentinel_reset(self, pattern: StringT) -> int:
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 await self.execute_command(
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[ValueT, ...]
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[ValueT] = (
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, ValueT], hello_resp)
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[ValueT], hello_resp)
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: ValueT | None = None,
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: ValueT,
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: ValueT,
598
+ *args: RedisValueT,
598
599
  noreply: bool | None = None,
599
- decode: ValueT | None = None,
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
- try:
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
- waiter.cancel()
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
- **_: ValueT,
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 ValueT
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[ValueT, ...] | None = None,
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
@@ -1,5 +1 @@
1
1
  from __future__ import annotations
2
-
3
- from coredis.client.keydb import KeyDB, KeyDBCluster
4
-
5
- __all__ = ["KeyDB", "KeyDBCluster"]
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
 
@@ -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
- async def sugadd(
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
- pieces: CommandArgList = [key, string, score]
46
+ command_arguments: CommandArgList = [key, string, score]
47
47
  if increment_score:
48
- pieces.append(PureToken.INCREMENT)
48
+ command_arguments.append(PureToken.INCREMENT)
49
49
  if payload:
50
- pieces.extend([PrefixToken.PAYLOAD, payload])
50
+ command_arguments.extend([PrefixToken.PAYLOAD, payload])
51
51
 
52
- return await self.execute_module_command(
53
- CommandName.FT_SUGADD, *pieces, callback=IntCallback()
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
- cache_config=CacheConfig(lambda *a, **_: a[0]),
61
+ cacheable=True,
62
62
  flags={CommandFlag.READONLY},
63
63
  )
64
- async def sugget(
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
- pieces: CommandArgList = [key, prefix]
85
+ command_arguments: CommandArgList = [key, prefix]
86
86
  if fuzzy:
87
- pieces.append(PureToken.FUZZY)
87
+ command_arguments.append(PureToken.FUZZY)
88
88
  if withscores:
89
- pieces.append(PureToken.WITHSCORES)
89
+ command_arguments.append(PureToken.WITHSCORES)
90
90
  if withpayloads:
91
- pieces.append(PureToken.WITHPAYLOADS)
91
+ command_arguments.append(PureToken.WITHPAYLOADS)
92
92
  if max_suggestions is not None:
93
- pieces.append(PureToken.MAX)
94
- pieces.append(max_suggestions)
93
+ command_arguments.append(PureToken.MAX)
94
+ command_arguments.append(max_suggestions)
95
95
 
96
- return await self.execute_module_command(
96
+ return self.client.create_request(
97
97
  CommandName.FT_SUGGET,
98
- *pieces,
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
- async def sugdel(self, key: KeyT, string: StringT) -> bool:
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
- pieces: CommandArgList = [key, string]
116
+ command_arguments: CommandArgList = [key, string]
119
117
 
120
- return await self.execute_module_command(
121
- CommandName.FT_SUGDEL, *pieces, callback=BoolCallback()
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
- async def suglen(self, key: KeyT) -> int:
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
- pieces: CommandArgList = [key]
134
+ command_arguments: CommandArgList = [key]
137
135
 
138
- return await self.execute_module_command(
139
- CommandName.FT_SUGLEN, *pieces, callback=IntCallback()
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
- async def ensure_compatibility(
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 = await client.get_server_module_version(module)
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
- cache_config: CacheConfig | None = None,
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, Coroutine[Any, Any, R]]], Callable[P, Coroutine[Any, Any, R]]]:
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, Coroutine[Any, Any, R]],
101
- ) -> Callable[P, Coroutine[Any, Any, R]]:
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
- async def wrapped(*args: P.args, **kwargs: P.kwargs) -> R:
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
- await ensure_compatibility(client, module.NAME, command_details, kwargs)
118
- async with command_cache(callable, *args, **kwargs) as response:
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 cache_config:
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
- )