valkey-glide 2.0.0rc7__cp39-cp39-macosx_11_0_arm64.whl → 2.1.0rc1__cp39-cp39-macosx_11_0_arm64.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 valkey-glide might be problematic. Click here for more details.

Files changed (45) hide show
  1. glide/__init__.py +103 -107
  2. glide/async_commands/cluster_commands.py +83 -101
  3. glide/async_commands/core.py +554 -424
  4. glide/async_commands/{server_modules/ft.py → ft.py} +8 -7
  5. glide/async_commands/{server_modules/glide_json.py → glide_json.py} +15 -92
  6. glide/async_commands/standalone_commands.py +18 -17
  7. glide/glide.cpython-39-darwin.so +0 -0
  8. glide/glide.pyi +26 -1
  9. glide/glide_client.py +70 -45
  10. glide/logger.py +33 -21
  11. glide/opentelemetry.py +185 -0
  12. glide_shared/__init__.py +326 -0
  13. {glide/async_commands → glide_shared/commands}/batch.py +411 -18
  14. glide_shared/commands/batch_options.py +261 -0
  15. glide_shared/commands/core_options.py +407 -0
  16. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_aggregate_options.py +3 -3
  17. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_create_options.py +4 -2
  18. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_profile_options.py +4 -4
  19. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_search_options.py +4 -2
  20. {glide/async_commands → glide_shared/commands}/server_modules/json_batch.py +4 -4
  21. glide_shared/commands/server_modules/json_options.py +93 -0
  22. {glide/async_commands → glide_shared/commands}/sorted_set.py +2 -2
  23. {glide/async_commands → glide_shared/commands}/stream.py +1 -1
  24. {glide → glide_shared}/config.py +120 -32
  25. {glide → glide_shared}/constants.py +3 -3
  26. {glide → glide_shared}/exceptions.py +27 -1
  27. glide_shared/protobuf/command_request_pb2.py +54 -0
  28. glide_shared/protobuf/connection_request_pb2.py +52 -0
  29. {glide → glide_shared}/protobuf/response_pb2.py +6 -6
  30. {glide → glide_shared}/routes.py +29 -15
  31. valkey_glide-2.1.0rc1.dist-info/METADATA +210 -0
  32. valkey_glide-2.1.0rc1.dist-info/RECORD +39 -0
  33. glide/protobuf/command_request_pb2.py +0 -54
  34. glide/protobuf/command_request_pb2.pyi +0 -1187
  35. glide/protobuf/connection_request_pb2.py +0 -54
  36. glide/protobuf/connection_request_pb2.pyi +0 -320
  37. glide/protobuf/response_pb2.pyi +0 -100
  38. valkey_glide-2.0.0rc7.dist-info/METADATA +0 -144
  39. valkey_glide-2.0.0rc7.dist-info/RECORD +0 -37
  40. /glide/py.typed → /glide_shared/commands/__init__.py +0 -0
  41. {glide/async_commands → glide_shared/commands}/bitmap.py +0 -0
  42. {glide/async_commands → glide_shared/commands}/command_args.py +0 -0
  43. {glide/async_commands → glide_shared/commands}/server_modules/ft_options/ft_constants.py +0 -0
  44. {glide → glide_shared}/protobuf_codec.py +0 -0
  45. {valkey_glide-2.0.0rc7.dist-info → valkey_glide-2.1.0rc1.dist-info}/WHEEL +0 -0
@@ -1,22 +1,8 @@
1
1
  # Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0
2
- from dataclasses import dataclass
3
- from datetime import datetime, timedelta
4
- from enum import Enum
5
- from typing import (
6
- Dict,
7
- List,
8
- Mapping,
9
- Optional,
10
- Protocol,
11
- Set,
12
- Tuple,
13
- Type,
14
- Union,
15
- cast,
16
- get_args,
17
- )
2
+ from typing import Dict, List, Mapping, Optional, Protocol, Set, Tuple, Union, cast
18
3
 
19
- from glide.async_commands.bitmap import (
4
+ from glide.glide import ClusterScanCursor
5
+ from glide_shared.commands.bitmap import (
20
6
  BitFieldGet,
21
7
  BitFieldSubCommands,
22
8
  BitwiseOperation,
@@ -24,8 +10,20 @@ from glide.async_commands.bitmap import (
24
10
  _create_bitfield_args,
25
11
  _create_bitfield_read_only_args,
26
12
  )
27
- from glide.async_commands.command_args import Limit, ListDirection, ObjectType, OrderBy
28
- from glide.async_commands.sorted_set import (
13
+ from glide_shared.commands.command_args import Limit, ListDirection, ObjectType, OrderBy
14
+ from glide_shared.commands.core_options import (
15
+ ConditionalChange,
16
+ ExpireOptions,
17
+ ExpiryGetEx,
18
+ ExpirySet,
19
+ HashFieldConditionalChange,
20
+ InsertPosition,
21
+ OnlyIfEqual,
22
+ PubSubMsg,
23
+ UpdateOptions,
24
+ _build_sort_args,
25
+ )
26
+ from glide_shared.commands.sorted_set import (
29
27
  AggregationType,
30
28
  GeoSearchByBox,
31
29
  GeoSearchByRadius,
@@ -43,7 +41,7 @@ from glide.async_commands.sorted_set import (
43
41
  _create_zinter_zunion_cmd_args,
44
42
  _create_zrange_args,
45
43
  )
46
- from glide.async_commands.stream import (
44
+ from glide_shared.commands.stream import (
47
45
  StreamAddOptions,
48
46
  StreamClaimOptions,
49
47
  StreamGroupOptions,
@@ -54,388 +52,16 @@ from glide.async_commands.stream import (
54
52
  StreamTrimOptions,
55
53
  _create_xpending_range_args,
56
54
  )
57
- from glide.constants import (
55
+ from glide_shared.constants import (
58
56
  TOK,
59
57
  TEncodable,
60
58
  TResult,
61
59
  TXInfoStreamFullResponse,
62
60
  TXInfoStreamResponse,
63
61
  )
64
- from glide.protobuf.command_request_pb2 import RequestType
65
- from glide.routes import Route
66
-
67
- from ..glide import ClusterScanCursor
68
-
69
-
70
- class ConditionalChange(Enum):
71
- """
72
- A condition to the `SET`, `ZADD` and `GEOADD` commands.
73
- """
74
-
75
- ONLY_IF_EXISTS = "XX"
76
- """ Only update key / elements that already exist. Equivalent to `XX` in the Valkey API. """
77
-
78
- ONLY_IF_DOES_NOT_EXIST = "NX"
79
- """ Only set key / add elements that does not already exist. Equivalent to `NX` in the Valkey API. """
80
-
81
-
82
- @dataclass
83
- class OnlyIfEqual:
84
- """
85
- Change condition to the `SET` command,
86
- For additional conditonal options see ConditionalChange
87
-
88
- - comparison_value - value to compare to the current value of a key.
89
-
90
- If comparison_value is equal to the key, it will overwrite the value of key to the new provided value
91
- Equivalent to the IFEQ comparison-value in the Valkey API
92
- """
93
-
94
- comparison_value: TEncodable
95
-
96
-
97
- class ExpiryType(Enum):
98
- """
99
- SET option: The type of the expiry.
100
- """
101
-
102
- SEC = 0, Union[int, timedelta]
103
- """
104
- Set the specified expire time, in seconds. Equivalent to `EX` in the Valkey API.
105
- """
106
-
107
- MILLSEC = 1, Union[int, timedelta]
108
- """
109
- Set the specified expire time, in milliseconds. Equivalent to `PX` in the Valkey API.
110
- """
111
-
112
- UNIX_SEC = 2, Union[int, datetime]
113
- """
114
- Set the specified Unix time at which the key will expire, in seconds. Equivalent to `EXAT` in the Valkey API.
115
- """
116
-
117
- UNIX_MILLSEC = 3, Union[int, datetime]
118
- """
119
- Set the specified Unix time at which the key will expire, in milliseconds. Equivalent to `PXAT` in the Valkey API.
120
- """
121
-
122
- KEEP_TTL = 4, Type[None]
123
- """
124
- Retain the time to live associated with the key. Equivalent to `KEEPTTL` in the Valkey API.
125
- """
126
-
127
-
128
- class ExpiryTypeGetEx(Enum):
129
- """
130
- GetEx option: The type of the expiry.
131
- """
132
-
133
- SEC = 0, Union[int, timedelta]
134
- """ Set the specified expire time, in seconds. Equivalent to `EX` in the Valkey API. """
135
-
136
- MILLSEC = 1, Union[int, timedelta]
137
- """ Set the specified expire time, in milliseconds. Equivalent to `PX` in the Valkey API. """
138
-
139
- UNIX_SEC = 2, Union[int, datetime]
140
- """ Set the specified Unix time at which the key will expire, in seconds. Equivalent to `EXAT` in the Valkey API. """
141
-
142
- UNIX_MILLSEC = 3, Union[int, datetime]
143
- """ Set the specified Unix time at which the key will expire, in milliseconds. Equivalent to `PXAT` in the Valkey API. """
144
-
145
- PERSIST = 4, Type[None]
146
- """ Remove the time to live associated with the key. Equivalent to `PERSIST` in the Valkey API. """
147
-
148
-
149
- class InfoSection(Enum):
150
- """
151
- INFO option: a specific section of information:
152
-
153
- When no parameter is provided, the default option is assumed.
154
- """
155
-
156
- SERVER = "server"
157
- """ General information about the server """
158
-
159
- CLIENTS = "clients"
160
- """ Client connections section """
161
-
162
- MEMORY = "memory"
163
- """ Memory consumption related information """
164
-
165
- PERSISTENCE = "persistence"
166
- """ RDB and AOF related information """
167
-
168
- STATS = "stats"
169
- """ General statistics """
170
-
171
- REPLICATION = "replication"
172
- """ Master/replica replication information """
173
-
174
- CPU = "cpu"
175
- """ CPU consumption statistics """
176
-
177
- COMMAND_STATS = "commandstats"
178
- """ Valkey command statistics """
179
-
180
- LATENCY_STATS = "latencystats"
181
- """ Valkey command latency percentile distribution statistics """
182
-
183
- SENTINEL = "sentinel"
184
- """ Valkey Sentinel section (only applicable to Sentinel instances) """
185
-
186
- CLUSTER = "cluster"
187
- """ Valkey Cluster section """
188
-
189
- MODULES = "modules"
190
- """ Modules section """
191
-
192
- KEYSPACE = "keyspace"
193
- """ Database related statistics """
194
-
195
- ERROR_STATS = "errorstats"
196
- """ Valkey error statistics """
197
-
198
- ALL = "all"
199
- """ Return all sections (excluding module generated ones) """
200
-
201
- DEFAULT = "default"
202
- """ Return only the default set of sections """
203
-
204
- EVERYTHING = "everything"
205
- """ Includes all and modules """
206
-
207
-
208
- class ExpireOptions(Enum):
209
- """
210
- EXPIRE option: options for setting key expiry.
211
- """
212
-
213
- HasNoExpiry = "NX"
214
- """ Set expiry only when the key has no expiry (Equivalent to "NX" in Valkey). """
215
-
216
- HasExistingExpiry = "XX"
217
- """ Set expiry only when the key has an existing expiry (Equivalent to "XX" in Valkey). """
218
-
219
- NewExpiryGreaterThanCurrent = "GT"
220
- """
221
- Set expiry only when the new expiry is greater than the current one (Equivalent to "GT" in Valkey).
222
- """
223
-
224
- NewExpiryLessThanCurrent = "LT"
225
- """
226
- Set expiry only when the new expiry is less than the current one (Equivalent to "LT" in Valkey).
227
- """
228
-
229
-
230
- class UpdateOptions(Enum):
231
- """
232
- Options for updating elements of a sorted set key.
233
- """
234
-
235
- LESS_THAN = "LT"
236
- """ Only update existing elements if the new score is less than the current score. """
237
-
238
- GREATER_THAN = "GT"
239
- """ Only update existing elements if the new score is greater than the current score. """
240
-
241
-
242
- class ExpirySet:
243
- """
244
- SET option: Represents the expiry type and value to be executed with "SET" command.
245
-
246
- Attributes:
247
- cmd_arg (str): The expiry type.
248
- value (str): The value for the expiry type.
249
- """
250
-
251
- def __init__(
252
- self,
253
- expiry_type: ExpiryType,
254
- value: Optional[Union[int, datetime, timedelta]],
255
- ) -> None:
256
- self.set_expiry_type_and_value(expiry_type, value)
257
-
258
- def __eq__(self, other: "object") -> bool:
259
- if not isinstance(other, ExpirySet):
260
- return NotImplemented
261
- return self.expiry_type == other.expiry_type and self.value == other.value
262
-
263
- def set_expiry_type_and_value(
264
- self, expiry_type: ExpiryType, value: Optional[Union[int, datetime, timedelta]]
265
- ):
266
- """
267
- Args:
268
- expiry_type (ExpiryType): The expiry type.
269
- value (Optional[Union[int, datetime, timedelta]]): The value of the expiration type. The type of expiration
270
- determines the type of expiration value:
271
-
272
- - SEC: Union[int, timedelta]
273
- - MILLSEC: Union[int, timedelta]
274
- - UNIX_SEC: Union[int, datetime]
275
- - UNIX_MILLSEC: Union[int, datetime]
276
- - KEEP_TTL: Type[None]
277
- """
278
- if not isinstance(value, get_args(expiry_type.value[1])):
279
- raise ValueError(
280
- f"The value of {expiry_type} should be of type {expiry_type.value[1]}"
281
- )
282
- self.expiry_type = expiry_type
283
- if self.expiry_type == ExpiryType.SEC:
284
- self.cmd_arg = "EX"
285
- if isinstance(value, timedelta):
286
- value = int(value.total_seconds())
287
- elif self.expiry_type == ExpiryType.MILLSEC:
288
- self.cmd_arg = "PX"
289
- if isinstance(value, timedelta):
290
- value = int(value.total_seconds() * 1000)
291
- elif self.expiry_type == ExpiryType.UNIX_SEC:
292
- self.cmd_arg = "EXAT"
293
- if isinstance(value, datetime):
294
- value = int(value.timestamp())
295
- elif self.expiry_type == ExpiryType.UNIX_MILLSEC:
296
- self.cmd_arg = "PXAT"
297
- if isinstance(value, datetime):
298
- value = int(value.timestamp() * 1000)
299
- elif self.expiry_type == ExpiryType.KEEP_TTL:
300
- self.cmd_arg = "KEEPTTL"
301
- self.value = str(value) if value else None
302
-
303
- def get_cmd_args(self) -> List[str]:
304
- return [self.cmd_arg] if self.value is None else [self.cmd_arg, self.value]
305
-
306
-
307
- class ExpiryGetEx:
308
- """
309
- GetEx option: Represents the expiry type and value to be executed with "GetEx" command.
310
-
311
- Attributes:
312
- cmd_arg (str): The expiry type.
313
- value (str): The value for the expiry type.
314
- """
315
-
316
- def __init__(
317
- self,
318
- expiry_type: ExpiryTypeGetEx,
319
- value: Optional[Union[int, datetime, timedelta]],
320
- ) -> None:
321
- self.set_expiry_type_and_value(expiry_type, value)
322
-
323
- def set_expiry_type_and_value(
324
- self,
325
- expiry_type: ExpiryTypeGetEx,
326
- value: Optional[Union[int, datetime, timedelta]],
327
- ):
328
- """
329
- Args:
330
- expiry_type (ExpiryType): The expiry type.
331
- value (Optional[Union[int, datetime, timedelta]]): The value of the expiration type. The type of expiration
332
- determines the type of expiration value:
333
-
334
- - SEC: Union[int, timedelta]
335
- - MILLSEC: Union[int, timedelta]
336
- - UNIX_SEC: Union[int, datetime]
337
- - UNIX_MILLSEC: Union[int, datetime]
338
- - PERSIST: Type[None]
339
- """
340
- if not isinstance(value, get_args(expiry_type.value[1])):
341
- raise ValueError(
342
- f"The value of {expiry_type} should be of type {expiry_type.value[1]}"
343
- )
344
- self.expiry_type = expiry_type
345
- if self.expiry_type == ExpiryTypeGetEx.SEC:
346
- self.cmd_arg = "EX"
347
- if isinstance(value, timedelta):
348
- value = int(value.total_seconds())
349
- elif self.expiry_type == ExpiryTypeGetEx.MILLSEC:
350
- self.cmd_arg = "PX"
351
- if isinstance(value, timedelta):
352
- value = int(value.total_seconds() * 1000)
353
- elif self.expiry_type == ExpiryTypeGetEx.UNIX_SEC:
354
- self.cmd_arg = "EXAT"
355
- if isinstance(value, datetime):
356
- value = int(value.timestamp())
357
- elif self.expiry_type == ExpiryTypeGetEx.UNIX_MILLSEC:
358
- self.cmd_arg = "PXAT"
359
- if isinstance(value, datetime):
360
- value = int(value.timestamp() * 1000)
361
- elif self.expiry_type == ExpiryTypeGetEx.PERSIST:
362
- self.cmd_arg = "PERSIST"
363
- self.value = str(value) if value else None
364
-
365
- def get_cmd_args(self) -> List[str]:
366
- return [self.cmd_arg] if self.value is None else [self.cmd_arg, self.value]
367
-
368
-
369
- class InsertPosition(Enum):
370
- BEFORE = "BEFORE"
371
- AFTER = "AFTER"
372
-
373
-
374
- class FlushMode(Enum):
375
- """
376
- Defines flushing mode for:
377
-
378
- `FLUSHALL` command and `FUNCTION FLUSH` command.
379
-
380
- See [FLUSHAL](https://valkey.io/commands/flushall/) and [FUNCTION-FLUSH](https://valkey.io/commands/function-flush/)
381
- for details
382
-
383
- SYNC was introduced in version 6.2.0.
384
- """
385
-
386
- ASYNC = "ASYNC"
387
- SYNC = "SYNC"
388
-
389
-
390
- class FunctionRestorePolicy(Enum):
391
- """
392
- Options for the FUNCTION RESTORE command.
393
- """
394
-
395
- APPEND = "APPEND"
396
- """ Appends the restored libraries to the existing libraries and aborts on collision. This is the default policy. """
397
-
398
- FLUSH = "FLUSH"
399
- """ Deletes all existing libraries before restoring the payload. """
400
-
401
- REPLACE = "REPLACE"
402
- """
403
- Appends the restored libraries to the existing libraries, replacing any existing ones in case
404
- of name collisions. Note that this policy doesn't prevent function name collisions, only libraries.
405
- """
406
-
407
-
408
- def _build_sort_args(
409
- key: TEncodable,
410
- by_pattern: Optional[TEncodable] = None,
411
- limit: Optional[Limit] = None,
412
- get_patterns: Optional[List[TEncodable]] = None,
413
- order: Optional[OrderBy] = None,
414
- alpha: Optional[bool] = None,
415
- store: Optional[TEncodable] = None,
416
- ) -> List[TEncodable]:
417
- args = [key]
418
-
419
- if by_pattern:
420
- args.extend(["BY", by_pattern])
421
-
422
- if limit:
423
- args.extend(["LIMIT", str(limit.offset), str(limit.count)])
424
-
425
- if get_patterns:
426
- for pattern in get_patterns:
427
- args.extend(["GET", pattern])
428
-
429
- if order:
430
- args.append(order.value)
431
-
432
- if alpha:
433
- args.append("ALPHA")
434
-
435
- if store:
436
- args.extend(["STORE", store])
437
-
438
- return args
62
+ from glide_shared.exceptions import RequestError
63
+ from glide_shared.protobuf.command_request_pb2 import RequestType
64
+ from glide_shared.routes import Route
439
65
 
440
66
 
441
67
  class CoreCommands(Protocol):
@@ -1477,6 +1103,519 @@ class CoreCommands(Protocol):
1477
1103
  await self._execute_command(RequestType.HStrlen, [key, field]),
1478
1104
  )
1479
1105
 
1106
+ async def httl(self, key: TEncodable, fields: List[TEncodable]) -> List[int]:
1107
+ """
1108
+ Returns the remaining time to live (in seconds) of hash key's field(s) that have an associated expiration.
1109
+
1110
+ See [valkey.io](https://valkey.io/commands/httl/) for more details.
1111
+
1112
+ Args:
1113
+ key (TEncodable): The key of the hash.
1114
+ fields (List[TEncodable]): The list of fields to get TTL for.
1115
+
1116
+ Returns:
1117
+ List[int]: A list of TTL values for each field:
1118
+ - Positive integer: remaining TTL in seconds
1119
+ - `-1`: field exists but has no expiration
1120
+ - `-2`: field does not exist or key does not exist
1121
+
1122
+ Examples:
1123
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.EX, 10))
1124
+ >>> await client.httl("my_hash", ["field1", "field2", "non_existent_field"])
1125
+ [9, 9, -2] # field1 and field2 have ~9 seconds left, non_existent_field doesn't exist
1126
+
1127
+ Since: Valkey 9.0.0
1128
+ """
1129
+ return cast(
1130
+ List[int],
1131
+ await self._execute_command(
1132
+ RequestType.HTtl, [key, "FIELDS", str(len(fields))] + fields
1133
+ ),
1134
+ )
1135
+
1136
+ async def hpttl(self, key: TEncodable, fields: List[TEncodable]) -> List[int]:
1137
+ """
1138
+ Returns the remaining time to live (in milliseconds) of hash key's field(s) that have an associated expiration.
1139
+
1140
+ See [valkey.io](https://valkey.io/commands/hpttl/) for more details.
1141
+
1142
+ Args:
1143
+ key (TEncodable): The key of the hash.
1144
+ fields (List[TEncodable]): The list of fields to get TTL for.
1145
+
1146
+ Returns:
1147
+ List[int]: A list of TTL values for each field:
1148
+ - Positive integer: remaining TTL in milliseconds
1149
+ - `-1`: field exists but has no expiration
1150
+ - `-2`: field does not exist or key does not exist
1151
+
1152
+ Examples:
1153
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.PX, 10000))
1154
+ >>> await client.hpttl("my_hash", ["field1", "field2", "non_existent_field"])
1155
+ [9500, 9500, -2] # field1 and field2 have ~9500 milliseconds left, non_existent_field doesn't exist
1156
+
1157
+ Since: Valkey 9.0.0
1158
+ """
1159
+ return cast(
1160
+ List[int],
1161
+ await self._execute_command(
1162
+ RequestType.HPTtl, [key, "FIELDS", str(len(fields))] + fields
1163
+ ),
1164
+ )
1165
+
1166
+ async def hexpiretime(self, key: TEncodable, fields: List[TEncodable]) -> List[int]:
1167
+ """
1168
+ Returns the expiration Unix timestamp (in seconds) of hash key's field(s) that have an associated expiration.
1169
+
1170
+ See [valkey.io](https://valkey.io/commands/hexpiretime/) for more details.
1171
+
1172
+ Args:
1173
+ key (TEncodable): The key of the hash.
1174
+ fields (List[TEncodable]): The list of fields to get expiration timestamps for.
1175
+
1176
+ Returns:
1177
+ List[int]: A list of expiration timestamps for each field:
1178
+ - Positive integer: absolute expiration timestamp in seconds (Unix timestamp)
1179
+ - `-1`: field exists but has no expiration
1180
+ - `-2`: field does not exist or key does not exist
1181
+
1182
+ Examples:
1183
+ >>> import time
1184
+ >>> future_timestamp = int(time.time()) + 60 # 60 seconds from now
1185
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.EXAT, future_timestamp))
1186
+ >>> await client.hexpiretime("my_hash", ["field1", "field2", "non_existent_field"])
1187
+ [future_timestamp, future_timestamp, -2] # field1 and field2 expire at future_timestamp, non_existent_field doesn't exist
1188
+
1189
+ Since: Valkey 9.0.0
1190
+ """
1191
+ return cast(
1192
+ List[int],
1193
+ await self._execute_command(
1194
+ RequestType.HExpireTime, [key, "FIELDS", str(len(fields))] + fields
1195
+ ),
1196
+ )
1197
+
1198
+ async def hpexpiretime(
1199
+ self, key: TEncodable, fields: List[TEncodable]
1200
+ ) -> List[int]:
1201
+ """
1202
+ Returns the expiration Unix timestamp (in milliseconds) of hash key's field(s) that have an associated expiration.
1203
+
1204
+ See [valkey.io](https://valkey.io/commands/hpexpiretime/) for more details.
1205
+
1206
+ Args:
1207
+ key (TEncodable): The key of the hash.
1208
+ fields (List[TEncodable]): The list of fields to get expiration timestamps for.
1209
+
1210
+ Returns:
1211
+ List[int]: A list of expiration timestamps for each field:
1212
+ - Positive integer: absolute expiration timestamp in milliseconds (Unix timestamp in ms)
1213
+ - `-1`: field exists but has no expiration
1214
+ - `-2`: field does not exist or key does not exist
1215
+
1216
+ Examples:
1217
+ >>> import time
1218
+ >>> future_timestamp_ms = int(time.time() * 1000) + 60000 # 60 seconds from now in milliseconds
1219
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.PXAT, future_timestamp_ms))
1220
+ >>> await client.hpexpiretime("my_hash", ["field1", "field2", "non_existent_field"])
1221
+ [future_timestamp_ms, future_timestamp_ms, -2] # field1 and field2 expire at future_timestamp_ms, non_existent_field doesn't exist
1222
+
1223
+ Since: Valkey 9.0.0
1224
+ """
1225
+ return cast(
1226
+ List[int],
1227
+ await self._execute_command(
1228
+ RequestType.HPExpireTime, [key, "FIELDS", str(len(fields))] + fields
1229
+ ),
1230
+ )
1231
+
1232
+ async def hsetex(
1233
+ self,
1234
+ key: TEncodable,
1235
+ field_value_map: Mapping[TEncodable, TEncodable],
1236
+ field_conditional_change: Optional[HashFieldConditionalChange] = None,
1237
+ expiry: Optional[ExpirySet] = None,
1238
+ ) -> int:
1239
+ """
1240
+ Sets the specified fields to their respective values in the hash stored at `key` with optional expiration.
1241
+
1242
+ See [valkey.io](https://valkey.io/commands/hsetex/) for more details.
1243
+
1244
+ Args:
1245
+ key (TEncodable): The key of the hash.
1246
+ field_value_map (Mapping[TEncodable, TEncodable]): A field-value map consisting of fields and their corresponding
1247
+ values to be set in the hash stored at the specified key.
1248
+ field_conditional_change (Optional[HashFieldConditionalChange]): Field conditional change option:
1249
+ - ONLY_IF_ALL_EXIST (FXX): Only set fields if all of them already exist.
1250
+ - ONLY_IF_NONE_EXIST (FNX): Only set fields if none of them already exist.
1251
+ expiry (Optional[ExpirySet]): Expiration options for the fields:
1252
+ - EX: Expiration time in seconds.
1253
+ - PX: Expiration time in milliseconds.
1254
+ - EXAT: Absolute expiration time in seconds (Unix timestamp).
1255
+ - PXAT: Absolute expiration time in milliseconds (Unix timestamp).
1256
+ - KEEPTTL: Retain existing TTL.
1257
+
1258
+ Returns:
1259
+ int: 1 if all fields were set successfully, 0 if none were set due to conditional constraints.
1260
+
1261
+ Examples:
1262
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.EX, 10))
1263
+ 1 # All fields set with 10 second expiration
1264
+ >>> await client.hsetex("my_hash", {"field3": "value3"}, field_conditional_change=HashFieldConditionalChange.ONLY_IF_ALL_EXIST)
1265
+ 1 # Field set because field already exists
1266
+ >>> await client.hsetex("new_hash", {"field1": "value1"}, field_conditional_change=HashFieldConditionalChange.ONLY_IF_ALL_EXIST)
1267
+ 0 # No fields set because hash doesn't exist
1268
+
1269
+ Since: Valkey 9.0.0
1270
+ """
1271
+ args: List[TEncodable] = [key]
1272
+
1273
+ # Add field conditional change option if specified
1274
+ if field_conditional_change is not None:
1275
+ args.append(field_conditional_change.value)
1276
+
1277
+ # Add expiry options if specified
1278
+ if expiry is not None:
1279
+ args.extend(expiry.get_cmd_args())
1280
+
1281
+ # Add FIELDS keyword and field count
1282
+ args.extend(["FIELDS", str(len(field_value_map))])
1283
+
1284
+ # Add field-value pairs
1285
+ for field, value in field_value_map.items():
1286
+ args.extend([field, value])
1287
+
1288
+ return cast(
1289
+ int,
1290
+ await self._execute_command(RequestType.HSetEx, args),
1291
+ )
1292
+
1293
+ async def hgetex(
1294
+ self,
1295
+ key: TEncodable,
1296
+ fields: List[TEncodable],
1297
+ expiry: Optional[ExpiryGetEx] = None,
1298
+ ) -> Optional[List[Optional[bytes]]]:
1299
+ """
1300
+ Retrieves the values of specified fields in the hash stored at `key` and optionally sets their expiration.
1301
+
1302
+ See [valkey.io](https://valkey.io/commands/hgetex/) for more details.
1303
+
1304
+ Args:
1305
+ key (TEncodable): The key of the hash.
1306
+ fields (List[TEncodable]): The list of fields to retrieve from the hash.
1307
+ expiry (Optional[ExpiryGetEx]): Expiration options for the retrieved fields:
1308
+ - EX: Expiration time in seconds.
1309
+ - PX: Expiration time in milliseconds.
1310
+ - EXAT: Absolute expiration time in seconds (Unix timestamp).
1311
+ - PXAT: Absolute expiration time in milliseconds (Unix timestamp).
1312
+ - PERSIST: Remove expiration from the fields.
1313
+
1314
+ Returns:
1315
+ Optional[List[Optional[bytes]]]: A list of values associated with the given fields, in the same order as requested.
1316
+ For every field that does not exist in the hash, a null value is returned.
1317
+ If `key` does not exist, it is treated as an empty hash, and the function returns a list of null values.
1318
+
1319
+ Examples:
1320
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.EX, 10))
1321
+ >>> await client.hgetex("my_hash", ["field1", "field2"])
1322
+ [b"value1", b"value2"]
1323
+ >>> await client.hgetex("my_hash", ["field1"], expiry=ExpiryGetEx(ExpiryTypeGetEx.EX, 20))
1324
+ [b"value1"] # field1 now has 20 second expiration
1325
+ >>> await client.hgetex("my_hash", ["field1"], expiry=ExpiryGetEx(ExpiryTypeGetEx.PERSIST, None))
1326
+ [b"value1"] # field1 expiration removed
1327
+
1328
+ Since: Valkey 9.0.0
1329
+ """
1330
+ args: List[TEncodable] = [key]
1331
+
1332
+ # Add expiry options if specified
1333
+ if expiry is not None:
1334
+ args.extend(expiry.get_cmd_args())
1335
+
1336
+ # Add FIELDS keyword and field count
1337
+ args.extend(["FIELDS", str(len(fields))])
1338
+
1339
+ # Add fields
1340
+ args.extend(fields)
1341
+
1342
+ return cast(
1343
+ Optional[List[Optional[bytes]]],
1344
+ await self._execute_command(RequestType.HGetEx, args),
1345
+ )
1346
+
1347
+ async def hexpire(
1348
+ self,
1349
+ key: TEncodable,
1350
+ seconds: int,
1351
+ fields: List[TEncodable],
1352
+ option: Optional[ExpireOptions] = None,
1353
+ ) -> List[int]:
1354
+ """
1355
+ Sets expiration time in seconds for one or more hash fields.
1356
+
1357
+ See [valkey.io](https://valkey.io/commands/hexpire/) for more details.
1358
+
1359
+ Args:
1360
+ key (TEncodable): The key of the hash.
1361
+ seconds (int): The expiration time in seconds.
1362
+ fields (List[TEncodable]): The list of fields to set expiration for.
1363
+ option (Optional[ExpireOptions]): Conditional expiration option:
1364
+ - HasNoExpiry (NX): Set expiration only when the field has no expiry.
1365
+ - HasExistingExpiry (XX): Set expiration only when the field has an existing expiry.
1366
+ - NewExpiryGreaterThanCurrent (GT): Set expiration only when the new expiry is greater than the current one.
1367
+ - NewExpiryLessThanCurrent (LT): Set expiration only when the new expiry is less than the current one.
1368
+
1369
+ Returns:
1370
+ List[int]: A list of status codes for each field:
1371
+ - `1`: Expiration time was applied successfully.
1372
+ - `0`: Specified condition was not met.
1373
+ - `-2`: Field does not exist or key does not exist.
1374
+ - `2`: Field was deleted immediately (when seconds is 0 or timestamp is in the past).
1375
+
1376
+ Examples:
1377
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.EX, 10))
1378
+ >>> await client.hexpire("my_hash", 20, ["field1", "field2"])
1379
+ [1, 1] # Both fields' expiration set to 20 seconds
1380
+ >>> await client.hexpire("my_hash", 30, ["field1"], option=ExpireOptions.NewExpiryGreaterThanCurrent)
1381
+ [1] # field1 expiration updated to 30 seconds (greater than current 20)
1382
+ >>> await client.hexpire("my_hash", 0, ["field2"])
1383
+ [2] # field2 deleted immediately
1384
+ >>> await client.hexpire("my_hash", 10, ["non_existent_field"])
1385
+ [-2] # Field doesn't exist
1386
+
1387
+ Since: Valkey 9.0.0
1388
+ """
1389
+ args: List[TEncodable] = [key, str(seconds)]
1390
+
1391
+ # Add conditional option if specified
1392
+ if option is not None:
1393
+ args.append(option.value)
1394
+
1395
+ # Add FIELDS keyword and field count
1396
+ args.extend(["FIELDS", str(len(fields))])
1397
+
1398
+ # Add fields
1399
+ args.extend(fields)
1400
+
1401
+ return cast(
1402
+ List[int],
1403
+ await self._execute_command(RequestType.HExpire, args),
1404
+ )
1405
+
1406
+ async def hpersist(self, key: TEncodable, fields: List[TEncodable]) -> List[int]:
1407
+ """
1408
+ Removes the expiration from one or more hash fields, making them persistent.
1409
+
1410
+ See [valkey.io](https://valkey.io/commands/hpersist/) for more details.
1411
+
1412
+ Args:
1413
+ key (TEncodable): The key of the hash.
1414
+ fields (List[TEncodable]): The list of fields to remove expiration from.
1415
+
1416
+ Returns:
1417
+ List[int]: A list of status codes for each field:
1418
+ - `1`: Expiration was removed successfully (field became persistent).
1419
+ - `-1`: Field exists but has no expiration.
1420
+ - `-2`: Field does not exist or key does not exist.
1421
+
1422
+ Examples:
1423
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.EX, 10))
1424
+ >>> await client.hpersist("my_hash", ["field1", "field2"])
1425
+ [1, 1] # Both fields made persistent
1426
+ >>> await client.hpersist("my_hash", ["field1"])
1427
+ [-1] # field1 already persistent
1428
+ >>> await client.hpersist("my_hash", ["non_existent_field"])
1429
+ [-2] # Field doesn't exist
1430
+
1431
+ Since: Valkey 9.0.0
1432
+ """
1433
+ args: List[TEncodable] = [key, "FIELDS", str(len(fields))] + fields
1434
+
1435
+ return cast(
1436
+ List[int],
1437
+ await self._execute_command(RequestType.HPersist, args),
1438
+ )
1439
+
1440
+ async def hpexpire(
1441
+ self,
1442
+ key: TEncodable,
1443
+ milliseconds: int,
1444
+ fields: List[TEncodable],
1445
+ option: Optional[ExpireOptions] = None,
1446
+ ) -> List[int]:
1447
+ """
1448
+ Sets expiration time in milliseconds for one or more hash fields.
1449
+
1450
+ See [valkey.io](https://valkey.io/commands/hpexpire/) for more details.
1451
+
1452
+ Args:
1453
+ key (TEncodable): The key of the hash.
1454
+ milliseconds (int): The expiration time in milliseconds.
1455
+ fields (List[TEncodable]): The list of fields to set expiration for.
1456
+ option (Optional[ExpireOptions]): Conditional expiration option:
1457
+ - HasNoExpiry (NX): Set expiration only when the field has no expiry.
1458
+ - HasExistingExpiry (XX): Set expiration only when the field has an existing expiry.
1459
+ - NewExpiryGreaterThanCurrent (GT): Set expiration only when the new expiry is greater than the current one.
1460
+ - NewExpiryLessThanCurrent (LT): Set expiration only when the new expiry is less than the current one.
1461
+
1462
+ Returns:
1463
+ List[int]: A list of status codes for each field:
1464
+ - `1`: Expiration time was applied successfully.
1465
+ - `0`: Specified condition was not met.
1466
+ - `-2`: Field does not exist or key does not exist.
1467
+ - `2`: Field was deleted immediately (when milliseconds is 0 or timestamp is in the past).
1468
+
1469
+ Examples:
1470
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.PX, 10000))
1471
+ >>> await client.hpexpire("my_hash", 20000, ["field1", "field2"])
1472
+ [1, 1] # Both fields' expiration set to 20000 milliseconds
1473
+ >>> await client.hpexpire("my_hash", 30000, ["field1"], option=ExpireOptions.NewExpiryGreaterThanCurrent)
1474
+ [1] # field1 expiration updated to 30000 milliseconds (greater than current 20000)
1475
+ >>> await client.hpexpire("my_hash", 0, ["field2"])
1476
+ [2] # field2 deleted immediately
1477
+ >>> await client.hpexpire("my_hash", 10000, ["non_existent_field"])
1478
+ [-2] # Field doesn't exist
1479
+
1480
+ Since: Valkey 9.0.0
1481
+ """
1482
+ args: List[TEncodable] = [key, str(milliseconds)]
1483
+
1484
+ # Add conditional option if specified
1485
+ if option is not None:
1486
+ args.append(option.value)
1487
+
1488
+ # Add FIELDS keyword and field count
1489
+ args.extend(["FIELDS", str(len(fields))])
1490
+
1491
+ # Add fields
1492
+ args.extend(fields)
1493
+
1494
+ return cast(
1495
+ List[int],
1496
+ await self._execute_command(RequestType.HPExpire, args),
1497
+ )
1498
+
1499
+ async def hexpireat(
1500
+ self,
1501
+ key: TEncodable,
1502
+ unix_timestamp: int,
1503
+ fields: List[TEncodable],
1504
+ option: Optional[ExpireOptions] = None,
1505
+ ) -> List[int]:
1506
+ """
1507
+ Sets expiration time at absolute Unix timestamp in seconds for one or more hash fields.
1508
+
1509
+ See [valkey.io](https://valkey.io/commands/hexpireat/) for more details.
1510
+
1511
+ Args:
1512
+ key (TEncodable): The key of the hash.
1513
+ unix_timestamp (int): The absolute expiration time as Unix timestamp in seconds.
1514
+ fields (List[TEncodable]): The list of fields to set expiration for.
1515
+ option (Optional[ExpireOptions]): Conditional expiration option:
1516
+ - HasNoExpiry (NX): Set expiration only when the field has no expiry.
1517
+ - HasExistingExpiry (XX): Set expiration only when the field has an existing expiry.
1518
+ - NewExpiryGreaterThanCurrent (GT): Set expiration only when the new expiry is greater than the current one.
1519
+ - NewExpiryLessThanCurrent (LT): Set expiration only when the new expiry is less than the current one.
1520
+
1521
+ Returns:
1522
+ List[int]: A list of status codes for each field:
1523
+ - `1`: Expiration time was applied successfully.
1524
+ - `0`: Specified condition was not met.
1525
+ - `-2`: Field does not exist or key does not exist.
1526
+ - `2`: Field was deleted immediately (when timestamp is in the past).
1527
+
1528
+ Examples:
1529
+ >>> import time
1530
+ >>> future_timestamp = int(time.time()) + 60 # 60 seconds from now
1531
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.EX, 10))
1532
+ >>> await client.hexpireat("my_hash", future_timestamp, ["field1", "field2"])
1533
+ [1, 1] # Both fields' expiration set to future_timestamp
1534
+ >>> past_timestamp = int(time.time()) - 60 # 60 seconds ago
1535
+ >>> await client.hexpireat("my_hash", past_timestamp, ["field1"])
1536
+ [2] # field1 deleted immediately (past timestamp)
1537
+ >>> await client.hexpireat("my_hash", future_timestamp, ["non_existent_field"])
1538
+ [-2] # Field doesn't exist
1539
+
1540
+ Since: Valkey 9.0.0
1541
+ """
1542
+ args: List[TEncodable] = [key, str(unix_timestamp)]
1543
+
1544
+ # Add conditional option if specified
1545
+ if option is not None:
1546
+ args.append(option.value)
1547
+
1548
+ # Add FIELDS keyword and field count
1549
+ args.extend(["FIELDS", str(len(fields))])
1550
+
1551
+ # Add fields
1552
+ args.extend(fields)
1553
+
1554
+ return cast(
1555
+ List[int],
1556
+ await self._execute_command(RequestType.HExpireAt, args),
1557
+ )
1558
+
1559
+ async def hpexpireat(
1560
+ self,
1561
+ key: TEncodable,
1562
+ unix_timestamp_ms: int,
1563
+ fields: List[TEncodable],
1564
+ option: Optional[ExpireOptions] = None,
1565
+ ) -> List[int]:
1566
+ """
1567
+ Sets expiration time at absolute Unix timestamp in milliseconds for one or more hash fields.
1568
+
1569
+ See [valkey.io](https://valkey.io/commands/hpexpireat/) for more details.
1570
+
1571
+ Args:
1572
+ key (TEncodable): The key of the hash.
1573
+ unix_timestamp_ms (int): The absolute expiration time as Unix timestamp in milliseconds.
1574
+ fields (List[TEncodable]): The list of fields to set expiration for.
1575
+ option (Optional[ExpireOptions]): Conditional expiration option:
1576
+ - HasNoExpiry (NX): Set expiration only when the field has no expiry.
1577
+ - HasExistingExpiry (XX): Set expiration only when the field has an existing expiry.
1578
+ - NewExpiryGreaterThanCurrent (GT): Set expiration only when the new expiry is greater than the current one.
1579
+ - NewExpiryLessThanCurrent (LT): Set expiration only when the new expiry is less than the current one.
1580
+
1581
+ Returns:
1582
+ List[int]: A list of status codes for each field:
1583
+ - `1`: Expiration time was applied successfully.
1584
+ - `0`: Specified condition was not met.
1585
+ - `-2`: Field does not exist or key does not exist.
1586
+ - `2`: Field was deleted immediately (when timestamp is in the past).
1587
+
1588
+ Examples:
1589
+ >>> import time
1590
+ >>> future_timestamp_ms = int(time.time() * 1000) + 60000 # 60 seconds from now in milliseconds
1591
+ >>> await client.hsetex("my_hash", {"field1": "value1", "field2": "value2"}, expiry=ExpirySet(ExpiryType.PX, 10000))
1592
+ >>> await client.hpexpireat("my_hash", future_timestamp_ms, ["field1", "field2"])
1593
+ [1, 1] # Both fields' expiration set to future_timestamp_ms
1594
+ >>> past_timestamp_ms = int(time.time() * 1000) - 60000 # 60 seconds ago in milliseconds
1595
+ >>> await client.hpexpireat("my_hash", past_timestamp_ms, ["field1"])
1596
+ [2] # field1 deleted immediately (past timestamp)
1597
+ >>> await client.hpexpireat("my_hash", future_timestamp_ms, ["non_existent_field"])
1598
+ [-2] # Field doesn't exist
1599
+
1600
+ Since: Valkey 9.0.0
1601
+ """
1602
+ args: List[TEncodable] = [key, str(unix_timestamp_ms)]
1603
+
1604
+ # Add conditional option if specified
1605
+ if option is not None:
1606
+ args.append(option.value)
1607
+
1608
+ # Add FIELDS keyword and field count
1609
+ args.extend(["FIELDS", str(len(fields))])
1610
+
1611
+ # Add fields
1612
+ args.extend(fields)
1613
+
1614
+ return cast(
1615
+ List[int],
1616
+ await self._execute_command(RequestType.HPExpireAt, args),
1617
+ )
1618
+
1480
1619
  async def lpush(self, key: TEncodable, elements: List[TEncodable]) -> int:
1481
1620
  """
1482
1621
  Insert all the specified values at the head of the list stored at `key`.
@@ -2535,8 +2674,8 @@ class CoreCommands(Protocol):
2535
2674
  Returns:
2536
2675
  TOK: A simple "OK" response.
2537
2676
 
2538
- If `start` exceeds the end of the list, or if `start` is greater than `end`, the result will be an empty list
2539
- (which causes `key` to be removed).
2677
+ If `start` exceeds the end of the list, or if `start` is greater than `end`, the list is emptied
2678
+ and the key is removed.
2540
2679
 
2541
2680
  If `end` exceeds the actual end of the list, it will be treated like the last element of the list.
2542
2681
 
@@ -2554,9 +2693,6 @@ class CoreCommands(Protocol):
2554
2693
  async def lrem(self, key: TEncodable, count: int, element: TEncodable) -> int:
2555
2694
  """
2556
2695
  Removes the first `count` occurrences of elements equal to `element` from the list stored at `key`.
2557
- If `count` is positive, it removes elements equal to `element` moving from head to tail.
2558
- If `count` is negative, it removes elements equal to `element` moving from tail to head.
2559
- If `count` is 0 or greater than the occurrences of elements equal to `element`, it removes all elements
2560
2696
  equal to `element`.
2561
2697
 
2562
2698
  See [valkey.io](https://valkey.io/commands/lrem/) for more details.
@@ -2564,6 +2700,11 @@ class CoreCommands(Protocol):
2564
2700
  Args:
2565
2701
  key (TEncodable): The key of the list.
2566
2702
  count (int): The count of occurrences of elements equal to `element` to remove.
2703
+
2704
+ - If `count` is positive, it removes elements equal to `element` moving from head to tail.
2705
+ - If `count` is negative, it removes elements equal to `element` moving from tail to head.
2706
+ - If `count` is 0 or greater than the occurrences of elements equal to `element`, it removes all elements
2707
+
2567
2708
  element (TEncodable): The element to remove from the list.
2568
2709
 
2569
2710
  Returns:
@@ -6048,19 +6189,19 @@ class CoreCommands(Protocol):
6048
6189
  elements (List[TEncodable]): A list of members to add to the HyperLogLog stored at `key`.
6049
6190
 
6050
6191
  Returns:
6051
- int: If the HyperLogLog is newly created, or if the HyperLogLog approximated cardinality is
6052
- altered, then returns 1.
6192
+ bool: If the HyperLogLog is newly created, or if the HyperLogLog approximated cardinality is
6193
+ altered, then returns `True`.
6053
6194
 
6054
- Otherwise, returns 0.
6195
+ Otherwise, returns `False`.
6055
6196
 
6056
6197
  Examples:
6057
6198
  >>> await client.pfadd("hll_1", ["a", "b", "c" ])
6058
- 1 # A data structure was created or modified
6199
+ True # A data structure was created or modified
6059
6200
  >>> await client.pfadd("hll_2", [])
6060
- 1 # A new empty data structure was created
6201
+ True # A new empty data structure was created
6061
6202
  """
6062
6203
  return cast(
6063
- int,
6204
+ bool,
6064
6205
  await self._execute_command(RequestType.PfAdd, [key] + elements),
6065
6206
  )
6066
6207
 
@@ -6649,6 +6790,10 @@ class CoreCommands(Protocol):
6649
6790
  args.append("REPLACE")
6650
6791
  if absttl is True:
6651
6792
  args.append("ABSTTL")
6793
+ if idletime is not None and frequency is not None:
6794
+ raise RequestError(
6795
+ "syntax error: IDLETIME and FREQ cannot be set at the same time."
6796
+ )
6652
6797
  if idletime is not None:
6653
6798
  args.extend(["IDLETIME", str(idletime)])
6654
6799
  if frequency is not None:
@@ -6987,7 +7132,7 @@ class CoreCommands(Protocol):
6987
7132
  See [valkey.io](https://valkey.io/commands/watch) for more details.
6988
7133
 
6989
7134
  Note:
6990
- In cluster mode, if keys in `key_value_map` map to different hash slots,
7135
+ In cluster mode, if keys in `keys` map to different hash slots,
6991
7136
  the command will be split across these slots and executed separately for each.
6992
7137
  This means the command is atomic only at the slot level. If one or more slot-specific
6993
7138
  requests fail, the entire call will return the first encountered error, even
@@ -7006,7 +7151,7 @@ class CoreCommands(Protocol):
7006
7151
  'OK'
7007
7152
  >>> transaction.set("sampleKey", "foobar")
7008
7153
  >>> await client.exec(transaction)
7009
- 'OK' # Executes successfully and keys are unwatched.
7154
+ ['OK'] # Executes successfully and keys are unwatched.
7010
7155
 
7011
7156
  >>> await client.watch("sampleKey")
7012
7157
  'OK'
@@ -7022,21 +7167,6 @@ class CoreCommands(Protocol):
7022
7167
  await self._execute_command(RequestType.Watch, keys),
7023
7168
  )
7024
7169
 
7025
- @dataclass
7026
- class PubSubMsg:
7027
- """
7028
- Describes the incoming pubsub message
7029
-
7030
- Attributes:
7031
- message (TEncodable): Incoming message.
7032
- channel (TEncodable): Name of an channel that triggered the message.
7033
- pattern (Optional[TEncodable]): Pattern that triggered the message.
7034
- """
7035
-
7036
- message: TEncodable
7037
- channel: TEncodable
7038
- pattern: Optional[TEncodable]
7039
-
7040
7170
  async def get_pubsub_message(self) -> PubSubMsg:
7041
7171
  """
7042
7172
  Returns the next pubsub message.