redis 5.3.0b5__py3-none-any.whl → 6.0.0b2__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 (44) hide show
  1. redis/__init__.py +2 -11
  2. redis/_parsers/base.py +14 -2
  3. redis/asyncio/client.py +27 -14
  4. redis/asyncio/cluster.py +85 -59
  5. redis/asyncio/connection.py +76 -23
  6. redis/asyncio/lock.py +26 -5
  7. redis/asyncio/sentinel.py +11 -1
  8. redis/asyncio/utils.py +1 -1
  9. redis/auth/token.py +6 -2
  10. redis/backoff.py +15 -0
  11. redis/client.py +23 -14
  12. redis/cluster.py +112 -48
  13. redis/commands/cluster.py +1 -11
  14. redis/commands/core.py +219 -207
  15. redis/commands/helpers.py +0 -70
  16. redis/commands/redismodules.py +5 -17
  17. redis/commands/search/aggregation.py +3 -1
  18. redis/commands/search/commands.py +41 -14
  19. redis/commands/search/dialect.py +3 -0
  20. redis/commands/search/profile_information.py +14 -0
  21. redis/commands/search/query.py +5 -1
  22. redis/commands/vectorset/__init__.py +46 -0
  23. redis/commands/vectorset/commands.py +367 -0
  24. redis/commands/vectorset/utils.py +94 -0
  25. redis/connection.py +76 -27
  26. redis/exceptions.py +4 -1
  27. redis/lock.py +24 -4
  28. redis/ocsp.py +2 -1
  29. redis/sentinel.py +3 -1
  30. redis/utils.py +114 -1
  31. {redis-5.3.0b5.dist-info → redis-6.0.0b2.dist-info}/METADATA +57 -23
  32. {redis-5.3.0b5.dist-info → redis-6.0.0b2.dist-info}/RECORD +35 -39
  33. {redis-5.3.0b5.dist-info → redis-6.0.0b2.dist-info}/WHEEL +1 -2
  34. redis/commands/graph/__init__.py +0 -263
  35. redis/commands/graph/commands.py +0 -313
  36. redis/commands/graph/edge.py +0 -91
  37. redis/commands/graph/exceptions.py +0 -3
  38. redis/commands/graph/execution_plan.py +0 -211
  39. redis/commands/graph/node.py +0 -88
  40. redis/commands/graph/path.py +0 -78
  41. redis/commands/graph/query_result.py +0 -588
  42. redis-5.3.0b5.dist-info/top_level.txt +0 -1
  43. /redis/commands/search/{indexDefinition.py → index_definition.py} +0 -0
  44. {redis-5.3.0b5.dist-info → redis-6.0.0b2.dist-info/licenses}/LICENSE +0 -0
redis/commands/helpers.py CHANGED
@@ -79,29 +79,6 @@ def parse_list_to_dict(response):
79
79
  return res
80
80
 
81
81
 
82
- def parse_to_dict(response):
83
- if response is None:
84
- return {}
85
-
86
- res = {}
87
- for det in response:
88
- if not isinstance(det, list) or not det:
89
- continue
90
- if len(det) == 1:
91
- res[det[0]] = True
92
- elif isinstance(det[1], list):
93
- res[det[0]] = parse_list_to_dict(det[1])
94
- else:
95
- try: # try to set the attribute. may be provided without value
96
- try: # try to convert the value to float
97
- res[det[0]] = float(det[1])
98
- except (TypeError, ValueError):
99
- res[det[0]] = det[1]
100
- except IndexError:
101
- pass
102
- return res
103
-
104
-
105
82
  def random_string(length=10):
106
83
  """
107
84
  Returns a random N character long string.
@@ -111,26 +88,6 @@ def random_string(length=10):
111
88
  )
112
89
 
113
90
 
114
- def quote_string(v):
115
- """
116
- RedisGraph strings must be quoted,
117
- quote_string wraps given v with quotes incase
118
- v is a string.
119
- """
120
-
121
- if isinstance(v, bytes):
122
- v = v.decode()
123
- elif not isinstance(v, str):
124
- return v
125
- if len(v) == 0:
126
- return '""'
127
-
128
- v = v.replace("\\", "\\\\")
129
- v = v.replace('"', '\\"')
130
-
131
- return f'"{v}"'
132
-
133
-
134
91
  def decode_dict_keys(obj):
135
92
  """Decode the keys of the given dictionary with utf-8."""
136
93
  newobj = copy.copy(obj)
@@ -141,33 +98,6 @@ def decode_dict_keys(obj):
141
98
  return newobj
142
99
 
143
100
 
144
- def stringify_param_value(value):
145
- """
146
- Turn a parameter value into a string suitable for the params header of
147
- a Cypher command.
148
- You may pass any value that would be accepted by `json.dumps()`.
149
-
150
- Ways in which output differs from that of `str()`:
151
- * Strings are quoted.
152
- * None --> "null".
153
- * In dictionaries, keys are _not_ quoted.
154
-
155
- :param value: The parameter value to be turned into a string.
156
- :return: string
157
- """
158
-
159
- if isinstance(value, str):
160
- return quote_string(value)
161
- elif value is None:
162
- return "null"
163
- elif isinstance(value, (list, tuple)):
164
- return f'[{",".join(map(stringify_param_value, value))}]'
165
- elif isinstance(value, dict):
166
- return f'{{{",".join(f"{k}:{stringify_param_value(v)}" for k, v in value.items())}}}' # noqa
167
- else:
168
- return str(value)
169
-
170
-
171
101
  def get_protocol_version(client):
172
102
  if isinstance(client, redis.Redis) or isinstance(client, redis.asyncio.Redis):
173
103
  return client.connection_pool.connection_kwargs.get("protocol")
@@ -72,15 +72,13 @@ class RedisModuleCommands:
72
72
  tdigest = TDigestBloom(client=self)
73
73
  return tdigest
74
74
 
75
- def graph(self, index_name="idx"):
76
- """Access the graph namespace, providing support for
77
- redis graph data.
78
- """
75
+ def vset(self):
76
+ """Access the VectorSet commands namespace."""
79
77
 
80
- from .graph import Graph
78
+ from .vectorset import VectorSet
81
79
 
82
- g = Graph(client=self, name=index_name)
83
- return g
80
+ vset = VectorSet(client=self)
81
+ return vset
84
82
 
85
83
 
86
84
  class AsyncRedisModuleCommands(RedisModuleCommands):
@@ -91,13 +89,3 @@ class AsyncRedisModuleCommands(RedisModuleCommands):
91
89
 
92
90
  s = AsyncSearch(client=self, index_name=index_name)
93
91
  return s
94
-
95
- def graph(self, index_name="idx"):
96
- """Access the graph namespace, providing support for
97
- redis graph data.
98
- """
99
-
100
- from .graph import AsyncGraph
101
-
102
- g = AsyncGraph(client=self, name=index_name)
103
- return g
@@ -1,5 +1,7 @@
1
1
  from typing import List, Union
2
2
 
3
+ from redis.commands.search.dialect import DEFAULT_DIALECT
4
+
3
5
  FIELDNAME = object()
4
6
 
5
7
 
@@ -110,7 +112,7 @@ class AggregateRequest:
110
112
  self._with_schema = False
111
113
  self._verbatim = False
112
114
  self._cursor = []
113
- self._dialect = None
115
+ self._dialect = DEFAULT_DIALECT
114
116
  self._add_scores = False
115
117
  self._scorer = "TFIDF"
116
118
 
@@ -5,12 +5,13 @@ from typing import Dict, List, Optional, Union
5
5
  from redis.client import NEVER_DECODE, Pipeline
6
6
  from redis.utils import deprecated_function
7
7
 
8
- from ..helpers import get_protocol_version, parse_to_dict
8
+ from ..helpers import get_protocol_version
9
9
  from ._util import to_string
10
10
  from .aggregation import AggregateRequest, AggregateResult, Cursor
11
11
  from .document import Document
12
12
  from .field import Field
13
- from .indexDefinition import IndexDefinition
13
+ from .index_definition import IndexDefinition
14
+ from .profile_information import ProfileInformation
14
15
  from .query import Query
15
16
  from .result import Result
16
17
  from .suggestion import SuggestionParser
@@ -67,7 +68,7 @@ class SearchCommands:
67
68
 
68
69
  def _parse_results(self, cmd, res, **kwargs):
69
70
  if get_protocol_version(self.client) in ["3", 3]:
70
- return res
71
+ return ProfileInformation(res) if cmd == "FT.PROFILE" else res
71
72
  else:
72
73
  return self._RESP2_MODULE_CALLBACKS[cmd](res, **kwargs)
73
74
 
@@ -101,7 +102,7 @@ class SearchCommands:
101
102
  with_scores=query._with_scores,
102
103
  )
103
104
 
104
- return result, parse_to_dict(res[1])
105
+ return result, ProfileInformation(res[1])
105
106
 
106
107
  def _parse_spellcheck(self, res, **kwargs):
107
108
  corrections = {}
@@ -254,8 +255,18 @@ class SearchCommands:
254
255
 
255
256
  For more information see `FT.DROPINDEX <https://redis.io/commands/ft.dropindex>`_.
256
257
  """ # noqa
257
- delete_str = "DD" if delete_documents else ""
258
- return self.execute_command(DROPINDEX_CMD, self.index_name, delete_str)
258
+ args = [DROPINDEX_CMD, self.index_name]
259
+
260
+ delete_str = (
261
+ "DD"
262
+ if isinstance(delete_documents, bool) and delete_documents is True
263
+ else ""
264
+ )
265
+
266
+ if delete_str:
267
+ args.append(delete_str)
268
+
269
+ return self.execute_command(*args)
259
270
 
260
271
  def _add_document(
261
272
  self,
@@ -499,7 +510,7 @@ class SearchCommands:
499
510
  For more information see `FT.SEARCH <https://redis.io/commands/ft.search>`_.
500
511
  """ # noqa
501
512
  args, query = self._mk_query_args(query, query_params=query_params)
502
- st = time.time()
513
+ st = time.monotonic()
503
514
 
504
515
  options = {}
505
516
  if get_protocol_version(self.client) not in ["3", 3]:
@@ -511,7 +522,7 @@ class SearchCommands:
511
522
  return res
512
523
 
513
524
  return self._parse_results(
514
- SEARCH_CMD, res, query=query, duration=(time.time() - st) * 1000.0
525
+ SEARCH_CMD, res, query=query, duration=(time.monotonic() - st) * 1000.0
515
526
  )
516
527
 
517
528
  def explain(
@@ -585,7 +596,7 @@ class SearchCommands:
585
596
 
586
597
  def profile(
587
598
  self,
588
- query: Union[str, Query, AggregateRequest],
599
+ query: Union[Query, AggregateRequest],
589
600
  limited: bool = False,
590
601
  query_params: Optional[Dict[str, Union[str, int, float]]] = None,
591
602
  ):
@@ -595,13 +606,13 @@ class SearchCommands:
595
606
 
596
607
  ### Parameters
597
608
 
598
- **query**: This can be either an `AggregateRequest`, `Query` or string.
609
+ **query**: This can be either an `AggregateRequest` or `Query`.
599
610
  **limited**: If set to True, removes details of reader iterator.
600
611
  **query_params**: Define one or more value parameters.
601
612
  Each parameter has a name and a value.
602
613
 
603
614
  """
604
- st = time.time()
615
+ st = time.monotonic()
605
616
  cmd = [PROFILE_CMD, self.index_name, ""]
606
617
  if limited:
607
618
  cmd.append("LIMITED")
@@ -620,7 +631,7 @@ class SearchCommands:
620
631
  res = self.execute_command(*cmd)
621
632
 
622
633
  return self._parse_results(
623
- PROFILE_CMD, res, query=query, duration=(time.time() - st) * 1000.0
634
+ PROFILE_CMD, res, query=query, duration=(time.monotonic() - st) * 1000.0
624
635
  )
625
636
 
626
637
  def spellcheck(self, query, distance=None, include=None, exclude=None):
@@ -691,6 +702,10 @@ class SearchCommands:
691
702
  cmd = [DICT_DUMP_CMD, name]
692
703
  return self.execute_command(*cmd)
693
704
 
705
+ @deprecated_function(
706
+ version="8.0.0",
707
+ reason="deprecated since Redis 8.0, call config_set from core module instead",
708
+ )
694
709
  def config_set(self, option: str, value: str) -> bool:
695
710
  """Set runtime configuration option.
696
711
 
@@ -705,6 +720,10 @@ class SearchCommands:
705
720
  raw = self.execute_command(*cmd)
706
721
  return raw == "OK"
707
722
 
723
+ @deprecated_function(
724
+ version="8.0.0",
725
+ reason="deprecated since Redis 8.0, call config_get from core module instead",
726
+ )
708
727
  def config_get(self, option: str) -> str:
709
728
  """Get runtime configuration option value.
710
729
 
@@ -931,7 +950,7 @@ class AsyncSearchCommands(SearchCommands):
931
950
  For more information see `FT.SEARCH <https://redis.io/commands/ft.search>`_.
932
951
  """ # noqa
933
952
  args, query = self._mk_query_args(query, query_params=query_params)
934
- st = time.time()
953
+ st = time.monotonic()
935
954
 
936
955
  options = {}
937
956
  if get_protocol_version(self.client) not in ["3", 3]:
@@ -943,7 +962,7 @@ class AsyncSearchCommands(SearchCommands):
943
962
  return res
944
963
 
945
964
  return self._parse_results(
946
- SEARCH_CMD, res, query=query, duration=(time.time() - st) * 1000.0
965
+ SEARCH_CMD, res, query=query, duration=(time.monotonic() - st) * 1000.0
947
966
  )
948
967
 
949
968
  async def aggregate(
@@ -1006,6 +1025,10 @@ class AsyncSearchCommands(SearchCommands):
1006
1025
 
1007
1026
  return self._parse_results(SPELLCHECK_CMD, res)
1008
1027
 
1028
+ @deprecated_function(
1029
+ version="8.0.0",
1030
+ reason="deprecated since Redis 8.0, call config_set from core module instead",
1031
+ )
1009
1032
  async def config_set(self, option: str, value: str) -> bool:
1010
1033
  """Set runtime configuration option.
1011
1034
 
@@ -1020,6 +1043,10 @@ class AsyncSearchCommands(SearchCommands):
1020
1043
  raw = await self.execute_command(*cmd)
1021
1044
  return raw == "OK"
1022
1045
 
1046
+ @deprecated_function(
1047
+ version="8.0.0",
1048
+ reason="deprecated since Redis 8.0, call config_get from core module instead",
1049
+ )
1023
1050
  async def config_get(self, option: str) -> str:
1024
1051
  """Get runtime configuration option value.
1025
1052
 
@@ -0,0 +1,3 @@
1
+ # Value for the default dialect to be used as a part of
2
+ # Search or Aggregate query.
3
+ DEFAULT_DIALECT = 2
@@ -0,0 +1,14 @@
1
+ from typing import Any
2
+
3
+
4
+ class ProfileInformation:
5
+ """
6
+ Wrapper around FT.PROFILE response
7
+ """
8
+
9
+ def __init__(self, info: Any) -> None:
10
+ self._info: Any = info
11
+
12
+ @property
13
+ def info(self) -> Any:
14
+ return self._info
@@ -1,5 +1,7 @@
1
1
  from typing import List, Optional, Union
2
2
 
3
+ from redis.commands.search.dialect import DEFAULT_DIALECT
4
+
3
5
 
4
6
  class Query:
5
7
  """
@@ -40,7 +42,7 @@ class Query:
40
42
  self._highlight_fields: List = []
41
43
  self._language: Optional[str] = None
42
44
  self._expander: Optional[str] = None
43
- self._dialect: Optional[int] = None
45
+ self._dialect: int = DEFAULT_DIALECT
44
46
 
45
47
  def query_string(self) -> str:
46
48
  """Return the query string of this query only."""
@@ -177,6 +179,8 @@ class Query:
177
179
  Use a different scoring function to evaluate document relevance.
178
180
  Default is `TFIDF`.
179
181
 
182
+ Since Redis 8.0 default was changed to BM25STD.
183
+
180
184
  :param scorer: The scoring function to use
181
185
  (e.g. `TFIDF.DOCNORM` or `BM25`)
182
186
  """
@@ -0,0 +1,46 @@
1
+ import json
2
+
3
+ from redis._parsers.helpers import pairs_to_dict
4
+ from redis.commands.vectorset.utils import (
5
+ parse_vemb_result,
6
+ parse_vlinks_result,
7
+ parse_vsim_result,
8
+ )
9
+
10
+ from ..helpers import get_protocol_version
11
+ from .commands import (
12
+ VEMB_CMD,
13
+ VGETATTR_CMD,
14
+ VINFO_CMD,
15
+ VLINKS_CMD,
16
+ VSIM_CMD,
17
+ VectorSetCommands,
18
+ )
19
+
20
+
21
+ class VectorSet(VectorSetCommands):
22
+ def __init__(self, client, **kwargs):
23
+ """Create a new VectorSet client."""
24
+ # Set the module commands' callbacks
25
+ self._MODULE_CALLBACKS = {
26
+ VEMB_CMD: parse_vemb_result,
27
+ VGETATTR_CMD: lambda r: r and json.loads(r) or None,
28
+ }
29
+
30
+ self._RESP2_MODULE_CALLBACKS = {
31
+ VINFO_CMD: lambda r: r and pairs_to_dict(r) or None,
32
+ VSIM_CMD: parse_vsim_result,
33
+ VLINKS_CMD: parse_vlinks_result,
34
+ }
35
+ self._RESP3_MODULE_CALLBACKS = {}
36
+
37
+ self.client = client
38
+ self.execute_command = client.execute_command
39
+
40
+ if get_protocol_version(self.client) in ["3", 3]:
41
+ self._MODULE_CALLBACKS.update(self._RESP3_MODULE_CALLBACKS)
42
+ else:
43
+ self._MODULE_CALLBACKS.update(self._RESP2_MODULE_CALLBACKS)
44
+
45
+ for k, v in self._MODULE_CALLBACKS.items():
46
+ self.client.set_response_callback(k, v)