redis 6.3.0__tar.gz → 6.4.0__tar.gz

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 (156) hide show
  1. {redis-6.3.0 → redis-6.4.0}/PKG-INFO +1 -1
  2. {redis-6.3.0 → redis-6.4.0}/redis/__init__.py +1 -2
  3. {redis-6.3.0 → redis-6.4.0}/redis/asyncio/cluster.py +0 -9
  4. {redis-6.3.0 → redis-6.4.0}/redis/client.py +2 -6
  5. redis-6.4.0/redis/commands/json/_util.py +5 -0
  6. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/aggregation.py +3 -3
  7. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/commands.py +3 -3
  8. {redis-6.3.0 → redis-6.4.0}/redis/commands/vectorset/commands.py +7 -0
  9. {redis-6.3.0 → redis-6.4.0}/redis/connection.py +2 -8
  10. {redis-6.3.0 → redis-6.4.0}/redis/event.py +4 -4
  11. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_vsets.py +15 -0
  12. {redis-6.3.0 → redis-6.4.0}/tests/test_cluster_transaction.py +2 -2
  13. {redis-6.3.0 → redis-6.4.0}/tests/test_connection.py +3 -3
  14. {redis-6.3.0 → redis-6.4.0}/tests/test_credentials.py +5 -5
  15. {redis-6.3.0 → redis-6.4.0}/tests/test_search.py +49 -0
  16. {redis-6.3.0 → redis-6.4.0}/tests/test_vsets.py +15 -0
  17. redis-6.3.0/redis/commands/json/_util.py +0 -3
  18. {redis-6.3.0 → redis-6.4.0}/.gitignore +0 -0
  19. {redis-6.3.0 → redis-6.4.0}/LICENSE +0 -0
  20. {redis-6.3.0 → redis-6.4.0}/README.md +0 -0
  21. {redis-6.3.0 → redis-6.4.0}/dev_requirements.txt +0 -0
  22. {redis-6.3.0 → redis-6.4.0}/pyproject.toml +0 -0
  23. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/__init__.py +0 -0
  24. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/base.py +0 -0
  25. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/commands.py +0 -0
  26. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/encoders.py +0 -0
  27. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/helpers.py +0 -0
  28. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/hiredis.py +0 -0
  29. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/resp2.py +0 -0
  30. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/resp3.py +0 -0
  31. {redis-6.3.0 → redis-6.4.0}/redis/_parsers/socket.py +0 -0
  32. {redis-6.3.0 → redis-6.4.0}/redis/asyncio/__init__.py +0 -0
  33. {redis-6.3.0 → redis-6.4.0}/redis/asyncio/client.py +0 -0
  34. {redis-6.3.0 → redis-6.4.0}/redis/asyncio/connection.py +0 -0
  35. {redis-6.3.0 → redis-6.4.0}/redis/asyncio/lock.py +0 -0
  36. {redis-6.3.0 → redis-6.4.0}/redis/asyncio/retry.py +0 -0
  37. {redis-6.3.0 → redis-6.4.0}/redis/asyncio/sentinel.py +0 -0
  38. {redis-6.3.0 → redis-6.4.0}/redis/asyncio/utils.py +0 -0
  39. {redis-6.3.0 → redis-6.4.0}/redis/auth/__init__.py +0 -0
  40. {redis-6.3.0 → redis-6.4.0}/redis/auth/err.py +0 -0
  41. {redis-6.3.0 → redis-6.4.0}/redis/auth/idp.py +0 -0
  42. {redis-6.3.0 → redis-6.4.0}/redis/auth/token.py +0 -0
  43. {redis-6.3.0 → redis-6.4.0}/redis/auth/token_manager.py +0 -0
  44. {redis-6.3.0 → redis-6.4.0}/redis/backoff.py +0 -0
  45. {redis-6.3.0 → redis-6.4.0}/redis/cache.py +0 -0
  46. {redis-6.3.0 → redis-6.4.0}/redis/cluster.py +0 -0
  47. {redis-6.3.0 → redis-6.4.0}/redis/commands/__init__.py +0 -0
  48. {redis-6.3.0 → redis-6.4.0}/redis/commands/bf/__init__.py +0 -0
  49. {redis-6.3.0 → redis-6.4.0}/redis/commands/bf/commands.py +0 -0
  50. {redis-6.3.0 → redis-6.4.0}/redis/commands/bf/info.py +0 -0
  51. {redis-6.3.0 → redis-6.4.0}/redis/commands/cluster.py +0 -0
  52. {redis-6.3.0 → redis-6.4.0}/redis/commands/core.py +0 -0
  53. {redis-6.3.0 → redis-6.4.0}/redis/commands/helpers.py +0 -0
  54. {redis-6.3.0 → redis-6.4.0}/redis/commands/json/__init__.py +0 -0
  55. {redis-6.3.0 → redis-6.4.0}/redis/commands/json/commands.py +0 -0
  56. {redis-6.3.0 → redis-6.4.0}/redis/commands/json/decoders.py +0 -0
  57. {redis-6.3.0 → redis-6.4.0}/redis/commands/json/path.py +0 -0
  58. {redis-6.3.0 → redis-6.4.0}/redis/commands/redismodules.py +0 -0
  59. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/__init__.py +0 -0
  60. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/_util.py +0 -0
  61. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/dialect.py +0 -0
  62. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/document.py +0 -0
  63. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/field.py +0 -0
  64. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/index_definition.py +0 -0
  65. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/profile_information.py +0 -0
  66. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/query.py +0 -0
  67. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/querystring.py +0 -0
  68. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/reducers.py +0 -0
  69. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/result.py +0 -0
  70. {redis-6.3.0 → redis-6.4.0}/redis/commands/search/suggestion.py +0 -0
  71. {redis-6.3.0 → redis-6.4.0}/redis/commands/sentinel.py +0 -0
  72. {redis-6.3.0 → redis-6.4.0}/redis/commands/timeseries/__init__.py +0 -0
  73. {redis-6.3.0 → redis-6.4.0}/redis/commands/timeseries/commands.py +0 -0
  74. {redis-6.3.0 → redis-6.4.0}/redis/commands/timeseries/info.py +0 -0
  75. {redis-6.3.0 → redis-6.4.0}/redis/commands/timeseries/utils.py +0 -0
  76. {redis-6.3.0 → redis-6.4.0}/redis/commands/vectorset/__init__.py +0 -0
  77. {redis-6.3.0 → redis-6.4.0}/redis/commands/vectorset/utils.py +0 -0
  78. {redis-6.3.0 → redis-6.4.0}/redis/crc.py +0 -0
  79. {redis-6.3.0 → redis-6.4.0}/redis/credentials.py +0 -0
  80. {redis-6.3.0 → redis-6.4.0}/redis/exceptions.py +0 -0
  81. {redis-6.3.0 → redis-6.4.0}/redis/lock.py +0 -0
  82. {redis-6.3.0 → redis-6.4.0}/redis/ocsp.py +0 -0
  83. {redis-6.3.0 → redis-6.4.0}/redis/py.typed +0 -0
  84. {redis-6.3.0 → redis-6.4.0}/redis/retry.py +0 -0
  85. {redis-6.3.0 → redis-6.4.0}/redis/sentinel.py +0 -0
  86. {redis-6.3.0 → redis-6.4.0}/redis/typing.py +0 -0
  87. {redis-6.3.0 → redis-6.4.0}/redis/utils.py +0 -0
  88. {redis-6.3.0 → redis-6.4.0}/tests/__init__.py +0 -0
  89. {redis-6.3.0 → redis-6.4.0}/tests/conftest.py +0 -0
  90. {redis-6.3.0 → redis-6.4.0}/tests/entraid_utils.py +0 -0
  91. {redis-6.3.0 → redis-6.4.0}/tests/mocks.py +0 -0
  92. {redis-6.3.0 → redis-6.4.0}/tests/ssl_utils.py +0 -0
  93. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/__init__.py +0 -0
  94. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/compat.py +0 -0
  95. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/conftest.py +0 -0
  96. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/mocks.py +0 -0
  97. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_bloom.py +0 -0
  98. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_cluster.py +0 -0
  99. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_cluster_transaction.py +0 -0
  100. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_commands.py +0 -0
  101. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_connect.py +0 -0
  102. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_connection.py +0 -0
  103. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_connection_pool.py +0 -0
  104. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_credentials.py +0 -0
  105. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_cwe_404.py +0 -0
  106. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_encoding.py +0 -0
  107. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_hash.py +0 -0
  108. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_json.py +0 -0
  109. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_lock.py +0 -0
  110. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_monitor.py +0 -0
  111. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_pipeline.py +0 -0
  112. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_pubsub.py +0 -0
  113. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_retry.py +0 -0
  114. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_scripting.py +0 -0
  115. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_search.py +0 -0
  116. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_sentinel.py +0 -0
  117. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_sentinel_managed_connection.py +0 -0
  118. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_ssl.py +0 -0
  119. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_timeseries.py +0 -0
  120. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/test_utils.py +0 -0
  121. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/testdata/jsontestdata.py +0 -0
  122. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/testdata/titles.csv +0 -0
  123. {redis-6.3.0 → redis-6.4.0}/tests/test_asyncio/testdata/will_play_text.csv.bz2 +0 -0
  124. {redis-6.3.0 → redis-6.4.0}/tests/test_auth/__init__.py +0 -0
  125. {redis-6.3.0 → redis-6.4.0}/tests/test_auth/test_token.py +0 -0
  126. {redis-6.3.0 → redis-6.4.0}/tests/test_auth/test_token_manager.py +0 -0
  127. {redis-6.3.0 → redis-6.4.0}/tests/test_backoff.py +0 -0
  128. {redis-6.3.0 → redis-6.4.0}/tests/test_bloom.py +0 -0
  129. {redis-6.3.0 → redis-6.4.0}/tests/test_cache.py +0 -0
  130. {redis-6.3.0 → redis-6.4.0}/tests/test_cluster.py +0 -0
  131. {redis-6.3.0 → redis-6.4.0}/tests/test_command_parser.py +0 -0
  132. {redis-6.3.0 → redis-6.4.0}/tests/test_commands.py +0 -0
  133. {redis-6.3.0 → redis-6.4.0}/tests/test_connect.py +0 -0
  134. {redis-6.3.0 → redis-6.4.0}/tests/test_connection_pool.py +0 -0
  135. {redis-6.3.0 → redis-6.4.0}/tests/test_encoding.py +0 -0
  136. {redis-6.3.0 → redis-6.4.0}/tests/test_function.py +0 -0
  137. {redis-6.3.0 → redis-6.4.0}/tests/test_hash.py +0 -0
  138. {redis-6.3.0 → redis-6.4.0}/tests/test_helpers.py +0 -0
  139. {redis-6.3.0 → redis-6.4.0}/tests/test_json.py +0 -0
  140. {redis-6.3.0 → redis-6.4.0}/tests/test_lock.py +0 -0
  141. {redis-6.3.0 → redis-6.4.0}/tests/test_max_connections_error.py +0 -0
  142. {redis-6.3.0 → redis-6.4.0}/tests/test_monitor.py +0 -0
  143. {redis-6.3.0 → redis-6.4.0}/tests/test_multiprocessing.py +0 -0
  144. {redis-6.3.0 → redis-6.4.0}/tests/test_parsers/test_helpers.py +0 -0
  145. {redis-6.3.0 → redis-6.4.0}/tests/test_pipeline.py +0 -0
  146. {redis-6.3.0 → redis-6.4.0}/tests/test_pubsub.py +0 -0
  147. {redis-6.3.0 → redis-6.4.0}/tests/test_retry.py +0 -0
  148. {redis-6.3.0 → redis-6.4.0}/tests/test_scripting.py +0 -0
  149. {redis-6.3.0 → redis-6.4.0}/tests/test_sentinel.py +0 -0
  150. {redis-6.3.0 → redis-6.4.0}/tests/test_sentinel_managed_connection.py +0 -0
  151. {redis-6.3.0 → redis-6.4.0}/tests/test_ssl.py +0 -0
  152. {redis-6.3.0 → redis-6.4.0}/tests/test_timeseries.py +0 -0
  153. {redis-6.3.0 → redis-6.4.0}/tests/test_utils.py +0 -0
  154. {redis-6.3.0 → redis-6.4.0}/tests/testdata/jsontestdata.py +0 -0
  155. {redis-6.3.0 → redis-6.4.0}/tests/testdata/titles.csv +0 -0
  156. {redis-6.3.0 → redis-6.4.0}/tests/testdata/will_play_text.csv.bz2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redis
3
- Version: 6.3.0
3
+ Version: 6.4.0
4
4
  Summary: Python client for Redis database and key-value store
5
5
  Project-URL: Changes, https://github.com/redis/redis-py/releases
6
6
  Project-URL: Code, https://github.com/redis/redis-py
@@ -46,8 +46,7 @@ def int_or_str(value):
46
46
  return value
47
47
 
48
48
 
49
- # This version is used when building the package for publishing
50
- __version__ = "6.3.0"
49
+ __version__ = "6.4.0"
51
50
  VERSION = tuple(map(int_or_str, __version__.split(".")))
52
51
 
53
52
 
@@ -1587,15 +1587,6 @@ class ClusterPipeline(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterComm
1587
1587
  def __await__(self) -> Generator[Any, None, "ClusterPipeline"]:
1588
1588
  return self.initialize().__await__()
1589
1589
 
1590
- def __enter__(self) -> "ClusterPipeline":
1591
- # TODO: Remove this method before 7.0.0
1592
- self._execution_strategy._command_queue = []
1593
- return self
1594
-
1595
- def __exit__(self, exc_type: None, exc_value: None, traceback: None) -> None:
1596
- # TODO: Remove this method before 7.0.0
1597
- self._execution_strategy._command_queue = []
1598
-
1599
1590
  def __bool__(self) -> bool:
1600
1591
  "Pipeline instances should always evaluate to True on Python 3+"
1601
1592
  return True
@@ -368,9 +368,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
368
368
  ]:
369
369
  raise RedisError("Client caching is only supported with RESP version 3")
370
370
 
371
- # TODO: To avoid breaking changes during the bug fix, we have to keep non-reentrant lock.
372
- # TODO: Remove this before next major version (7.0.0)
373
- self.single_connection_lock = threading.Lock()
371
+ self.single_connection_lock = threading.RLock()
374
372
  self.connection = None
375
373
  self._single_connection_client = single_connection_client
376
374
  if self._single_connection_client:
@@ -776,9 +774,7 @@ class PubSub:
776
774
  else:
777
775
  self._event_dispatcher = event_dispatcher
778
776
 
779
- # TODO: To avoid breaking changes during the bug fix, we have to keep non-reentrant lock.
780
- # TODO: Remove this before next major version (7.0.0)
781
- self._lock = threading.Lock()
777
+ self._lock = threading.RLock()
782
778
  if self.encoder is None:
783
779
  self.encoder = self.connection_pool.get_encoder()
784
780
  self.health_check_response_b = self.encoder.encode(self.HEALTH_CHECK_MESSAGE)
@@ -0,0 +1,5 @@
1
+ from typing import List, Mapping, Union
2
+
3
+ JsonType = Union[
4
+ str, int, float, bool, None, Mapping[str, "JsonType"], List["JsonType"]
5
+ ]
@@ -26,7 +26,7 @@ class Reducer:
26
26
 
27
27
  NAME = None
28
28
 
29
- def __init__(self, *args: List[str]) -> None:
29
+ def __init__(self, *args: str) -> None:
30
30
  self._args = args
31
31
  self._field = None
32
32
  self._alias = None
@@ -116,7 +116,7 @@ class AggregateRequest:
116
116
  self._add_scores = False
117
117
  self._scorer = "TFIDF"
118
118
 
119
- def load(self, *fields: List[str]) -> "AggregateRequest":
119
+ def load(self, *fields: str) -> "AggregateRequest":
120
120
  """
121
121
  Indicate the fields to be returned in the response. These fields are
122
122
  returned in addition to any others implicitly specified.
@@ -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
@@ -542,7 +542,7 @@ class SearchCommands:
542
542
 
543
543
  def aggregate(
544
544
  self,
545
- query: Union[str, Query],
545
+ query: Union[AggregateRequest, Cursor],
546
546
  query_params: Dict[str, Union[str, int, float]] = None,
547
547
  ):
548
548
  """
@@ -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):
@@ -967,7 +967,7 @@ class AsyncSearchCommands(SearchCommands):
967
967
 
968
968
  async def aggregate(
969
969
  self,
970
- query: Union[str, Query],
970
+ query: Union[AggregateResult, Cursor],
971
971
  query_params: Dict[str, Union[str, int, float]] = None,
972
972
  ):
973
973
  """
@@ -129,6 +129,7 @@ class VectorSetCommands(CommandsProtocol):
129
129
  filter_ef: Optional[str] = None,
130
130
  truth: Optional[bool] = False,
131
131
  no_thread: Optional[bool] = False,
132
+ epsilon: Optional[Number] = None,
132
133
  ) -> Union[
133
134
  Awaitable[Optional[List[Union[List[EncodableT], Dict[EncodableT, Number]]]]],
134
135
  Optional[List[Union[List[EncodableT], Dict[EncodableT, Number]]]],
@@ -152,6 +153,9 @@ class VectorSetCommands(CommandsProtocol):
152
153
  ``no_thread`` when enabled forces the command to execute the search
153
154
  on the data structure in the main thread.
154
155
 
156
+ ``epsilon`` floating point between 0 and 1, if specified will return
157
+ only elements with distance no further than the specified one.
158
+
155
159
  For more information see https://redis.io/commands/vsim
156
160
  """
157
161
 
@@ -176,6 +180,9 @@ class VectorSetCommands(CommandsProtocol):
176
180
  if count:
177
181
  pieces.extend(["COUNT", count])
178
182
 
183
+ if epsilon:
184
+ pieces.extend(["EPSILON", epsilon])
185
+
179
186
  if ef:
180
187
  pieces.extend(["EF", ef])
181
188
 
@@ -816,7 +816,7 @@ class CacheProxyConnection(ConnectionInterface):
816
816
  self,
817
817
  conn: ConnectionInterface,
818
818
  cache: CacheInterface,
819
- pool_lock: threading.Lock,
819
+ pool_lock: threading.RLock,
820
820
  ):
821
821
  self.pid = os.getpid()
822
822
  self._conn = conn
@@ -1429,13 +1429,7 @@ class ConnectionPool:
1429
1429
  # release the lock.
1430
1430
 
1431
1431
  self._fork_lock = threading.RLock()
1432
-
1433
- if self.cache is None:
1434
- self._lock = threading.RLock()
1435
- else:
1436
- # TODO: To avoid breaking changes during the bug fix, we have to keep non-reentrant lock.
1437
- # TODO: Remove this before next major version (7.0.0)
1438
- self._lock = threading.Lock()
1432
+ self._lock = threading.RLock()
1439
1433
 
1440
1434
  self.reset()
1441
1435
 
@@ -152,7 +152,7 @@ class AfterSingleConnectionInstantiationEvent:
152
152
  self,
153
153
  connection,
154
154
  client_type: ClientType,
155
- connection_lock: Union[threading.Lock, asyncio.Lock],
155
+ connection_lock: Union[threading.RLock, asyncio.Lock],
156
156
  ):
157
157
  self._connection = connection
158
158
  self._client_type = client_type
@@ -167,7 +167,7 @@ class AfterSingleConnectionInstantiationEvent:
167
167
  return self._client_type
168
168
 
169
169
  @property
170
- def connection_lock(self) -> Union[threading.Lock, asyncio.Lock]:
170
+ def connection_lock(self) -> Union[threading.RLock, asyncio.Lock]:
171
171
  return self._connection_lock
172
172
 
173
173
 
@@ -177,7 +177,7 @@ class AfterPubSubConnectionInstantiationEvent:
177
177
  pubsub_connection,
178
178
  connection_pool,
179
179
  client_type: ClientType,
180
- connection_lock: Union[threading.Lock, asyncio.Lock],
180
+ connection_lock: Union[threading.RLock, asyncio.Lock],
181
181
  ):
182
182
  self._pubsub_connection = pubsub_connection
183
183
  self._connection_pool = connection_pool
@@ -197,7 +197,7 @@ class AfterPubSubConnectionInstantiationEvent:
197
197
  return self._client_type
198
198
 
199
199
  @property
200
- def connection_lock(self) -> Union[threading.Lock, asyncio.Lock]:
200
+ def connection_lock(self) -> Union[threading.RLock, asyncio.Lock]:
201
201
  return self._connection_lock
202
202
 
203
203
 
@@ -423,6 +423,21 @@ async def test_vsim_truth_no_thread_enabled(d_client):
423
423
  assert isinstance(sim_no_thread, dict)
424
424
 
425
425
 
426
+ @skip_if_server_version_lt("8.2.0")
427
+ async def test_vsim_epsilon(d_client):
428
+ await d_client.vset().vadd("myset", [2, 1, 1], "a")
429
+ await d_client.vset().vadd("myset", [2, 0, 1], "b")
430
+ await d_client.vset().vadd("myset", [2, 0, 0], "c")
431
+ await d_client.vset().vadd("myset", [2, 0, -1], "d")
432
+ await d_client.vset().vadd("myset", [2, -1, -1], "e")
433
+
434
+ res1 = await d_client.vset().vsim("myset", [2, 1, 1])
435
+ assert 5 == len(res1)
436
+
437
+ res2 = await d_client.vset().vsim("myset", [2, 1, 1], epsilon=0.5)
438
+ assert 4 == len(res2)
439
+
440
+
426
441
  @skip_if_server_version_lt("7.9.0")
427
442
  async def test_vdim(d_client):
428
443
  float_array = [1, 4.32, 0.11, 0.5, 0.9, 0.1, 0.2]
@@ -285,7 +285,7 @@ class TestClusterTransaction:
285
285
  mock_pool = Mock(spec=ConnectionPool)
286
286
  mock_pool.get_connection.return_value = mock_connection
287
287
  mock_pool._available_connections = [mock_connection]
288
- mock_pool._lock = threading.Lock()
288
+ mock_pool._lock = threading.RLock()
289
289
 
290
290
  _node_migrating, node_importing = _find_source_and_target_node_for_slot(r, slot)
291
291
  node_importing.redis_connection.connection_pool = mock_pool
@@ -310,7 +310,7 @@ class TestClusterTransaction:
310
310
  mock_pool = Mock(spec=ConnectionPool)
311
311
  mock_pool.get_connection.return_value = mock_connection
312
312
  mock_pool._available_connections = [mock_connection]
313
- mock_pool._lock = threading.Lock()
313
+ mock_pool._lock = threading.RLock()
314
314
 
315
315
  _node_migrating, node_importing = _find_source_and_target_node_for_slot(r, slot)
316
316
  node_importing.redis_connection.connection_pool = mock_pool
@@ -442,7 +442,7 @@ class TestUnitCacheProxyConnection:
442
442
  mock_connection.credential_provider = UsernamePasswordCredentialProvider()
443
443
 
444
444
  proxy_connection = CacheProxyConnection(
445
- mock_connection, cache, threading.Lock()
445
+ mock_connection, cache, threading.RLock()
446
446
  )
447
447
  proxy_connection.disconnect()
448
448
 
@@ -492,7 +492,7 @@ class TestUnitCacheProxyConnection:
492
492
  mock_connection.can_read.return_value = False
493
493
 
494
494
  proxy_connection = CacheProxyConnection(
495
- mock_connection, mock_cache, threading.Lock()
495
+ mock_connection, mock_cache, threading.RLock()
496
496
  )
497
497
  proxy_connection.send_command(*["GET", "foo"], **{"keys": ["foo"]})
498
498
  assert proxy_connection.read_response() == b"bar"
@@ -554,7 +554,7 @@ class TestUnitCacheProxyConnection:
554
554
  mock_connection.can_read.return_value = False
555
555
 
556
556
  proxy_connection = CacheProxyConnection(
557
- mock_connection, mock_cache, threading.Lock()
557
+ mock_connection, mock_cache, threading.RLock()
558
558
  )
559
559
  proxy_connection.send_command(*["GET", "foo"], **{"keys": ["foo"]})
560
560
 
@@ -323,7 +323,7 @@ class TestStreamingCredentialProvider:
323
323
  }
324
324
  mock_pool.get_connection.return_value = mock_connection
325
325
  mock_pool._available_connections = [mock_connection, mock_another_connection]
326
- mock_pool._lock = threading.Lock()
326
+ mock_pool._lock = threading.RLock()
327
327
  auth_token = None
328
328
 
329
329
  def re_auth_callback(token):
@@ -382,7 +382,7 @@ class TestStreamingCredentialProvider:
382
382
  mock_another_connection,
383
383
  mock_failed_connection,
384
384
  ]
385
- mock_pool._lock = threading.Lock()
385
+ mock_pool._lock = threading.RLock()
386
386
 
387
387
  def _raise(error: RedisError):
388
388
  pass
@@ -442,7 +442,7 @@ class TestStreamingCredentialProvider:
442
442
  mock_another_connection,
443
443
  ]
444
444
  mock_pool._available_connections = [mock_another_connection]
445
- mock_pool._lock = threading.Lock()
445
+ mock_pool._lock = threading.RLock()
446
446
  auth_token = None
447
447
 
448
448
  def re_auth_callback(token):
@@ -502,7 +502,7 @@ class TestStreamingCredentialProvider:
502
502
  mock_another_connection,
503
503
  ]
504
504
  mock_pool._available_connections = [mock_another_connection]
505
- mock_pool._lock = threading.Lock()
505
+ mock_pool._lock = threading.RLock()
506
506
  auth_token = None
507
507
 
508
508
  def re_auth_callback(token):
@@ -560,7 +560,7 @@ class TestStreamingCredentialProvider:
560
560
  }
561
561
  mock_pool.get_connection.return_value = mock_connection
562
562
  mock_pool._available_connections = [mock_connection, mock_another_connection]
563
- mock_pool._lock = threading.Lock()
563
+ mock_pool._lock = threading.RLock()
564
564
 
565
565
  Redis(
566
566
  connection_pool=mock_pool,
@@ -3802,3 +3802,52 @@ def test_svs_vamana_vector_search_with_parameters(client):
3802
3802
  else:
3803
3803
  assert res["total_results"] == 3
3804
3804
  assert "doc0" == res["results"][0]["id"]
3805
+
3806
+
3807
+ @pytest.mark.redismod
3808
+ @skip_ifmodversion_lt("2.4.3", "search")
3809
+ @skip_if_server_version_lt("8.1.224")
3810
+ def test_svs_vamana_vector_search_with_parameters_leanvec(client):
3811
+ client.ft().create_index(
3812
+ (
3813
+ VectorField(
3814
+ "v",
3815
+ "SVS-VAMANA",
3816
+ {
3817
+ "TYPE": "FLOAT32",
3818
+ "DIM": 8,
3819
+ "DISTANCE_METRIC": "L2",
3820
+ "COMPRESSION": "LeanVec8x8", # LeanVec compression required for REDUCE
3821
+ "CONSTRUCTION_WINDOW_SIZE": 200,
3822
+ "GRAPH_MAX_DEGREE": 32,
3823
+ "SEARCH_WINDOW_SIZE": 15,
3824
+ "EPSILON": 0.01,
3825
+ "TRAINING_THRESHOLD": 1024,
3826
+ "REDUCE": 4, # Half of DIM (8/2 = 4)
3827
+ },
3828
+ ),
3829
+ )
3830
+ )
3831
+
3832
+ # Create test vectors (8-dimensional to match DIM)
3833
+ vectors = [
3834
+ [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0],
3835
+ [2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0],
3836
+ [3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0],
3837
+ [4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0],
3838
+ [5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0],
3839
+ ]
3840
+
3841
+ for i, vec in enumerate(vectors):
3842
+ client.hset(f"doc{i}", "v", np.array(vec, dtype=np.float32).tobytes())
3843
+
3844
+ query = Query("*=>[KNN 3 @v $vec as score]").no_content()
3845
+ query_params = {"vec": np.array(vectors[0], dtype=np.float32).tobytes()}
3846
+
3847
+ res = client.ft().search(query, query_params=query_params)
3848
+ if is_resp2_connection(client):
3849
+ assert res.total == 3
3850
+ assert "doc0" == res.docs[0].id
3851
+ else:
3852
+ assert res["total_results"] == 3
3853
+ assert "doc0" == res["results"][0]["id"]
@@ -425,6 +425,21 @@ def test_vsim_truth_no_thread_enabled(d_client):
425
425
  assert isinstance(sim_no_thread, dict)
426
426
 
427
427
 
428
+ @skip_if_server_version_lt("8.2.0")
429
+ def test_vsim_epsilon(d_client):
430
+ d_client.vset().vadd("myset", [2, 1, 1], "a")
431
+ d_client.vset().vadd("myset", [2, 0, 1], "b")
432
+ d_client.vset().vadd("myset", [2, 0, 0], "c")
433
+ d_client.vset().vadd("myset", [2, 0, -1], "d")
434
+ d_client.vset().vadd("myset", [2, -1, -1], "e")
435
+
436
+ res1 = d_client.vset().vsim("myset", [2, 1, 1])
437
+ assert 5 == len(res1)
438
+
439
+ res2 = d_client.vset().vsim("myset", [2, 1, 1], epsilon=0.5)
440
+ assert 4 == len(res2)
441
+
442
+
428
443
  @skip_if_server_version_lt("7.9.0")
429
444
  def test_vdim(d_client):
430
445
  float_array = [1, 4.32, 0.11, 0.5, 0.9, 0.1, 0.2]
@@ -1,3 +0,0 @@
1
- from typing import Any, Dict, List, Union
2
-
3
- JsonType = Union[str, int, float, bool, None, Dict[str, Any], List[Any]]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes