redis 6.3.0__py3-none-any.whl → 7.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. redis/__init__.py +1 -2
  2. redis/_parsers/base.py +193 -8
  3. redis/_parsers/helpers.py +64 -6
  4. redis/_parsers/hiredis.py +16 -10
  5. redis/_parsers/resp3.py +11 -5
  6. redis/asyncio/client.py +65 -8
  7. redis/asyncio/cluster.py +57 -14
  8. redis/asyncio/connection.py +62 -2
  9. redis/asyncio/http/__init__.py +0 -0
  10. redis/asyncio/http/http_client.py +265 -0
  11. redis/asyncio/multidb/__init__.py +0 -0
  12. redis/asyncio/multidb/client.py +530 -0
  13. redis/asyncio/multidb/command_executor.py +339 -0
  14. redis/asyncio/multidb/config.py +210 -0
  15. redis/asyncio/multidb/database.py +69 -0
  16. redis/asyncio/multidb/event.py +84 -0
  17. redis/asyncio/multidb/failover.py +125 -0
  18. redis/asyncio/multidb/failure_detector.py +38 -0
  19. redis/asyncio/multidb/healthcheck.py +285 -0
  20. redis/background.py +204 -0
  21. redis/cache.py +1 -0
  22. redis/client.py +99 -22
  23. redis/cluster.py +14 -3
  24. redis/commands/core.py +348 -313
  25. redis/commands/helpers.py +0 -20
  26. redis/commands/json/_util.py +4 -2
  27. redis/commands/json/commands.py +2 -2
  28. redis/commands/search/__init__.py +2 -2
  29. redis/commands/search/aggregation.py +28 -30
  30. redis/commands/search/commands.py +13 -13
  31. redis/commands/search/field.py +2 -2
  32. redis/commands/search/query.py +23 -23
  33. redis/commands/vectorset/__init__.py +1 -1
  34. redis/commands/vectorset/commands.py +50 -25
  35. redis/commands/vectorset/utils.py +40 -4
  36. redis/connection.py +1258 -90
  37. redis/data_structure.py +81 -0
  38. redis/event.py +88 -14
  39. redis/exceptions.py +8 -0
  40. redis/http/__init__.py +0 -0
  41. redis/http/http_client.py +425 -0
  42. redis/maint_notifications.py +810 -0
  43. redis/multidb/__init__.py +0 -0
  44. redis/multidb/circuit.py +144 -0
  45. redis/multidb/client.py +526 -0
  46. redis/multidb/command_executor.py +350 -0
  47. redis/multidb/config.py +207 -0
  48. redis/multidb/database.py +130 -0
  49. redis/multidb/event.py +89 -0
  50. redis/multidb/exception.py +17 -0
  51. redis/multidb/failover.py +125 -0
  52. redis/multidb/failure_detector.py +104 -0
  53. redis/multidb/healthcheck.py +282 -0
  54. redis/retry.py +14 -1
  55. redis/utils.py +34 -0
  56. {redis-6.3.0.dist-info → redis-7.0.0.dist-info}/METADATA +7 -4
  57. redis-7.0.0.dist-info/RECORD +105 -0
  58. redis-6.3.0.dist-info/RECORD +0 -78
  59. {redis-6.3.0.dist-info → redis-7.0.0.dist-info}/WHEEL +0 -0
  60. {redis-6.3.0.dist-info → redis-7.0.0.dist-info}/licenses/LICENSE +0 -0
redis/commands/helpers.py CHANGED
@@ -72,26 +72,6 @@ def parse_to_list(response):
72
72
  return res
73
73
 
74
74
 
75
- def parse_list_to_dict(response):
76
- res = {}
77
- for i in range(0, len(response), 2):
78
- if isinstance(response[i], list):
79
- res["Child iterators"].append(parse_list_to_dict(response[i]))
80
- try:
81
- if isinstance(response[i + 1], list):
82
- res["Child iterators"].append(parse_list_to_dict(response[i + 1]))
83
- except IndexError:
84
- pass
85
- elif isinstance(response[i + 1], list):
86
- res["Child iterators"] = [parse_list_to_dict(response[i + 1])]
87
- else:
88
- try:
89
- res[response[i]] = float(response[i + 1])
90
- except (TypeError, ValueError):
91
- res[response[i]] = response[i + 1]
92
- return res
93
-
94
-
95
75
  def random_string(length=10):
96
76
  """
97
77
  Returns a random N character long string.
@@ -1,3 +1,5 @@
1
- from typing import Any, Dict, List, Union
1
+ from typing import List, Mapping, Union
2
2
 
3
- JsonType = Union[str, int, float, bool, None, Dict[str, Any], List[Any]]
3
+ JsonType = Union[
4
+ str, int, float, bool, None, Mapping[str, "JsonType"], List["JsonType"]
5
+ ]
@@ -14,7 +14,7 @@ class JSONCommands:
14
14
  """json commands."""
15
15
 
16
16
  def arrappend(
17
- self, name: str, path: Optional[str] = Path.root_path(), *args: List[JsonType]
17
+ self, name: str, path: Optional[str] = Path.root_path(), *args: JsonType
18
18
  ) -> List[Optional[int]]:
19
19
  """Append the objects ``args`` to the array under the
20
20
  ``path` in key ``name``.
@@ -52,7 +52,7 @@ class JSONCommands:
52
52
  return self.execute_command("JSON.ARRINDEX", *pieces, keys=[name])
53
53
 
54
54
  def arrinsert(
55
- self, name: str, path: str, index: int, *args: List[JsonType]
55
+ self, name: str, path: str, index: int, *args: JsonType
56
56
  ) -> List[Optional[int]]:
57
57
  """Insert the objects ``args`` to the array at index ``index``
58
58
  under the ``path` in key ``name``.
@@ -1,4 +1,4 @@
1
- import redis
1
+ from redis.client import Pipeline as RedisPipeline
2
2
 
3
3
  from ...asyncio.client import Pipeline as AsyncioPipeline
4
4
  from .commands import (
@@ -181,7 +181,7 @@ class AsyncSearch(Search, AsyncSearchCommands):
181
181
  return p
182
182
 
183
183
 
184
- class Pipeline(SearchCommands, redis.client.Pipeline):
184
+ class Pipeline(SearchCommands, RedisPipeline):
185
185
  """Pipeline for the module."""
186
186
 
187
187
 
@@ -1,4 +1,4 @@
1
- from typing import List, Union
1
+ from typing import List, Optional, Tuple, Union
2
2
 
3
3
  from redis.commands.search.dialect import DEFAULT_DIALECT
4
4
 
@@ -26,10 +26,10 @@ class Reducer:
26
26
 
27
27
  NAME = None
28
28
 
29
- def __init__(self, *args: List[str]) -> None:
30
- self._args = args
31
- self._field = None
32
- self._alias = None
29
+ def __init__(self, *args: str) -> None:
30
+ self._args: Tuple[str, ...] = args
31
+ self._field: Optional[str] = None
32
+ self._alias: Optional[str] = None
33
33
 
34
34
  def alias(self, alias: str) -> "Reducer":
35
35
  """
@@ -49,13 +49,14 @@ class Reducer:
49
49
  if alias is FIELDNAME:
50
50
  if not self._field:
51
51
  raise ValueError("Cannot use FIELDNAME alias with no field")
52
- # Chop off initial '@'
53
- alias = self._field[1:]
52
+ else:
53
+ # Chop off initial '@'
54
+ alias = self._field[1:]
54
55
  self._alias = alias
55
56
  return self
56
57
 
57
58
  @property
58
- def args(self) -> List[str]:
59
+ def args(self) -> Tuple[str, ...]:
59
60
  return self._args
60
61
 
61
62
 
@@ -64,7 +65,7 @@ class SortDirection:
64
65
  This special class is used to indicate sort direction.
65
66
  """
66
67
 
67
- DIRSTRING = None
68
+ DIRSTRING: Optional[str] = None
68
69
 
69
70
  def __init__(self, field: str) -> None:
70
71
  self.field = field
@@ -104,19 +105,19 @@ class AggregateRequest:
104
105
  All member methods (except `build_args()`)
105
106
  return the object itself, making them useful for chaining.
106
107
  """
107
- self._query = query
108
- self._aggregateplan = []
109
- self._loadfields = []
110
- self._loadall = False
111
- self._max = 0
112
- self._with_schema = False
113
- self._verbatim = False
114
- self._cursor = []
115
- self._dialect = DEFAULT_DIALECT
116
- self._add_scores = False
117
- self._scorer = "TFIDF"
118
-
119
- def load(self, *fields: List[str]) -> "AggregateRequest":
108
+ self._query: str = query
109
+ self._aggregateplan: List[str] = []
110
+ self._loadfields: List[str] = []
111
+ self._loadall: bool = False
112
+ self._max: int = 0
113
+ self._with_schema: bool = False
114
+ self._verbatim: bool = False
115
+ self._cursor: List[str] = []
116
+ self._dialect: int = DEFAULT_DIALECT
117
+ self._add_scores: bool = False
118
+ self._scorer: str = "TFIDF"
119
+
120
+ def load(self, *fields: str) -> "AggregateRequest":
120
121
  """
121
122
  Indicate the fields to be returned in the response. These fields are
122
123
  returned in addition to any others implicitly specified.
@@ -133,7 +134,7 @@ class AggregateRequest:
133
134
  return self
134
135
 
135
136
  def group_by(
136
- self, fields: List[str], *reducers: Union[Reducer, List[Reducer]]
137
+ self, fields: Union[str, List[str]], *reducers: Reducer
137
138
  ) -> "AggregateRequest":
138
139
  """
139
140
  Specify by which fields to group the aggregation.
@@ -147,7 +148,6 @@ class AggregateRequest:
147
148
  `aggregation` module.
148
149
  """
149
150
  fields = [fields] if isinstance(fields, str) else fields
150
- reducers = [reducers] if isinstance(reducers, Reducer) else reducers
151
151
 
152
152
  ret = ["GROUPBY", str(len(fields)), *fields]
153
153
  for reducer in reducers:
@@ -223,7 +223,7 @@ class AggregateRequest:
223
223
  self._aggregateplan.extend(_limit.build_args())
224
224
  return self
225
225
 
226
- def sort_by(self, *fields: List[str], **kwargs) -> "AggregateRequest":
226
+ def sort_by(self, *fields: str, **kwargs) -> "AggregateRequest":
227
227
  """
228
228
  Indicate how the results should be sorted. This can also be used for
229
229
  *top-N* style queries
@@ -251,12 +251,10 @@ class AggregateRequest:
251
251
  .sort_by(Desc("@paid"), max=10)
252
252
  ```
253
253
  """
254
- if isinstance(fields, (str, SortDirection)):
255
- fields = [fields]
256
254
 
257
255
  fields_args = []
258
256
  for f in fields:
259
- if isinstance(f, SortDirection):
257
+ if isinstance(f, (Asc, Desc)):
260
258
  fields_args += [f.field, f.DIRSTRING]
261
259
  else:
262
260
  fields_args += [f]
@@ -356,7 +354,7 @@ class AggregateRequest:
356
354
  ret.extend(self._loadfields)
357
355
 
358
356
  if self._dialect:
359
- ret.extend(["DIALECT", self._dialect])
357
+ ret.extend(["DIALECT", str(self._dialect)])
360
358
 
361
359
  ret.extend(self._aggregateplan)
362
360
 
@@ -393,7 +391,7 @@ class AggregateResult:
393
391
  self.cursor = cursor
394
392
  self.schema = schema
395
393
 
396
- def __repr__(self) -> (str, str):
394
+ def __repr__(self) -> str:
397
395
  cid = self.cursor.cid if self.cursor else -1
398
396
  return (
399
397
  f"<{self.__class__.__name__} at 0x{id(self):x} "
@@ -221,7 +221,7 @@ class SearchCommands:
221
221
 
222
222
  return self.execute_command(*args)
223
223
 
224
- def alter_schema_add(self, fields: List[str]):
224
+ def alter_schema_add(self, fields: Union[Field, List[Field]]):
225
225
  """
226
226
  Alter the existing search index by adding new fields. The index
227
227
  must already exist.
@@ -336,11 +336,11 @@ class SearchCommands:
336
336
  doc_id: str,
337
337
  nosave: bool = False,
338
338
  score: float = 1.0,
339
- payload: bool = None,
339
+ payload: Optional[bool] = None,
340
340
  replace: bool = False,
341
341
  partial: bool = False,
342
342
  language: Optional[str] = None,
343
- no_create: str = False,
343
+ no_create: bool = False,
344
344
  **fields: List[str],
345
345
  ):
346
346
  """
@@ -464,7 +464,7 @@ class SearchCommands:
464
464
  return self._parse_results(INFO_CMD, res)
465
465
 
466
466
  def get_params_args(
467
- self, query_params: Union[Dict[str, Union[str, int, float, bytes]], None]
467
+ self, query_params: Optional[Dict[str, Union[str, int, float, bytes]]]
468
468
  ):
469
469
  if query_params is None:
470
470
  return []
@@ -478,7 +478,7 @@ class SearchCommands:
478
478
  return args
479
479
 
480
480
  def _mk_query_args(
481
- self, query, query_params: Union[Dict[str, Union[str, int, float, bytes]], None]
481
+ self, query, query_params: Optional[Dict[str, Union[str, int, float, bytes]]]
482
482
  ):
483
483
  args = [self.index_name]
484
484
 
@@ -528,7 +528,7 @@ class SearchCommands:
528
528
  def explain(
529
529
  self,
530
530
  query: Union[str, Query],
531
- query_params: Dict[str, Union[str, int, float]] = None,
531
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
532
532
  ):
533
533
  """Returns the execution plan for a complex query.
534
534
 
@@ -542,8 +542,8 @@ class SearchCommands:
542
542
 
543
543
  def aggregate(
544
544
  self,
545
- query: Union[str, Query],
546
- query_params: Dict[str, Union[str, int, float]] = None,
545
+ query: Union[AggregateRequest, Cursor],
546
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
547
547
  ):
548
548
  """
549
549
  Issue an aggregation query.
@@ -573,7 +573,7 @@ class SearchCommands:
573
573
  )
574
574
 
575
575
  def _get_aggregate_result(
576
- self, raw: List, query: Union[str, Query, AggregateRequest], has_cursor: bool
576
+ self, raw: List, query: Union[AggregateRequest, Cursor], has_cursor: bool
577
577
  ):
578
578
  if has_cursor:
579
579
  if isinstance(query, Cursor):
@@ -598,7 +598,7 @@ class SearchCommands:
598
598
  self,
599
599
  query: Union[Query, AggregateRequest],
600
600
  limited: bool = False,
601
- query_params: Optional[Dict[str, Union[str, int, float]]] = None,
601
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
602
602
  ):
603
603
  """
604
604
  Performs a search or aggregate command and collects performance
@@ -936,7 +936,7 @@ class AsyncSearchCommands(SearchCommands):
936
936
  async def search(
937
937
  self,
938
938
  query: Union[str, Query],
939
- query_params: Dict[str, Union[str, int, float]] = None,
939
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
940
940
  ):
941
941
  """
942
942
  Search the index for a given query, and return a result of documents
@@ -967,8 +967,8 @@ class AsyncSearchCommands(SearchCommands):
967
967
 
968
968
  async def aggregate(
969
969
  self,
970
- query: Union[str, Query],
971
- query_params: Dict[str, Union[str, int, float]] = None,
970
+ query: Union[AggregateResult, Cursor],
971
+ query_params: Optional[Dict[str, Union[str, int, float, bytes]]] = None,
972
972
  ):
973
973
  """
974
974
  Issue an aggregation query.
@@ -52,14 +52,14 @@ class Field:
52
52
  self.args_suffix = list()
53
53
  self.as_name = as_name
54
54
 
55
- if sortable:
56
- self.args_suffix.append(Field.SORTABLE)
57
55
  if no_index:
58
56
  self.args_suffix.append(Field.NOINDEX)
59
57
  if index_missing:
60
58
  self.args_suffix.append(Field.INDEX_MISSING)
61
59
  if index_empty:
62
60
  self.args_suffix.append(Field.INDEX_EMPTY)
61
+ if sortable:
62
+ self.args_suffix.append(Field.SORTABLE)
63
63
 
64
64
  if no_index and not sortable:
65
65
  raise ValueError("Non-Sortable non-Indexable fields are ignored")
@@ -1,4 +1,4 @@
1
- from typing import List, Optional, Union
1
+ from typing import List, Optional, Tuple, Union
2
2
 
3
3
  from redis.commands.search.dialect import DEFAULT_DIALECT
4
4
 
@@ -9,7 +9,7 @@ class Query:
9
9
  the query string. The query string is set in the constructor, and other
10
10
  options have setter functions.
11
11
 
12
- The setter functions return the query object, so they can be chained,
12
+ The setter functions return the query object so they can be chained.
13
13
  i.e. `Query("foo").verbatim().filter(...)` etc.
14
14
  """
15
15
 
@@ -31,7 +31,7 @@ class Query:
31
31
  self._with_scores: bool = False
32
32
  self._scorer: Optional[str] = None
33
33
  self._filters: List = list()
34
- self._ids: Optional[List[str]] = None
34
+ self._ids: Optional[Tuple[str, ...]] = None
35
35
  self._slop: int = -1
36
36
  self._timeout: Optional[float] = None
37
37
  self._in_order: bool = False
@@ -81,7 +81,7 @@ class Query:
81
81
  self._return_fields += ("AS", as_field)
82
82
  return self
83
83
 
84
- def _mk_field_list(self, fields: List[str]) -> List:
84
+ def _mk_field_list(self, fields: Optional[Union[List[str], str]]) -> List:
85
85
  if not fields:
86
86
  return []
87
87
  return [fields] if isinstance(fields, str) else list(fields)
@@ -95,12 +95,12 @@ class Query:
95
95
  ) -> "Query":
96
96
  """
97
97
  Return an abridged format of the field, containing only the segments of
98
- the field which contain the matching term(s).
98
+ the field that contain the matching term(s).
99
99
 
100
100
  If `fields` is specified, then only the mentioned fields are
101
- summarized; otherwise all results are summarized.
101
+ summarized; otherwise, all results are summarized.
102
102
 
103
- Server side defaults are used for each option (except `fields`)
103
+ Server-side defaults are used for each option (except `fields`)
104
104
  if not specified
105
105
 
106
106
  - **fields** List of fields to summarize. All fields are summarized
@@ -126,11 +126,11 @@ class Query:
126
126
 
127
127
  def highlight(
128
128
  self, fields: Optional[List[str]] = None, tags: Optional[List[str]] = None
129
- ) -> None:
129
+ ) -> "Query":
130
130
  """
131
131
  Apply specified markup to matched term(s) within the returned field(s).
132
132
 
133
- - **fields** If specified then only those mentioned fields are
133
+ - **fields** If specified, then only those mentioned fields are
134
134
  highlighted, otherwise all fields are highlighted
135
135
  - **tags** A list of two strings to surround the match.
136
136
  """
@@ -154,7 +154,7 @@ class Query:
154
154
  return self
155
155
 
156
156
  def slop(self, slop: int) -> "Query":
157
- """Allow a maximum of N intervening non matched terms between
157
+ """Allow a maximum of N intervening non-matched terms between
158
158
  phrase terms (0 means exact phrase).
159
159
  """
160
160
  self._slop = slop
@@ -169,7 +169,7 @@ class Query:
169
169
  """
170
170
  Match only documents where the query terms appear in
171
171
  the same order in the document.
172
- i.e. for the query "hello world", we do not match "world hello"
172
+ i.e., for the query "hello world", we do not match "world hello"
173
173
  """
174
174
  self._in_order = True
175
175
  return self
@@ -187,16 +187,16 @@ class Query:
187
187
  self._scorer = scorer
188
188
  return self
189
189
 
190
- def get_args(self) -> List[str]:
190
+ def get_args(self) -> List[Union[str, int, float]]:
191
191
  """Format the redis arguments for this query and return them."""
192
- args = [self._query_string]
192
+ args: List[Union[str, int, float]] = [self._query_string]
193
193
  args += self._get_args_tags()
194
194
  args += self._summarize_fields + self._highlight_fields
195
195
  args += ["LIMIT", self._offset, self._num]
196
196
  return args
197
197
 
198
- def _get_args_tags(self) -> List[str]:
199
- args = []
198
+ def _get_args_tags(self) -> List[Union[str, int, float]]:
199
+ args: List[Union[str, int, float]] = []
200
200
  if self._no_content:
201
201
  args.append("NOCONTENT")
202
202
  if self._fields:
@@ -258,7 +258,7 @@ class Query:
258
258
  return self
259
259
 
260
260
  def verbatim(self) -> "Query":
261
- """Set the query to be verbatim, i.e. use no query expansion
261
+ """Set the query to be verbatim, i.e., use no query expansion
262
262
  or stemming.
263
263
  """
264
264
  self._verbatim = True
@@ -288,20 +288,20 @@ class Query:
288
288
  self._with_scores = True
289
289
  return self
290
290
 
291
- def limit_fields(self, *fields: List[str]) -> "Query":
291
+ def limit_fields(self, *fields: str) -> "Query":
292
292
  """
293
293
  Limit the search to specific TEXT fields only.
294
294
 
295
- - **fields**: A list of strings, case sensitive field names
295
+ - **fields**: Each element should be a string, case sensitive field name
296
296
  from the defined schema.
297
297
  """
298
- self._fields = fields
298
+ self._fields = list(fields)
299
299
  return self
300
300
 
301
301
  def add_filter(self, flt: "Filter") -> "Query":
302
302
  """
303
303
  Add a numeric or geo filter to the query.
304
- **Currently only one of each filter is supported by the engine**
304
+ **Currently, only one of each filter is supported by the engine**
305
305
 
306
306
  - **flt**: A NumericFilter or GeoFilter object, used on a
307
307
  corresponding field
@@ -315,14 +315,14 @@ class Query:
315
315
  Add a sortby field to the query.
316
316
 
317
317
  - **field** - the name of the field to sort by
318
- - **asc** - when `True`, sorting will be done in asceding order
318
+ - **asc** - when `True`, sorting will be done in ascending order
319
319
  """
320
320
  self._sortby = SortbyField(field, asc)
321
321
  return self
322
322
 
323
323
  def expander(self, expander: str) -> "Query":
324
324
  """
325
- Add a expander field to the query.
325
+ Add an expander field to the query.
326
326
 
327
327
  - **expander** - the name of the expander
328
328
  """
@@ -340,7 +340,7 @@ class Query:
340
340
 
341
341
 
342
342
  class Filter:
343
- def __init__(self, keyword: str, field: str, *args: List[str]) -> None:
343
+ def __init__(self, keyword: str, field: str, *args: Union[str, float]) -> None:
344
344
  self.args = [keyword, field] + list(args)
345
345
 
346
346
 
@@ -24,12 +24,12 @@ class VectorSet(VectorSetCommands):
24
24
  # Set the module commands' callbacks
25
25
  self._MODULE_CALLBACKS = {
26
26
  VEMB_CMD: parse_vemb_result,
27
+ VSIM_CMD: parse_vsim_result,
27
28
  VGETATTR_CMD: lambda r: r and json.loads(r) or None,
28
29
  }
29
30
 
30
31
  self._RESP2_MODULE_CALLBACKS = {
31
32
  VINFO_CMD: lambda r: r and pairs_to_dict(r) or None,
32
- VSIM_CMD: parse_vsim_result,
33
33
  VLINKS_CMD: parse_vlinks_result,
34
34
  }
35
35
  self._RESP3_MODULE_CALLBACKS = {}