valkey-glide 2.0.0rc5__cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl → 2.0.0rc6__cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.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.

@@ -1,9 +1,9 @@
1
1
  # Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0
2
2
 
3
+ import sys
3
4
  import threading
4
5
  from typing import List, Mapping, Optional, Tuple, TypeVar, Union
5
6
 
6
- from deprecated import deprecated
7
7
  from glide.async_commands.bitmap import (
8
8
  BitFieldGet,
9
9
  BitFieldSubCommands,
@@ -55,8 +55,14 @@ from glide.async_commands.stream import (
55
55
  _create_xpending_range_args,
56
56
  )
57
57
  from glide.constants import TEncodable
58
+ from glide.exceptions import RequestError
58
59
  from glide.protobuf.command_request_pb2 import RequestType
59
60
 
61
+ if sys.version_info >= (3, 13):
62
+ from warnings import deprecated
63
+ else:
64
+ from typing_extensions import deprecated
65
+
60
66
  TBatch = TypeVar("TBatch", bound="BaseBatch")
61
67
 
62
68
 
@@ -109,7 +115,7 @@ class BaseBatch:
109
115
 
110
116
  def get(self: TBatch, key: TEncodable) -> TBatch:
111
117
  """
112
- Get the value associated with the given key, or null if no such value exists.
118
+ Get the value associated with the given key, or null if no such key exists.
113
119
 
114
120
  See [valkey.io](https://valkey.io/commands/get/) for details.
115
121
 
@@ -273,6 +279,10 @@ class BaseBatch:
273
279
  [custom command](https://github.com/valkey-io/valkey-glide/wiki/General-Concepts#custom-command)
274
280
  for details on the restrictions and limitations of the custom command API.
275
281
 
282
+ This function should only be used for single-response commands. Commands that don't return complete response and awaits
283
+ (such as SUBSCRIBE), or that return potentially more than a single response (such as XREAD), or that change the
284
+ client's behavior (such as entering pub/sub mode on RESP2 connections) shouldn't be called using this function.
285
+
276
286
  Args:
277
287
  command_args (List[TEncodable]): List of command arguments.
278
288
  Every part of the command, including the command name and subcommands, should be added as a
@@ -312,6 +322,8 @@ class BaseBatch:
312
322
  """
313
323
  Get information and statistics about the server.
314
324
 
325
+ Starting from server version 7, command supports multiple section arguments.
326
+
315
327
  See [valkey.io](https://valkey.io/commands/info/) for details.
316
328
 
317
329
  Args:
@@ -1057,7 +1069,7 @@ class BaseBatch:
1057
1069
  Command response:
1058
1070
  Optional[Mapping[bytes, List[bytes]]]: A map of `key` name mapped to an array of popped elements.
1059
1071
 
1060
- None if no elements could be popped.
1072
+ `None` if no elements could be popped.
1061
1073
 
1062
1074
  Since: Valkey version 7.0.0.
1063
1075
  """
@@ -2306,6 +2318,10 @@ class BaseBatch:
2306
2318
  args.append("REPLACE")
2307
2319
  if absttl is True:
2308
2320
  args.append("ABSTTL")
2321
+ if idletime is not None and frequency is not None:
2322
+ raise RequestError(
2323
+ "syntax error: IDLETIME and FREQ cannot be set at the same time."
2324
+ )
2309
2325
  if idletime is not None:
2310
2326
  args.extend(["IDLETIME", str(idletime)])
2311
2327
  if frequency is not None:
@@ -2412,26 +2428,26 @@ class BaseBatch:
2412
2428
 
2413
2429
  Args:
2414
2430
  key (TEncodable): The key of the stream.
2415
- start (StreamRangeBound): The starting stream ID bound for the range.
2431
+ start (StreamRangeBound): The starting stream entry ID bound for the range.
2416
2432
 
2417
- - Use `IdBound` to specify a stream ID.
2418
- - Use `ExclusiveIdBound` to specify an exclusive bounded stream ID.
2433
+ - Use `IdBound` to specify a stream entry ID.
2434
+ - Since Valkey 6.2.0, use `ExclusiveIdBound` to specify an exclusive bounded stream entry ID.
2419
2435
  - Use `MinId` to start with the minimum available ID.
2420
2436
 
2421
- end (StreamRangeBound): The ending stream ID bound for the range.
2437
+ end (StreamRangeBound): The ending stream entry ID bound for the range.
2422
2438
 
2423
- - Use `IdBound` to specify a stream ID.
2424
- - Use `ExclusiveIdBound` to specify an exclusive bounded stream ID.
2439
+ - Use `IdBound` to specify a stream entry ID.
2440
+ - Since Valkey 6.2.0, use `ExclusiveIdBound` to specify an exclusive bounded stream entry ID.
2425
2441
  - Use `MaxId` to end with the maximum available ID.
2426
2442
 
2427
2443
  count (Optional[int]): An optional argument specifying the maximum count of stream entries to return.
2428
2444
  If `count` is not provided, all stream entries in the range will be returned.
2429
2445
 
2430
2446
  Command response:
2431
- Optional[Mapping[bytes, List[List[bytes]]]]: A mapping of stream IDs to stream entry data, where entry data is a
2447
+ Optional[Mapping[bytes, List[List[bytes]]]]: A mapping of stream entry IDs to stream entry data, where entry data is a
2432
2448
  list of pairings with format `[[field, entry], [field, entry], ...]`.
2433
2449
 
2434
- Returns None if the range arguments are not applicable.
2450
+ Returns None if the range arguments are not applicable. Or if count is non-positive.
2435
2451
  """
2436
2452
  args = [key, start.to_arg(), end.to_arg()]
2437
2453
  if count is not None:
@@ -2454,26 +2470,26 @@ class BaseBatch:
2454
2470
 
2455
2471
  Args:
2456
2472
  key (TEncodable): The key of the stream.
2457
- end (StreamRangeBound): The ending stream ID bound for the range.
2473
+ end (StreamRangeBound): The ending stream entry ID bound for the range.
2458
2474
 
2459
- - Use `IdBound` to specify a stream ID.
2460
- - Use `ExclusiveIdBound` to specify an exclusive bounded stream ID.
2475
+ - Use `IdBound` to specify a stream entry ID.
2476
+ - Since Valkey 6.2.0, use `ExclusiveIdBound` to specify an exclusive bounded stream entry ID.
2461
2477
  - Use `MaxId` to end with the maximum available ID.
2462
2478
 
2463
- start (StreamRangeBound): The starting stream ID bound for the range.
2479
+ start (StreamRangeBound): The starting stream entry ID bound for the range.
2464
2480
 
2465
- - Use `IdBound` to specify a stream ID.
2466
- - Use `ExclusiveIdBound` to specify an exclusive bounded stream ID.
2481
+ - Use `IdBound` to specify a stream entry ID.
2482
+ - Since Valkey 6.2.0, use `ExclusiveIdBound` to specify an exclusive bounded stream entry ID.
2467
2483
  - Use `MinId` to start with the minimum available ID.
2468
2484
 
2469
2485
  count (Optional[int]): An optional argument specifying the maximum count of stream entries to return.
2470
2486
  If `count` is not provided, all stream entries in the range will be returned.
2471
2487
 
2472
2488
  Command response:
2473
- Optional[Mapping[bytes, List[List[bytes]]]]: A mapping of stream IDs to stream entry data, where entry data is a
2489
+ Optional[Mapping[bytes, List[List[bytes]]]]: A mapping of stream entry IDs to stream entry data, where entry data is a
2474
2490
  list of pairings with format `[[field, entry], [field, entry], ...]`.
2475
2491
 
2476
- Returns None if the range arguments are not applicable.
2492
+ Returns None if the range arguments are not applicable. Or if count is non-positive.
2477
2493
  """
2478
2494
  args = [key, end.to_arg(), start.to_arg()]
2479
2495
  if count is not None:
@@ -2787,7 +2803,7 @@ class BaseBatch:
2787
2803
  min_idle_time_ms (int): Filters the claimed entries to those that have been idle for more than the specified
2788
2804
  value.
2789
2805
  start (TEncodable): Filters the claimed entries to those that have an ID equal or greater than the specified value.
2790
- count (Optional[int]): Limits the number of claimed entries to the specified value.
2806
+ count (Optional[int]): Limits the number of claimed entries to the specified value. Default value is 100.
2791
2807
 
2792
2808
  Command response:
2793
2809
  List[Union[str, Mapping[bytes, List[List[bytes]]], List[bytes]]]: A list containing the following elements:
@@ -2832,7 +2848,7 @@ class BaseBatch:
2832
2848
  min_idle_time_ms (int): Filters the claimed entries to those that have been idle for more than the specified
2833
2849
  value.
2834
2850
  start (TEncodable): Filters the claimed entries to those that have an ID equal or greater than the specified value.
2835
- count (Optional[int]): Limits the number of claimed entries to the specified value.
2851
+ count (Optional[int]): Limits the number of claimed entries to the specified value. Default value is 100.
2836
2852
 
2837
2853
  Command response:
2838
2854
  List[Union[bytes, List[bytes]]]: A list containing the following elements:
@@ -3263,7 +3279,7 @@ class BaseBatch:
3263
3279
  Commands response:
3264
3280
  Optional[float]: The score of the member.
3265
3281
 
3266
- If there was a conflict with choosing the XX/NX/LT/GT options, the operation aborts and None is returned.
3282
+ If there was a conflict with choosing the XX/NX/LT/GT options, the operation aborts and `None` is returned.
3267
3283
  """
3268
3284
  args = [key]
3269
3285
  if existing_options:
@@ -5573,13 +5589,13 @@ class ClusterBatch(BaseBatch):
5573
5589
  # TODO: add all CLUSTER commands
5574
5590
 
5575
5591
 
5576
- @deprecated(reason="Use ClusterBatch(is_atomic=True) instead.")
5592
+ @deprecated("Use ClusterBatch(is_atomic=True) instead.")
5577
5593
  class Transaction(Batch):
5578
5594
  def __init__(self):
5579
5595
  super().__init__(is_atomic=True)
5580
5596
 
5581
5597
 
5582
- @deprecated(reason="Use ClusterBatch(is_atomic=True) instead.")
5598
+ @deprecated("Use ClusterBatch(is_atomic=True) instead.")
5583
5599
  class ClusterTransaction(ClusterBatch):
5584
5600
  def __init__(self):
5585
5601
  super().__init__(is_atomic=True)
@@ -36,9 +36,13 @@ class ClusterCommands(CoreCommands):
36
36
  See the [Valkey GLIDE Wiki](https://github.com/valkey-io/valkey-glide/wiki/General-Concepts#custom-command)
37
37
  for details on the restrictions and limitations of the custom command API.
38
38
 
39
+ This function should only be used for single-response commands. Commands that don't return complete response and awaits
40
+ (such as SUBSCRIBE), or that return potentially more than a single response (such as XREAD), or that change the
41
+ client's behavior (such as entering pub/sub mode on RESP2 connections) shouldn't be called using this function.
42
+
39
43
  For example - Return a list of all pub/sub clients from all nodes::
40
44
 
41
- connection.customCommand(["CLIENT", "LIST","TYPE", "PUBSUB"], AllNodes())
45
+ await client.customCommand(["CLIENT", "LIST","TYPE", "PUBSUB"], AllNodes())
42
46
 
43
47
  Args:
44
48
  command_args (List[TEncodable]): List of the command's arguments, where each argument is either a string or bytes.
@@ -63,6 +67,8 @@ class ClusterCommands(CoreCommands):
63
67
  """
64
68
  Get information and statistics about the server.
65
69
 
70
+ Starting from server version 7, command supports multiple section arguments.
71
+
66
72
  See [valkey.io](https://valkey.io/commands/info/) for details.
67
73
 
68
74
  Args:
@@ -61,6 +61,7 @@ from glide.constants import (
61
61
  TXInfoStreamFullResponse,
62
62
  TXInfoStreamResponse,
63
63
  )
64
+ from glide.exceptions import RequestError
64
65
  from glide.protobuf.command_request_pb2 import RequestType
65
66
  from glide.routes import Route
66
67
 
@@ -593,7 +594,7 @@ class CoreCommands(Protocol):
593
594
 
594
595
  async def get(self, key: TEncodable) -> Optional[bytes]:
595
596
  """
596
- Get the value associated with the given key, or null if no such value exists.
597
+ Get the value associated with the given key, or null if no such key exists.
597
598
 
598
599
  See [valkey.io](https://valkey.io/commands/get/) for details.
599
600
 
@@ -3114,26 +3115,26 @@ class CoreCommands(Protocol):
3114
3115
 
3115
3116
  Args:
3116
3117
  key (TEncodable): The key of the stream.
3117
- start (StreamRangeBound): The starting stream ID bound for the range.
3118
+ start (StreamRangeBound): The starting stream entry ID bound for the range.
3118
3119
 
3119
- - Use `IdBound` to specify a stream ID.
3120
- - Use `ExclusiveIdBound` to specify an exclusive bounded stream ID.
3120
+ - Use `IdBound` to specify a stream entry ID.
3121
+ - Since Valkey 6.2.0, use `ExclusiveIdBound` to specify an exclusive bounded stream entry ID.
3121
3122
  - Use `MinId` to start with the minimum available ID.
3122
3123
 
3123
- end (StreamRangeBound): The ending stream ID bound for the range.
3124
+ end (StreamRangeBound): The ending stream entry ID bound for the range.
3124
3125
 
3125
- - Use `IdBound` to specify a stream ID.
3126
- - Use `ExclusiveIdBound` to specify an exclusive bounded stream ID.
3126
+ - Use `IdBound` to specify a stream entry ID.
3127
+ - Since Valkey 6.2.0, use `ExclusiveIdBound` to specify an exclusive bounded stream entry ID.
3127
3128
  - Use `MaxId` to end with the maximum available ID.
3128
3129
 
3129
3130
  count (Optional[int]): An optional argument specifying the maximum count of stream entries to return.
3130
3131
  If `count` is not provided, all stream entries in the range will be returned.
3131
3132
 
3132
3133
  Returns:
3133
- Optional[Mapping[bytes, List[List[bytes]]]]: A mapping of stream IDs to stream entry data, where entry data is a
3134
+ Optional[Mapping[bytes, List[List[bytes]]]]: A mapping of stream entry IDs to stream entry data, where entry data is a
3134
3135
  list of pairings with format `[[field, entry], [field, entry], ...]`.
3135
3136
 
3136
- Returns None if the range arguments are not applicable.
3137
+ Returns None if the range arguments are not applicable. Or if count is non-positive.
3137
3138
 
3138
3139
  Examples:
3139
3140
  >>> await client.xadd("mystream", [("field1", "value1")], StreamAddOptions(id="0-1"))
@@ -3168,26 +3169,26 @@ class CoreCommands(Protocol):
3168
3169
 
3169
3170
  Args:
3170
3171
  key (TEncodable): The key of the stream.
3171
- end (StreamRangeBound): The ending stream ID bound for the range.
3172
+ end (StreamRangeBound): The ending stream entry ID bound for the range.
3172
3173
 
3173
- - Use `IdBound` to specify a stream ID.
3174
- - Use `ExclusiveIdBound` to specify an exclusive bounded stream ID.
3174
+ - Use `IdBound` to specify a stream entry ID.
3175
+ - Since Valkey 6.2.0, use `ExclusiveIdBound` to specify an exclusive bounded stream entry ID.
3175
3176
  - Use `MaxId` to end with the maximum available ID.
3176
3177
 
3177
- start (StreamRangeBound): The starting stream ID bound for the range.
3178
+ start (StreamRangeBound): The starting stream entry ID bound for the range.
3178
3179
 
3179
- - Use `IdBound` to specify a stream ID.
3180
- - Use `ExclusiveIdBound` to specify an exclusive bounded stream ID.
3180
+ - Use `IdBound` to specify a stream entry ID.
3181
+ - Since Valkey 6.2.0, use `ExclusiveIdBound` to specify an exclusive bounded stream entry ID.
3181
3182
  - Use `MinId` to start with the minimum available ID.
3182
3183
 
3183
3184
  count (Optional[int]): An optional argument specifying the maximum count of stream entries to return.
3184
3185
  If `count` is not provided, all stream entries in the range will be returned.
3185
3186
 
3186
3187
  Returns:
3187
- Optional[Mapping[bytes, List[List[bytes]]]]: A mapping of stream IDs to stream entry data, where entry data is a
3188
+ Optional[Mapping[bytes, List[List[bytes]]]]: A mapping of stream entry IDs to stream entry data, where entry data is a
3188
3189
  list of pairings with format `[[field, entry], [field, entry], ...]`.
3189
3190
 
3190
- Returns None if the range arguments are not applicable.
3191
+ Returns None if the range arguments are not applicable. Or if count is non-positive.
3191
3192
 
3192
3193
  Examples:
3193
3194
  >>> await client.xadd("mystream", [("field1", "value1")], StreamAddOptions(id="0-1"))
@@ -3273,7 +3274,7 @@ class CoreCommands(Protocol):
3273
3274
  key (TEncodable): The key of the stream.
3274
3275
  group_name (TEncodable): The newly created consumer group name.
3275
3276
  group_id (TEncodable): The stream entry ID that specifies the last delivered entry in the stream from the new
3276
- groups perspective. The special ID "$" can be used to specify the last entry in the stream.
3277
+ group's perspective. The special ID "$" can be used to specify the last entry in the stream.
3277
3278
  options (Optional[StreamGroupOptions]): Options for creating the stream group.
3278
3279
 
3279
3280
  Returns:
@@ -3733,7 +3734,7 @@ class CoreCommands(Protocol):
3733
3734
  min_idle_time_ms (int): Filters the claimed entries to those that have been idle for more than the specified
3734
3735
  value.
3735
3736
  start (TEncodable): Filters the claimed entries to those that have an ID equal or greater than the specified value.
3736
- count (Optional[int]): Limits the number of claimed entries to the specified value.
3737
+ count (Optional[int]): Limits the number of claimed entries to the specified value. Default value is 100.
3737
3738
 
3738
3739
  Returns:
3739
3740
  List[Union[bytes, Mapping[bytes, List[List[bytes]]], List[bytes]]]: A list containing the following elements:
@@ -3820,7 +3821,7 @@ class CoreCommands(Protocol):
3820
3821
  min_idle_time_ms (int): Filters the claimed entries to those that have been idle for more than the specified
3821
3822
  value.
3822
3823
  start (TEncodable): Filters the claimed entries to those that have an ID equal or greater than the specified value.
3823
- count (Optional[int]): Limits the number of claimed entries to the specified value.
3824
+ count (Optional[int]): Limits the number of claimed entries to the specified value. Default value is 100.
3824
3825
 
3825
3826
  Returns:
3826
3827
  List[Union[bytes, List[bytes]]]: A list containing the following elements:
@@ -6649,6 +6650,10 @@ class CoreCommands(Protocol):
6649
6650
  args.append("REPLACE")
6650
6651
  if absttl is True:
6651
6652
  args.append("ABSTTL")
6653
+ if idletime is not None and frequency is not None:
6654
+ raise RequestError(
6655
+ "syntax error: IDLETIME and FREQ cannot be set at the same time."
6656
+ )
6652
6657
  if idletime is not None:
6653
6658
  args.extend(["IDLETIME", str(idletime)])
6654
6659
  if frequency is not None:
@@ -31,6 +31,10 @@ class StandaloneCommands(CoreCommands):
31
31
  See the [Valkey GLIDE Wiki](https://github.com/valkey-io/valkey-glide/wiki/General-Concepts#custom-command)
32
32
  for details on the restrictions and limitations of the custom command API.
33
33
 
34
+ This function should only be used for single-response commands. Commands that don't return complete response and awaits
35
+ (such as SUBSCRIBE), or that return potentially more than a single response (such as XREAD), or that change the
36
+ client's behavior (such as entering pub/sub mode on RESP2 connections) shouldn't be called using this function.
37
+
34
38
  Args:
35
39
  command_args (List[TEncodable]): List of the command's arguments, where each argument is either a string or bytes.
36
40
  Every part of the command, including the command name and subcommands, should be added as a separate value in args.
@@ -39,7 +43,8 @@ class StandaloneCommands(CoreCommands):
39
43
  TResult: The returning value depends on the executed command.
40
44
 
41
45
  Example:
42
- >>> connection.customCommand(["CLIENT", "LIST","TYPE", "PUBSUB"])
46
+ >>> await client.customCommand(["CLIENT", "LIST", "TYPE", "PUBSUB"])
47
+ # Expected Output: A list of all pub/sub clients
43
48
 
44
49
  """
45
50
  return await self._execute_command(RequestType.CustomCommand, command_args)
@@ -51,6 +56,8 @@ class StandaloneCommands(CoreCommands):
51
56
  """
52
57
  Get information and statistics about the server.
53
58
 
59
+ Starting from server version 7, command supports multiple section arguments.
60
+
54
61
  See [valkey.io](https://valkey.io/commands/info/) for details.
55
62
 
56
63
  Args:
glide/glide_client.py CHANGED
@@ -1,9 +1,24 @@
1
1
  # Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0
2
2
 
3
- import asyncio
4
3
  import sys
5
4
  import threading
6
- from typing import Any, Dict, List, Optional, Tuple, Type, Union, cast
5
+ from typing import (
6
+ TYPE_CHECKING,
7
+ Any,
8
+ Awaitable,
9
+ Dict,
10
+ List,
11
+ Optional,
12
+ Set,
13
+ Tuple,
14
+ Type,
15
+ Union,
16
+ cast,
17
+ )
18
+
19
+ import anyio
20
+ import sniffio
21
+ from anyio import to_thread
7
22
 
8
23
  from glide.async_commands.cluster_commands import ClusterCommands
9
24
  from glide.async_commands.command_args import ObjectType
@@ -38,12 +53,18 @@ from .glide import (
38
53
  )
39
54
 
40
55
  if sys.version_info >= (3, 11):
41
- import asyncio as async_timeout
42
56
  from typing import Self
43
57
  else:
44
- import async_timeout
45
58
  from typing_extensions import Self
46
59
 
60
+ if TYPE_CHECKING:
61
+ import asyncio
62
+
63
+ import trio
64
+
65
+ TTask = Union[asyncio.Task[None], trio.lowlevel.Task]
66
+ TFuture = Union[asyncio.Future[Any], "_CompatFuture"]
67
+
47
68
 
48
69
  def get_request_error_class(
49
70
  error_type: Optional[RequestErrorType.ValueType],
@@ -59,23 +80,94 @@ def get_request_error_class(
59
80
  return RequestError
60
81
 
61
82
 
83
+ class _CompatFuture:
84
+ """anyio shim for asyncio.Future-like functionality"""
85
+
86
+ def __init__(self) -> None:
87
+ self._is_done = anyio.Event()
88
+ self._result: Any = None
89
+ self._exception: Optional[Exception] = None
90
+
91
+ def set_result(self, result: Any) -> None:
92
+ self._result = result
93
+ self._is_done.set()
94
+
95
+ def set_exception(self, exception: Exception) -> None:
96
+ self._exception = exception
97
+ self._is_done.set()
98
+
99
+ def done(self) -> bool:
100
+ return self._is_done.is_set()
101
+
102
+ def __await__(self):
103
+ return self._is_done.wait().__await__()
104
+
105
+ def result(self) -> Any:
106
+ if self._exception:
107
+ raise self._exception
108
+
109
+ return self._result
110
+
111
+
112
+ def _get_new_future_instance() -> "TFuture":
113
+ if sniffio.current_async_library() == "asyncio":
114
+ import asyncio
115
+
116
+ return asyncio.get_running_loop().create_future()
117
+
118
+ # _CompatFuture is also compatible with asyncio, but is not as closely integrated
119
+ # into the asyncio event loop and thus introduces a noticeable performance
120
+ # degradation. so we only use it for trio
121
+ return _CompatFuture()
122
+
123
+
62
124
  class BaseClient(CoreCommands):
63
125
  def __init__(self, config: BaseClientConfiguration):
64
126
  """
65
127
  To create a new client, use the `create` classmethod
66
128
  """
67
129
  self.config: BaseClientConfiguration = config
68
- self._available_futures: Dict[int, asyncio.Future] = {}
130
+ self._available_futures: Dict[int, "TFuture"] = {}
69
131
  self._available_callback_indexes: List[int] = list()
70
132
  self._buffered_requests: List[TRequest] = list()
71
133
  self._writer_lock = threading.Lock()
72
134
  self.socket_path: Optional[str] = None
73
- self._reader_task: Optional[asyncio.Task] = None
135
+ self._reader_task: Optional["TTask"] = None
74
136
  self._is_closed: bool = False
75
- self._pubsub_futures: List[asyncio.Future] = []
137
+ self._pubsub_futures: List["TFuture"] = []
76
138
  self._pubsub_lock = threading.Lock()
77
139
  self._pending_push_notifications: List[Response] = list()
78
140
 
141
+ self._pending_tasks: Optional[Set[Awaitable[None]]] = None
142
+ """asyncio-only to avoid gc on pending write tasks"""
143
+
144
+ def _create_task(self, task, *args, **kwargs):
145
+ """framework agnostic free-floating task shim"""
146
+ framework = sniffio.current_async_library()
147
+ if framework == "trio":
148
+ from functools import partial
149
+
150
+ import trio
151
+
152
+ return trio.lowlevel.spawn_system_task(partial(task, **kwargs), *args)
153
+ elif framework == "asyncio":
154
+ import asyncio
155
+
156
+ # the asyncio event loop holds weak refs to tasks, so it's recommended to
157
+ # hold strong refs to them during their lifetime to prevent garbage
158
+ # collection
159
+ t = asyncio.create_task(task(*args, **kwargs))
160
+
161
+ if self._pending_tasks is None:
162
+ self._pending_tasks = set()
163
+
164
+ self._pending_tasks.add(t)
165
+ t.add_done_callback(self._pending_tasks.discard)
166
+
167
+ return t
168
+
169
+ raise RuntimeError(f"Unsupported async framework {framework}")
170
+
79
171
  @classmethod
80
172
  async def create(cls, config: BaseClientConfiguration) -> Self:
81
173
  """Creates a Glide client.
@@ -148,8 +240,8 @@ class BaseClient(CoreCommands):
148
240
  """
149
241
  config = config
150
242
  self = cls(config)
151
- init_future: asyncio.Future = asyncio.Future()
152
- loop = asyncio.get_event_loop()
243
+
244
+ init_event: threading.Event = threading.Event()
153
245
 
154
246
  def init_callback(socket_path: Optional[str], err: Optional[str]):
155
247
  if err is not None:
@@ -161,7 +253,7 @@ class BaseClient(CoreCommands):
161
253
  else:
162
254
  # Received socket path
163
255
  self.socket_path = socket_path
164
- loop.call_soon_threadsafe(init_future.set_result, True)
256
+ init_event.set()
165
257
 
166
258
  start_socket_listener_external(init_callback=init_callback)
167
259
 
@@ -169,36 +261,27 @@ class BaseClient(CoreCommands):
169
261
  # level or higher
170
262
  ClientLogger.log(LogLevel.INFO, "connection info", "new connection established")
171
263
  # Wait for the socket listener to complete its initialization
172
- await init_future
264
+ await to_thread.run_sync(init_event.wait)
173
265
  # Create UDS connection
174
266
  await self._create_uds_connection()
267
+
175
268
  # Start the reader loop as a background task
176
- self._reader_task = asyncio.create_task(self._reader_loop())
269
+ self._reader_task = self._create_task(self._reader_loop)
270
+
177
271
  # Set the client configurations
178
272
  await self._set_connection_configurations()
273
+
179
274
  return self
180
275
 
181
276
  async def _create_uds_connection(self) -> None:
182
277
  try:
183
278
  # Open an UDS connection
184
- async with async_timeout.timeout(DEFAULT_TIMEOUT_IN_MILLISECONDS):
185
- reader, writer = await asyncio.open_unix_connection(
186
- path=self.socket_path
279
+ with anyio.fail_after(DEFAULT_TIMEOUT_IN_MILLISECONDS):
280
+ self._stream = await anyio.connect_unix(
281
+ path=cast(str, self.socket_path)
187
282
  )
188
- self._reader = reader
189
- self._writer = writer
190
283
  except Exception as e:
191
- await self.close(f"Failed to create UDS connection: {e}")
192
- raise
193
-
194
- def __del__(self) -> None:
195
- try:
196
- if self._reader_task:
197
- self._reader_task.cancel()
198
- except RuntimeError as e:
199
- if "no running event loop" in str(e):
200
- # event loop already closed
201
- pass
284
+ raise ClosingError("Failed to create UDS connection") from e
202
285
 
203
286
  async def close(self, err_message: Optional[str] = None) -> None:
204
287
  """
@@ -210,25 +293,24 @@ class BaseClient(CoreCommands):
210
293
  closing all open futures.
211
294
  Defaults to None.
212
295
  """
213
- self._is_closed = True
214
- for response_future in self._available_futures.values():
215
- if not response_future.done():
216
- err_message = "" if err_message is None else err_message
217
- response_future.set_exception(ClosingError(err_message))
218
- try:
219
- self._pubsub_lock.acquire()
220
- for pubsub_future in self._pubsub_futures:
221
- if not pubsub_future.done() and not pubsub_future.cancelled():
222
- pubsub_future.set_exception(ClosingError(""))
223
- finally:
224
- self._pubsub_lock.release()
296
+ if not self._is_closed:
297
+ self._is_closed = True
298
+ err_message = "" if err_message is None else err_message
299
+ for response_future in self._available_futures.values():
300
+ if not response_future.done():
301
+ response_future.set_exception(ClosingError(err_message))
302
+ try:
303
+ self._pubsub_lock.acquire()
304
+ for pubsub_future in self._pubsub_futures:
305
+ if not pubsub_future.done():
306
+ pubsub_future.set_exception(ClosingError(err_message))
307
+ finally:
308
+ self._pubsub_lock.release()
225
309
 
226
- self._writer.close()
227
- await self._writer.wait_closed()
228
- self.__del__()
310
+ await self._stream.aclose()
229
311
 
230
- def _get_future(self, callback_idx: int) -> asyncio.Future:
231
- response_future: asyncio.Future = asyncio.Future()
312
+ def _get_future(self, callback_idx: int) -> "TFuture":
313
+ response_future: "TFuture" = _get_new_future_instance()
232
314
  self._available_futures.update({callback_idx: response_future})
233
315
  return response_future
234
316
 
@@ -237,14 +319,15 @@ class BaseClient(CoreCommands):
237
319
 
238
320
  async def _set_connection_configurations(self) -> None:
239
321
  conn_request = self._get_protobuf_conn_request()
240
- response_future: asyncio.Future = self._get_future(0)
241
- await self._write_or_buffer_request(conn_request)
322
+ response_future: "TFuture" = self._get_future(0)
323
+ self._create_write_task(conn_request)
242
324
  await response_future
243
- if response_future.result() is not OK:
244
- raise ClosingError(response_future.result())
325
+ res = response_future.result()
326
+ if res is not OK:
327
+ raise ClosingError(res)
245
328
 
246
329
  def _create_write_task(self, request: TRequest):
247
- asyncio.create_task(self._write_or_buffer_request(request))
330
+ self._create_task(self._write_or_buffer_request, request)
248
331
 
249
332
  async def _write_or_buffer_request(self, request: TRequest):
250
333
  self._buffered_requests.append(request)
@@ -252,7 +335,21 @@ class BaseClient(CoreCommands):
252
335
  try:
253
336
  while len(self._buffered_requests) > 0:
254
337
  await self._write_buffered_requests_to_socket()
255
-
338
+ except Exception as e:
339
+ # trio system tasks cannot raise exceptions, so gracefully propagate
340
+ # any error to the pending future instead
341
+ callback_idx = (
342
+ request.callback_idx if isinstance(request, CommandRequest) else 0
343
+ )
344
+ res_future = self._available_futures.pop(callback_idx, None)
345
+ if res_future:
346
+ res_future.set_exception(e)
347
+ else:
348
+ ClientLogger.log(
349
+ LogLevel.WARN,
350
+ "unhandled response error",
351
+ f"Unhandled response error for unknown request: {callback_idx}",
352
+ )
256
353
  finally:
257
354
  self._writer_lock.release()
258
355
 
@@ -262,8 +359,7 @@ class BaseClient(CoreCommands):
262
359
  b_arr = bytearray()
263
360
  for request in requests:
264
361
  ProtobufCodec.encode_delimited(b_arr, request)
265
- self._writer.write(b_arr)
266
- await self._writer.drain()
362
+ await self._stream.send(b_arr)
267
363
 
268
364
  def _encode_arg(self, arg: TEncodable) -> bytes:
269
365
  """
@@ -301,7 +397,7 @@ class BaseClient(CoreCommands):
301
397
  for arg in args_list:
302
398
  encoded_arg = self._encode_arg(arg) if isinstance(arg, str) else arg
303
399
  encoded_args_list.append(encoded_arg)
304
- args_size += len(encoded_arg)
400
+ args_size += sys.getsizeof(encoded_arg)
305
401
  return (encoded_args_list, args_size)
306
402
 
307
403
  async def _execute_command(
@@ -417,14 +513,15 @@ class BaseClient(CoreCommands):
417
513
  )
418
514
 
419
515
  # locking might not be required
420
- response_future: asyncio.Future = asyncio.Future()
516
+ response_future: "TFuture" = _get_new_future_instance()
421
517
  try:
422
518
  self._pubsub_lock.acquire()
423
519
  self._pubsub_futures.append(response_future)
424
520
  self._complete_pubsub_futures_safe()
425
521
  finally:
426
522
  self._pubsub_lock.release()
427
- return await response_future
523
+ await response_future
524
+ return response_future.result()
428
525
 
429
526
  def try_get_pubsub_message(self) -> Optional[CoreCommands.PubSubMsg]:
430
527
  if self._is_closed:
@@ -457,8 +554,7 @@ class BaseClient(CoreCommands):
457
554
  def _cancel_pubsub_futures_with_exception_safe(self, exception: ConnectionError):
458
555
  while len(self._pubsub_futures):
459
556
  next_future = self._pubsub_futures.pop(0)
460
- if not next_future.cancelled():
461
- next_future.set_exception(exception)
557
+ next_future.set_exception(exception)
462
558
 
463
559
  def _notification_to_pubsub_message_safe(
464
560
  self, response: Response
@@ -538,10 +634,16 @@ class BaseClient(CoreCommands):
538
634
  if response.HasField("closing_error")
539
635
  else f"Client Error - closing due to unknown error. callback index: {response.callback_idx}"
540
636
  )
637
+ exc = ClosingError(err_msg)
541
638
  if res_future is not None:
542
- res_future.set_exception(ClosingError(err_msg))
543
- await self.close(err_msg)
544
- raise ClosingError(err_msg)
639
+ res_future.set_exception(exc)
640
+ else:
641
+ ClientLogger.log(
642
+ LogLevel.WARN,
643
+ "unhandled response error",
644
+ f"Unhandled response error for unknown request: {response.callback_idx}",
645
+ )
646
+ raise exc
545
647
  else:
546
648
  self._available_callback_indexes.append(response.callback_idx)
547
649
  if response.HasField("request_error"):
@@ -561,9 +663,7 @@ class BaseClient(CoreCommands):
561
663
  if response.HasField("closing_error")
562
664
  else "Client Error - push notification without resp_pointer"
563
665
  )
564
- await self.close(err_msg)
565
666
  raise ClosingError(err_msg)
566
-
567
667
  try:
568
668
  self._pubsub_lock.acquire()
569
669
  callback, context = self.config._get_pubsub_callback_and_context()
@@ -579,30 +679,36 @@ class BaseClient(CoreCommands):
579
679
 
580
680
  async def _reader_loop(self) -> None:
581
681
  # Socket reader loop
582
- remaining_read_bytes = bytearray()
583
- while True:
584
- read_bytes = await self._reader.read(DEFAULT_READ_BYTES_SIZE)
585
- if len(read_bytes) == 0:
586
- err_msg = "The communication layer was unexpectedly closed."
587
- await self.close(err_msg)
588
- raise ClosingError(err_msg)
589
- read_bytes = remaining_read_bytes + bytearray(read_bytes)
590
- read_bytes_view = memoryview(read_bytes)
591
- offset = 0
592
- while offset <= len(read_bytes):
682
+ try:
683
+ remaining_read_bytes = bytearray()
684
+ while True:
593
685
  try:
594
- response, offset = ProtobufCodec.decode_delimited(
595
- read_bytes, read_bytes_view, offset, Response
686
+ read_bytes = await self._stream.receive(DEFAULT_READ_BYTES_SIZE)
687
+ except (anyio.ClosedResourceError, anyio.EndOfStream):
688
+ raise ClosingError(
689
+ "The communication layer was unexpectedly closed."
596
690
  )
597
- except PartialMessageException:
598
- # Received only partial response, break the inner loop
599
- remaining_read_bytes = read_bytes[offset:]
600
- break
601
- response = cast(Response, response)
602
- if response.is_push:
603
- await self._process_push(response=response)
604
- else:
605
- await self._process_response(response=response)
691
+ read_bytes = remaining_read_bytes + bytearray(read_bytes)
692
+ read_bytes_view = memoryview(read_bytes)
693
+ offset = 0
694
+ while offset <= len(read_bytes):
695
+ try:
696
+ response, offset = ProtobufCodec.decode_delimited(
697
+ read_bytes, read_bytes_view, offset, Response
698
+ )
699
+ except PartialMessageException:
700
+ # Received only partial response, break the inner loop
701
+ remaining_read_bytes = read_bytes[offset:]
702
+ break
703
+ response = cast(Response, response)
704
+ if response.is_push:
705
+ await self._process_push(response=response)
706
+ else:
707
+ await self._process_response(response=response)
708
+ except Exception as e:
709
+ # close and stop reading at terminal exceptions from incoming responses or
710
+ # stream closures
711
+ await self.close(str(e))
606
712
 
607
713
  async def get_statistics(self) -> dict:
608
714
  return get_statistics()
glide/routes.py CHANGED
@@ -10,9 +10,14 @@ from glide.protobuf.command_request_pb2 import SlotTypes as ProtoSlotTypes
10
10
 
11
11
  class SlotType(Enum):
12
12
  PRIMARY = 1
13
+ """
14
+ Address a primary node.
15
+ """
13
16
 
14
17
  REPLICA = 2
15
18
  """
19
+ Address a replica node.
20
+
16
21
  `REPLICA` overrides the `read_from_replica` configuration. If it's used the request
17
22
  will be routed to a replica, even if the strategy is `ALWAYS_FROM_MASTER`.
18
23
  """
@@ -38,6 +43,10 @@ class AllNodes(Route):
38
43
 
39
44
 
40
45
  class AllPrimaries(Route):
46
+ """
47
+ Route request to all primary nodes.
48
+ """
49
+
41
50
  pass
42
51
 
43
52
 
@@ -53,6 +62,13 @@ class RandomNode(Route):
53
62
 
54
63
 
55
64
  class SlotKeyRoute(Route):
65
+ """Routes a request to a node by its slot key
66
+
67
+ Attributes:
68
+ slot_type (SlotType): Defines type of the node being addressed.
69
+ slot_key (str): The request will be sent to nodes managing this key.
70
+ """
71
+
56
72
  def __init__(self, slot_type: SlotType, slot_key: str) -> None:
57
73
  super().__init__()
58
74
  self.slot_type = slot_type
@@ -60,6 +76,15 @@ class SlotKeyRoute(Route):
60
76
 
61
77
 
62
78
  class SlotIdRoute(Route):
79
+ """Routes a request to a node by its slot ID
80
+
81
+ Attributes:
82
+ slot_type (SlotType): Defines type of the node being addressed.
83
+ slot_id (int): Slot number. There are 16384 slots in a Valkey cluster, and each shard
84
+ manages a slot range. Unless the slot is known, it's better to route using
85
+ `SlotType.PRIMARY`
86
+ """
87
+
63
88
  def __init__(self, slot_type: SlotType, slot_id: int) -> None:
64
89
  super().__init__()
65
90
  self.slot_type = slot_type
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: valkey-glide
3
- Version: 2.0.0rc5
3
+ Version: 2.0.0rc6
4
4
  Classifier: Topic :: Database
5
5
  Classifier: Topic :: Utilities
6
6
  Classifier: License :: OSI Approved :: Apache Software License
@@ -9,7 +9,7 @@ Classifier: Topic :: Software Development
9
9
  Classifier: Programming Language :: Rust
10
10
  Classifier: Programming Language :: Python :: Implementation :: CPython
11
11
  Classifier: Programming Language :: Python :: Implementation :: PyPy
12
- Requires-Dist: async-timeout>=4.0.2; python_version < '3.11'
12
+ Requires-Dist: anyio>=4.9.0
13
13
  Requires-Dist: typing-extensions>=4.8.0; python_version < '3.11'
14
14
  Requires-Dist: protobuf>=3.20
15
15
  Summary: An open source Valkey client library that supports Valkey and Redis open source 6.2, 7.0, 7.2 and 8.0.
@@ -18,9 +18,19 @@ License: Apache-2.0
18
18
  Requires-Python: >=3.9
19
19
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
20
20
 
21
- # Valkey GLIDE
21
+ # Welcome to Valkey GLIDE!
22
22
 
23
- Valkey General Language Independent Driver for the Enterprise (GLIDE), is an open-source Valkey client library. Valkey GLIDE is one of the official client libraries for Valkey, and it supports all Valkey commands. Valkey GLIDE supports Valkey 7.2 and above, and Redis open-source 6.2, 7.0 and 7.2. Application programmers use Valkey GLIDE to safely and reliably connect their applications to Valkey- and Redis OSS- compatible services. Valkey GLIDE is designed for reliability, optimized performance, and high-availability, for Valkey and Redis OSS based applications. It is sponsored and supported by AWS, and is pre-configured with best practices learned from over a decade of operating Redis OSS-compatible services used by hundreds of thousands of customers. To help ensure consistency in application development and operations, Valkey GLIDE is implemented using a core driver framework, written in Rust, with language specific extensions. This design ensures consistency in features across languages and reduces overall complexity.
23
+ Valkey General Language Independent Driver for the Enterprise (GLIDE) is the official open-source Valkey client library, proudly part of the Valkey organization. Our mission is to make your experience with Valkey and Redis OSS seamless and enjoyable. Whether you're a seasoned developer or just starting out, Valkey GLIDE is here to support you every step of the way.
24
+
25
+ # Why Choose Valkey GLIDE?
26
+
27
+ - **Community and Open Source**: Join our vibrant community and contribute to the project. We are always here to respond, and the client is for the community.
28
+ - **Reliability**: Built with best practices learned from over a decade of operating Redis OSS-compatible services.
29
+ - **Performance**: Optimized for high performance and low latency.
30
+ - **High Availability**: Designed to ensure your applications are always up and running.
31
+ - **Cross-Language Support**: Implemented using a core driver framework written in Rust, with language-specific extensions to ensure consistency and reduce complexity.
32
+ - **Stability and Fault Tolerance**: We brought our years of experience to create a bulletproof client.
33
+ - **Backed and Supported by AWS and GCP**: Ensuring robust support and continuous improvement of the project.
24
34
 
25
35
  ## Documentation
26
36
 
@@ -47,6 +57,7 @@ Linux:
47
57
  macOS:
48
58
 
49
59
  - macOS 14.7 (Apple silicon/aarch_64)
60
+ - macOS 13.7 (x86_64/amd64)
50
61
 
51
62
  ## Python Supported Versions
52
63
 
@@ -58,6 +69,8 @@ macOS:
58
69
  | 3.12 |
59
70
  | 3.13 |
60
71
 
72
+ Valkey GLIDE transparently supports both the `asyncio` and `trio` concurrency frameworks.
73
+
61
74
  ## Installation and Setup
62
75
 
63
76
  ### Installing via Package Manager (pip)
@@ -125,3 +138,7 @@ For complete examples with error handling, please refer to the [cluster example]
125
138
 
126
139
  Development instructions for local building & testing the package are in the [DEVELOPER.md](https://github.com/valkey-io/valkey-glide/blob/main/python/DEVELOPER.md#build-from-source) file.
127
140
 
141
+ ## Community and Feedback
142
+
143
+ We encourage you to join our community to support, share feedback, and ask questions. You can approach us for anything on our Valkey Slack: [Join Valkey Slack](https://join.slack.com/t/valkey-oss-developer/shared_invite/zt-2nxs51chx-EB9hu9Qdch3GMfRcztTSkQ).
144
+
@@ -1,9 +1,9 @@
1
- valkey_glide-2.0.0rc5.dist-info/METADATA,sha256=Ux9L-LWvcJmiXrXK0pnolsTIN8YmLR87X5D7ChZybpI,5091
2
- valkey_glide-2.0.0rc5.dist-info/WHEEL,sha256=qAnRBE_xRr_J1n_pHNmRLbHxdeQ89MtmwRTQfjSTk4M,133
1
+ valkey_glide-2.0.0rc6.dist-info/METADATA,sha256=yOI45wSVq6bfF0HKNWungAYTiHdVrsLyn_lf57cznRg,5650
2
+ valkey_glide-2.0.0rc6.dist-info/WHEEL,sha256=qAnRBE_xRr_J1n_pHNmRLbHxdeQ89MtmwRTQfjSTk4M,133
3
3
  glide/config.py,sha256=1IkmvCPLU9nm7EpwcvYT3URoLWApWGr6eSRmuyPXDyA,28891
4
4
  glide/glide.pyi,sha256=K2CeC-F0E8vlr-rbko-E0PofzFoWl4da-xUQSuvuFDo,1105
5
5
  glide/__init__.py,sha256=nCbDQf344sIuXbaCsivrjBjkQ3kaP_ArT3IQnzTUFx8,7372
6
- glide/glide_client.py,sha256=LVx6gJNoD9KxG26eKbSLgj8TZP6M-ZVR0n1qOnABKUU,28695
6
+ glide/glide_client.py,sha256=E6bS-Yu9XNKERD9QjWm4ukbdjMPD17KtWPXLddhBvXQ,31880
7
7
  glide/exceptions.py,sha256=ZI_LwIduoR-1jHm9-6jWZkx90AqCJLRtjbv4iESIEEA,1194
8
8
  glide/protobuf_codec.py,sha256=xwt4-D4WbvNIY_vjOd-00c73HOyNjWq7nN-Z718PBVA,3667
9
9
  glide/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -13,11 +13,11 @@ glide/protobuf/command_request_pb2.py,sha256=Kl07aQBGu8i3oFqbT-uZByWVaTCjYuDeYb0
13
13
  glide/protobuf/command_request_pb2.pyi,sha256=DYnPAmwzyygCdCpW1R1CDGZB_O6zQRJp51K8Wr4CpFc,52470
14
14
  glide/protobuf/response_pb2.pyi,sha256=f7E-n7cba9UsVVVFn5YuetEvS1_aME70aeNt5n4Kwm8,4152
15
15
  glide/protobuf/response_pb2.py,sha256=r3_OFhf8HZvEmBSigSyBzG7S7kWl7CCgVMog4HgMFXM,2025
16
- glide/routes.py,sha256=Y958gbY-o53cG4-SqaN-enrZXByZyp8BYVkOk5YeB8U,3725
16
+ glide/routes.py,sha256=ZcLV_HrMzAYxHJW7uk905gdhOZbchBDSHlG1LVF7BJ4,4439
17
17
  glide/constants.py,sha256=Kb-wTQrcshjCivNXcrrOzz7xp7pNL_-Gt6Updiq9k4o,4354
18
18
  glide/logger.py,sha256=IhlrmIDF4IufCOszQu2WnMZeyBfH21kVr8KwDlBqDP0,3800
19
19
  glide/async_commands/bitmap.py,sha256=ZHGLcKBP7RADtANqIR7PWdIW_gfDBFgpkIsQ-T4QFBQ,10125
20
- glide/async_commands/cluster_commands.py,sha256=tD45_xUV0nyxuEDDhgTVenREx-O_l-yAY277UTv1xEI,61537
20
+ glide/async_commands/cluster_commands.py,sha256=trG0vZCMZxrkziYBFlFvLZ3nMUvfS6r-BRkRwb9Gif0,61994
21
21
  glide/async_commands/__init__.py,sha256=_tbTAFATlzp4L2qe-H77PpAQK-16VsV-y7uKNUKLC_o,136
22
22
  glide/async_commands/server_modules/glide_json.py,sha256=cf93MG6GioA7hFLPn1vNMjipoUt6G-qJ2oK9E91KRkw,63120
23
23
  glide/async_commands/server_modules/ft_options/ft_search_options.py,sha256=q72JVuLMFIVWXDOe0IQWTYFZYNiPSE6Yo3fKIn27hTA,4855
@@ -27,11 +27,11 @@ glide/async_commands/server_modules/ft_options/ft_profile_options.py,sha256=19t7
27
27
  glide/async_commands/server_modules/ft_options/ft_aggregate_options.py,sha256=hnutaOAZB5iFNJ_GKK2K48gm-iiuR-rhaCFUOD6VtBM,9919
28
28
  glide/async_commands/server_modules/ft.py,sha256=xOV22Znj9z9clKK5XZUL8UhthSbiA1F2H11EOKtMJCg,16840
29
29
  glide/async_commands/server_modules/json_batch.py,sha256=XBySnoRJnm1ABPYBnajHmPK6C-Wr9uk3gjNg2s7wVm8,36631
30
- glide/async_commands/batch.py,sha256=oInDvW-7XPQtm9kJYWMdILCEjzlKCv6H--SeG-fDjcg,229669
30
+ glide/async_commands/batch.py,sha256=MMNXtdY7cOCXuFCkHoliZIejZZJb0ApJWxJuXXinGsU,230704
31
31
  glide/async_commands/sorted_set.py,sha256=nALPJQNNjW7dAqXMDJIur_xDwnJzQCdqxtx5zXYCRNM,11458
32
32
  glide/async_commands/stream.py,sha256=y3tmuOc-Ndnjvc__xybBcf2wqoNUQ1H1hPXYerpwNA8,15582
33
- glide/async_commands/core.py,sha256=ydb0dFsJN6KLttSdbjOLMcK5RahUBIsX7X5QxgyooIg,316222
34
- glide/async_commands/standalone_commands.py,sha256=_7WN5eBc_-oQ1j3BHW4X4JVqEMkL2Eihdl5_0MEPvUU,38991
33
+ glide/async_commands/core.py,sha256=ZYjUwpf7DJBlBC7KPHSiRjbA6lbWcBbY8xEGxkJXRno,316713
34
+ glide/async_commands/standalone_commands.py,sha256=vfqXTIU5YLMsHUD9MD3Qi1vccD9QMSQuH38mpo_5rjU,39510
35
35
  glide/async_commands/command_args.py,sha256=55vpSxeQr8wFU7olkFTEecl66wPk1g6vWxVYj16UvXs,2529
36
- glide/glide.cpython-310-aarch64-linux-gnu.so,sha256=bB3SmhZ5k-rJCBKC4cefvE5gx1pVZc21k3PjtG326No,6767208
37
- valkey_glide-2.0.0rc5.dist-info/RECORD,,
36
+ glide/glide.cpython-310-aarch64-linux-gnu.so,sha256=pWr6mhEu_nlUx-4fOGX4DYwZoBHyivFr8qxOul-dEkM,6767208
37
+ valkey_glide-2.0.0rc6.dist-info/RECORD,,