coredis 5.0.0rc1__tar.gz → 5.0.1__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.

Potentially problematic release.


This version of coredis might be problematic. Click here for more details.

Files changed (113) hide show
  1. {coredis-5.0.0rc1 → coredis-5.0.1}/HISTORY.rst +43 -6
  2. {coredis-5.0.0rc1/coredis.egg-info → coredis-5.0.1}/PKG-INFO +1 -5
  3. {coredis-5.0.0rc1 → coredis-5.0.1}/README.md +0 -3
  4. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/_protocols.py +0 -23
  5. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/_utils.py +8 -4
  6. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/_version.py +3 -3
  7. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/core.py +50 -8
  8. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/script.py +19 -20
  9. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/sentinel.py +9 -7
  10. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/pipeline.py +124 -137
  11. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/sentinel.py +28 -25
  12. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/vector_sets.py +44 -11
  13. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/retry.py +12 -13
  14. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/sentinel.py +18 -5
  15. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/tokens.py +5 -0
  16. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/typing.py +1 -3
  17. {coredis-5.0.0rc1 → coredis-5.0.1/coredis.egg-info}/PKG-INFO +1 -5
  18. {coredis-5.0.0rc1 → coredis-5.0.1}/pyproject.toml +1 -1
  19. {coredis-5.0.0rc1 → coredis-5.0.1}/requirements/dev.txt +1 -1
  20. {coredis-5.0.0rc1 → coredis-5.0.1}/setup.py +0 -1
  21. {coredis-5.0.0rc1 → coredis-5.0.1}/LICENSE +0 -0
  22. {coredis-5.0.0rc1 → coredis-5.0.1}/MANIFEST.in +0 -0
  23. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/__init__.py +0 -0
  24. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/_json.py +0 -0
  25. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/_packer.py +0 -0
  26. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/_py_311_typing.py +0 -0
  27. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/_py_312_typing.py +0 -0
  28. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/_sidecar.py +0 -0
  29. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/cache.py +0 -0
  30. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/client/__init__.py +0 -0
  31. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/client/basic.py +0 -0
  32. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/client/cluster.py +0 -0
  33. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/__init__.py +0 -0
  34. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/_key_spec.py +0 -0
  35. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/_utils.py +0 -0
  36. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/_validators.py +0 -0
  37. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/_wrappers.py +0 -0
  38. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/bitfield.py +0 -0
  39. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/constants.py +0 -0
  40. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/function.py +0 -0
  41. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/monitor.py +0 -0
  42. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/pubsub.py +0 -0
  43. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/commands/request.py +0 -0
  44. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/config.py +0 -0
  45. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/connection.py +0 -0
  46. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/constants.py +0 -0
  47. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/credentials.py +0 -0
  48. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/exceptions.py +0 -0
  49. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/experimental/__init__.py +0 -0
  50. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/globals.py +0 -0
  51. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/__init__.py +0 -0
  52. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/autocomplete.py +0 -0
  53. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/base.py +0 -0
  54. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/filters.py +0 -0
  55. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/graph.py +0 -0
  56. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/json.py +0 -0
  57. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/response/__init__.py +0 -0
  58. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/response/_callbacks/__init__.py +0 -0
  59. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/response/_callbacks/autocomplete.py +0 -0
  60. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/response/_callbacks/graph.py +0 -0
  61. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/response/_callbacks/json.py +0 -0
  62. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/response/_callbacks/search.py +0 -0
  63. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/response/_callbacks/timeseries.py +0 -0
  64. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/response/types.py +0 -0
  65. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/search.py +0 -0
  66. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/modules/timeseries.py +0 -0
  67. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/parser.py +0 -0
  68. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/pool/__init__.py +0 -0
  69. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/pool/basic.py +0 -0
  70. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/pool/cluster.py +0 -0
  71. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/pool/nodemanager.py +0 -0
  72. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/py.typed +0 -0
  73. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/recipes/__init__.py +0 -0
  74. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/recipes/credentials/__init__.py +0 -0
  75. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/recipes/credentials/iam_provider.py +0 -0
  76. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/recipes/locks/__init__.py +0 -0
  77. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/recipes/locks/extend.lua +0 -0
  78. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/recipes/locks/lua_lock.py +0 -0
  79. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/recipes/locks/release.lua +0 -0
  80. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/__init__.py +0 -0
  81. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/__init__.py +0 -0
  82. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/acl.py +0 -0
  83. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/cluster.py +0 -0
  84. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/command.py +0 -0
  85. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/connection.py +0 -0
  86. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/geo.py +0 -0
  87. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/hash.py +0 -0
  88. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/keys.py +0 -0
  89. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/module.py +0 -0
  90. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/script.py +0 -0
  91. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/server.py +0 -0
  92. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/sets.py +0 -0
  93. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/sorted_set.py +0 -0
  94. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/streams.py +0 -0
  95. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_callbacks/strings.py +0 -0
  96. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/_utils.py +0 -0
  97. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/response/types.py +0 -0
  98. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/speedups.c +0 -0
  99. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/speedups.pyi +0 -0
  100. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis/stream.py +0 -0
  101. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis.egg-info/SOURCES.txt +0 -0
  102. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis.egg-info/dependency_links.txt +0 -0
  103. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis.egg-info/requires.txt +0 -0
  104. {coredis-5.0.0rc1 → coredis-5.0.1}/coredis.egg-info/top_level.txt +0 -0
  105. {coredis-5.0.0rc1 → coredis-5.0.1}/requirements/ci.txt +0 -0
  106. {coredis-5.0.0rc1 → coredis-5.0.1}/requirements/dev_extra.txt +0 -0
  107. {coredis-5.0.0rc1 → coredis-5.0.1}/requirements/docs.txt +0 -0
  108. {coredis-5.0.0rc1 → coredis-5.0.1}/requirements/main.txt +0 -0
  109. {coredis-5.0.0rc1 → coredis-5.0.1}/requirements/publishing.txt +0 -0
  110. {coredis-5.0.0rc1 → coredis-5.0.1}/requirements/recipes.txt +0 -0
  111. {coredis-5.0.0rc1 → coredis-5.0.1}/requirements/test.txt +0 -0
  112. {coredis-5.0.0rc1 → coredis-5.0.1}/setup.cfg +0 -0
  113. {coredis-5.0.0rc1 → coredis-5.0.1}/versioneer.py +0 -0
@@ -3,6 +3,49 @@
3
3
  Changelog
4
4
  =========
5
5
 
6
+ v5.0.1
7
+ ------
8
+ Release Date: 2025-07-18
9
+
10
+ * Bug Fix
11
+
12
+ * Fix regression caused by ``5.0.0`` which completely broke the use of Sentinel
13
+ with ``decode_responses=False``.
14
+
15
+ v5.0.0
16
+ ------
17
+ Release Date: 2025-07-16
18
+
19
+ * Features
20
+
21
+ * Add support for using custom types with redis commands
22
+ by registering serializers and deserializers
23
+ * Allow stacking pipeline commands synchronously
24
+ * Expose statically typed responses for pipeline commands
25
+
26
+
27
+ * Compatibility
28
+
29
+ * Redis command methods are no longer coroutines and instead
30
+ synchronous methods that return subclasses of ``Awaitable``
31
+ (``CommandRequest``) which can be awaited as before.
32
+ * Add support for redis 8.0 vector set commands
33
+ * Add support for redis 8.0 hash expiry commands
34
+ * Remove deprecated pubsub ``listen`` and threaded worker APIs
35
+ * Remove support for KeyDB
36
+
37
+ * Performance
38
+
39
+ * Streamline client side cache shrinking
40
+
41
+ v5.0.0rc2
42
+ ---------
43
+ Release Date: 2025-07-10
44
+
45
+ * Bug Fix
46
+
47
+ * Fix duplicate command error in using ``transform`` with pipeline
48
+
6
49
  v5.0.0rc1
7
50
  ---------
8
51
  Release Date: 2025-07-07
@@ -1957,9 +2000,3 @@ v1.0.1
1957
2000
  * fix bug of `PubSub.run_in_thread`
1958
2001
  * add more examples
1959
2002
  * change `Script.register` to `Script.execute`
1960
-
1961
-
1962
-
1963
-
1964
-
1965
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coredis
3
- Version: 5.0.0rc1
3
+ Version: 5.0.1
4
4
  Summary: Python async client for Redis key-value store
5
5
  Home-page: https://github.com/alisaifee/coredis
6
6
  Author: Ali-Akber Saifee
@@ -14,7 +14,6 @@ Project-URL: Documentation, https://coredis.readthedocs.org
14
14
  Keywords: Redis,key-value store,asyncio
15
15
  Classifier: Development Status :: 5 - Production/Stable
16
16
  Classifier: Intended Audience :: Developers
17
- Classifier: License :: OSI Approved :: MIT License
18
17
  Classifier: Operating System :: OS Independent
19
18
  Classifier: Programming Language :: Python
20
19
  Classifier: Programming Language :: Python :: 3.10
@@ -80,9 +79,6 @@ coredis is an async redis client with support for redis server, cluster & sentin
80
79
  and the [API Documentation](https://coredis.readthedocs.io/en/latest/api/index.html)
81
80
  for more details.
82
81
 
83
- > **Warning**
84
- > The command API does NOT mirror the official python [redis client](https://github.com/redis/redis-py). For details about the high level differences refer to [Divergence from aredis & redis-py](https://coredis.readthedocs.io/en/latest/history.html#divergence-from-aredis-redis-py)
85
-
86
82
  ______________________________________________________________________
87
83
 
88
84
  <!-- TOC depthFrom:2 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 -->
@@ -27,9 +27,6 @@ coredis is an async redis client with support for redis server, cluster & sentin
27
27
  and the [API Documentation](https://coredis.readthedocs.io/en/latest/api/index.html)
28
28
  for more details.
29
29
 
30
- > **Warning**
31
- > The command API does NOT mirror the official python [redis client](https://github.com/redis/redis-py). For details about the high level differences refer to [Divergence from aredis & redis-py](https://coredis.readthedocs.io/en/latest/history.html#divergence-from-aredis-redis-py)
32
-
33
30
  ______________________________________________________________________
34
31
 
35
32
  <!-- TOC depthFrom:2 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 -->
@@ -10,14 +10,10 @@ from coredis.typing import (
10
10
  Awaitable,
11
11
  Callable,
12
12
  ExecutionParameters,
13
- KeyT,
14
- Parameters,
15
13
  Protocol,
16
14
  R,
17
15
  RedisCommandP,
18
- RedisValueT,
19
16
  ResponseType,
20
- StringT,
21
17
  TypeVar,
22
18
  Unpack,
23
19
  ValueT,
@@ -47,25 +43,6 @@ class AbstractExecutor(Protocol):
47
43
  ) -> CommandRequest[R]: ...
48
44
 
49
45
 
50
- @runtime_checkable
51
- class SupportsScript(Protocol[T_co]): # noqa
52
- async def evalsha(
53
- self,
54
- sha1: StringT,
55
- keys: Parameters[KeyT] | None = ...,
56
- args: Parameters[RedisValueT] | None = ...,
57
- ) -> ResponseType: ...
58
-
59
- async def evalsha_ro(
60
- self,
61
- sha1: StringT,
62
- keys: Parameters[KeyT] | None = ...,
63
- args: Parameters[RedisValueT] | None = ...,
64
- ) -> ResponseType: ...
65
-
66
- async def script_load(self, script: StringT) -> T_co: ...
67
-
68
-
69
46
  @runtime_checkable
70
47
  class ConnectionP(Protocol):
71
48
  decode_responses: bool
@@ -72,6 +72,14 @@ class EncodingInsensitiveDict(UserDict[Any, Any]):
72
72
  alt = self._alt_key(key)
73
73
  return self.data.pop(alt, default)
74
74
 
75
+ def stringify_keys(self) -> dict[str, Any]:
76
+ d = {}
77
+ for key, value in self.items():
78
+ d[key.decode(self._encoding) if isinstance(key, bytes) else key] = (
79
+ value.stringify_keys() if isinstance(value, EncodingInsensitiveDict) else value
80
+ )
81
+ return d
82
+
75
83
 
76
84
  @enum.unique
77
85
  class CaseAndEncodingInsensitiveEnum(bytes, enum.Enum):
@@ -120,10 +128,6 @@ def b(x: ResponseType, encoding: str | None = None) -> bytes:
120
128
  return _v.encode(encoding) if encoding else _v.encode()
121
129
 
122
130
 
123
- def defaultvalue(value: U | None, default: T) -> U | T:
124
- return default if value is None else value
125
-
126
-
127
131
  def nativestr(x: ResponseType, encoding: str = "utf-8") -> str:
128
132
  if isinstance(x, (str, bytes)):
129
133
  return x if isinstance(x, str) else x.decode(encoding, "replace")
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-07-07T11:41:52-0700",
11
+ "date": "2025-07-18T09:26:51-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "e2034feabddbed8c1a549ccc53976ca41d724a00",
15
- "version": "5.0.0rc1"
14
+ "full-revisionid": "dbec024933c248c144549a88927631eb9294a9a2",
15
+ "version": "5.0.1"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -7,7 +7,7 @@ from typing import overload
7
7
  from deprecated.sphinx import versionadded
8
8
 
9
9
  from coredis._json import json
10
- from coredis._utils import defaultvalue, dict_to_flat_list, tuples_to_flat_list
10
+ from coredis._utils import dict_to_flat_list, tuples_to_flat_list
11
11
  from coredis.commands import CommandMixin
12
12
  from coredis.commands._utils import (
13
13
  normalized_milliseconds,
@@ -5269,8 +5269,8 @@ class CoreCommands(CommandMixin[AnyStr]):
5269
5269
  """
5270
5270
 
5271
5271
  command_arguments: CommandArgList = [
5272
- defaultvalue(start, "-"),
5273
- defaultvalue(end, "+"),
5272
+ start if start is not None else "-",
5273
+ end if end is not None else "+",
5274
5274
  ]
5275
5275
 
5276
5276
  if count is not None:
@@ -5298,8 +5298,8 @@ class CoreCommands(CommandMixin[AnyStr]):
5298
5298
  IDs interval, in reverse order (from greater to smaller IDs) compared to XRANGE
5299
5299
  """
5300
5300
  command_arguments: CommandArgList = [
5301
- defaultvalue(end, "+"),
5302
- defaultvalue(start, "-"),
5301
+ end if end is not None else "+",
5302
+ start if start is not None else "-",
5303
5303
  ]
5304
5304
 
5305
5305
  if count is not None:
@@ -8110,6 +8110,37 @@ class CoreCommands(CommandMixin[AnyStr]):
8110
8110
  filter_ef: int | None = ...,
8111
8111
  truth: bool | None = ...,
8112
8112
  ) -> CommandRequest[dict[AnyStr, float]]: ...
8113
+ @overload
8114
+ def vsim(
8115
+ self,
8116
+ key: KeyT,
8117
+ *,
8118
+ element: StringT | None = ...,
8119
+ values: Parameters[float] | bytes | None = ...,
8120
+ withattribs: Literal[True],
8121
+ count: int | None = ...,
8122
+ epsilon: float | None = ...,
8123
+ ef: int | None = ...,
8124
+ filter: StringT | None = ...,
8125
+ filter_ef: int | None = ...,
8126
+ truth: bool | None = ...,
8127
+ ) -> CommandRequest[dict[AnyStr, JsonType]]: ...
8128
+ @overload
8129
+ def vsim(
8130
+ self,
8131
+ key: KeyT,
8132
+ *,
8133
+ element: StringT | None = ...,
8134
+ values: Parameters[float] | bytes | None = ...,
8135
+ withscores: Literal[True],
8136
+ withattribs: Literal[True],
8137
+ count: int | None = ...,
8138
+ epsilon: float | None = ...,
8139
+ ef: int | None = ...,
8140
+ filter: StringT | None = ...,
8141
+ filter_ef: int | None = ...,
8142
+ truth: bool | None = ...,
8143
+ ) -> CommandRequest[dict[AnyStr, tuple[float, JsonType]]]: ...
8113
8144
 
8114
8145
  @versionadded(version="5.0.0")
8115
8146
  @mutually_exclusive_parameters("values", "element", required=True)
@@ -8117,6 +8148,7 @@ class CoreCommands(CommandMixin[AnyStr]):
8117
8148
  CommandName.VSIM,
8118
8149
  version_introduced="8.0.0",
8119
8150
  group=CommandGroup.VECTOR_SET,
8151
+ arguments={"withattribs": {"version_introduced": "8.1.240"}},
8120
8152
  )
8121
8153
  def vsim(
8122
8154
  self,
@@ -8125,13 +8157,19 @@ class CoreCommands(CommandMixin[AnyStr]):
8125
8157
  element: StringT | None = None,
8126
8158
  values: Parameters[float] | bytes | None = None,
8127
8159
  withscores: bool | None = None,
8160
+ withattribs: bool | None = None,
8128
8161
  count: int | None = None,
8129
8162
  epsilon: float | None = None,
8130
8163
  ef: int | None = None,
8131
8164
  filter: StringT | None = None,
8132
8165
  filter_ef: int | None = None,
8133
8166
  truth: bool | None = None,
8134
- ) -> CommandRequest[tuple[AnyStr, ...] | dict[AnyStr, float]]:
8167
+ ) -> CommandRequest[
8168
+ tuple[AnyStr, ...]
8169
+ | dict[AnyStr, float]
8170
+ | dict[AnyStr, JsonType]
8171
+ | dict[AnyStr, tuple[float, JsonType]]
8172
+ ]:
8135
8173
  """
8136
8174
  Return elements similar to a given vector or element
8137
8175
 
@@ -8140,6 +8178,7 @@ class CoreCommands(CommandMixin[AnyStr]):
8140
8178
  :param values: either a byte representation of a 32-bit floating point (FP32) blob of values
8141
8179
  or a sequence of doubles representing the vector to use as the similarity reference.
8142
8180
  :param withscores: whether to return similarity scores for each result
8181
+ :param withattribs: whether to include attributes for for each result
8143
8182
  :param count: number of results to limit to
8144
8183
  :param epsilon: distance threshold; results with distance greater than this are
8145
8184
  excluded.
@@ -8148,7 +8187,8 @@ class CoreCommands(CommandMixin[AnyStr]):
8148
8187
  :param filter_ef: limits the number of filtering attempts
8149
8188
  :param truth: forces an exact linear scan of all elements bypassing the HSNW graph
8150
8189
  :return: the matching elements or a mapping of the matching elements to their scores
8151
- if :paramref:`withscores` is ``True``
8190
+ if :paramref:`withscores` is ``True`` and/or their attributes if :paramref:`withattribs`
8191
+ is ``True``
8152
8192
  """
8153
8193
  command_arguments: CommandArgList = [key]
8154
8194
  if values is not None:
@@ -8162,6 +8202,8 @@ class CoreCommands(CommandMixin[AnyStr]):
8162
8202
 
8163
8203
  if withscores:
8164
8204
  command_arguments.append(PureToken.WITHSCORES)
8205
+ if withattribs:
8206
+ command_arguments.append(PureToken.WITHATTRIBS)
8165
8207
  if count is not None:
8166
8208
  command_arguments.extend([PrefixToken.COUNT, count])
8167
8209
  if ef is not None:
@@ -8177,7 +8219,7 @@ class CoreCommands(CommandMixin[AnyStr]):
8177
8219
  return self.create_request(
8178
8220
  CommandName.VSIM,
8179
8221
  *command_arguments,
8180
- callback=VSimCallback[AnyStr](withscores=withscores),
8222
+ callback=VSimCallback[AnyStr](withscores=withscores, withattribs=withattribs),
8181
8223
  )
8182
8224
 
8183
8225
  @versionadded(version="5.0.0")
@@ -8,9 +8,9 @@ from typing import TYPE_CHECKING, Any, cast
8
8
 
9
9
  from deprecated.sphinx import versionadded
10
10
 
11
- from coredis._protocols import SupportsScript
12
11
  from coredis._utils import b
13
12
  from coredis.exceptions import NoScriptError
13
+ from coredis.retry import ConstantRetryPolicy, retryable
14
14
  from coredis.typing import (
15
15
  AnyStr,
16
16
  Awaitable,
@@ -23,6 +23,7 @@ from coredis.typing import (
23
23
  RedisValueT,
24
24
  ResponseType,
25
25
  StringT,
26
+ ValueT,
26
27
  add_runtime_checks,
27
28
  safe_beartype,
28
29
  )
@@ -51,7 +52,7 @@ class Script(Generic[AnyStr]):
51
52
 
52
53
  def __init__(
53
54
  self,
54
- registered_client: SupportsScript[AnyStr] | None = None,
55
+ registered_client: coredis.client.Client[AnyStr] | None = None,
55
56
  script: StringT | None = None,
56
57
  readonly: bool = False,
57
58
  ):
@@ -64,7 +65,7 @@ class Script(Generic[AnyStr]):
64
65
  :param readonly: If ``True`` the script will be called with
65
66
  :meth:`coredis.Redis.evalsha_ro` instead of :meth:`coredis.Redis.evalsha`
66
67
  """
67
- self.registered_client: SupportsScript[AnyStr] | None = registered_client
68
+ self.registered_client: coredis.client.Client[AnyStr] | None = registered_client
68
69
  self.script: StringT
69
70
  if not script:
70
71
  raise RuntimeError("No script provided")
@@ -72,16 +73,16 @@ class Script(Generic[AnyStr]):
72
73
  self.sha = hashlib.sha1(b(script)).hexdigest() # type: ignore
73
74
  self.readonly = readonly
74
75
 
75
- async def __call__(
76
+ def __call__(
76
77
  self,
77
78
  keys: Parameters[KeyT] | None = None,
78
- args: Parameters[RedisValueT] | None = None,
79
- client: SupportsScript[AnyStr] | None = None,
79
+ args: Parameters[ValueT] | None = None,
80
+ client: coredis.client.Client[AnyStr] | None = None,
80
81
  readonly: bool | None = None,
81
- ) -> ResponseType:
82
+ ) -> Awaitable[ResponseType]:
82
83
  """
83
84
  Executes the script registered in :paramref:`Script.script` using
84
- :meth:`coredis.Redis.evalsha`. Additionally if the script was not yet
85
+ :meth:`coredis.Redis.evalsha`. Additionally, if the script was not yet
85
86
  registered on the instance, it will automatically do that as well
86
87
  and cache the sha at :data:`Script.sha`
87
88
 
@@ -103,26 +104,24 @@ class Script(Generic[AnyStr]):
103
104
  if readonly is None:
104
105
  readonly = self.readonly
105
106
 
107
+ method = client.evalsha_ro if readonly else client.evalsha
108
+
106
109
  # make sure the Redis server knows about the script
107
110
  if isinstance(client, Pipeline):
108
111
  # make sure this script is good to go on pipeline
109
112
  cast(Pipeline[AnyStr], client).scripts.add(self)
110
-
111
- method = client.evalsha_ro if readonly else client.evalsha
112
- try:
113
- return await method(self.sha, keys=keys, args=args)
114
- except NoScriptError:
115
- # Maybe the client is pointed to a different server than the client
116
- # that created this instance?
117
- # Overwrite the sha just in case there was a discrepancy.
118
- self.sha = await client.script_load(self.script)
119
- return await method(self.sha, keys=keys, args=args)
113
+ return method(self.sha, keys=keys, args=args)
114
+ else:
115
+ return retryable(
116
+ ConstantRetryPolicy((NoScriptError,), 1, 0),
117
+ failure_hook=lambda _: client.script_load(self.script),
118
+ )(method)(self.sha, keys=keys, args=args)
120
119
 
121
120
  async def execute(
122
121
  self,
123
122
  keys: Parameters[KeyT] | None = None,
124
- args: Parameters[RedisValueT] | None = None,
125
- client: SupportsScript[AnyStr] | None = None,
123
+ args: Parameters[ValueT] | None = None,
124
+ client: coredis.client.Client[AnyStr] | None = None,
126
125
  readonly: bool | None = None,
127
126
  ) -> ResponseType:
128
127
  """
@@ -16,7 +16,7 @@ from coredis.response._callbacks.sentinel import (
16
16
  from coredis.typing import (
17
17
  AnyStr,
18
18
  RedisValueT,
19
- ResponseType,
19
+ ResponsePrimitive,
20
20
  StringT,
21
21
  )
22
22
 
@@ -105,7 +105,7 @@ class SentinelCommands(CommandMixin[AnyStr]):
105
105
  @redis_command(CommandName.SENTINEL_INFO_CACHE)
106
106
  def sentinel_infocache(
107
107
  self, *nodenames: StringT
108
- ) -> CommandRequest[dict[AnyStr, dict[int, dict[str, ResponseType]]]]:
108
+ ) -> CommandRequest[dict[AnyStr, dict[int, dict[str, ResponsePrimitive]]]]:
109
109
  """
110
110
  Return cached INFO output from masters and replicas.
111
111
  """
@@ -119,7 +119,9 @@ class SentinelCommands(CommandMixin[AnyStr]):
119
119
  @redis_command(
120
120
  CommandName.SENTINEL_MASTER,
121
121
  )
122
- def sentinel_master(self, service_name: StringT) -> CommandRequest[dict[str, int | bool | str]]:
122
+ def sentinel_master(
123
+ self, service_name: StringT
124
+ ) -> CommandRequest[dict[str, ResponsePrimitive]]:
123
125
  """Returns a dictionary containing the specified masters state."""
124
126
 
125
127
  return CommandRequest(
@@ -129,7 +131,7 @@ class SentinelCommands(CommandMixin[AnyStr]):
129
131
  @redis_command(
130
132
  CommandName.SENTINEL_MASTERS,
131
133
  )
132
- def sentinel_masters(self) -> CommandRequest[dict[str, dict[str, int | bool | str]]]:
134
+ def sentinel_masters(self) -> CommandRequest[dict[str, dict[str, ResponsePrimitive]]]:
133
135
  """Returns a list of dictionaries containing each master's state."""
134
136
 
135
137
  return CommandRequest(self, CommandName.SENTINEL_MASTERS, callback=PrimariesCallback())
@@ -173,7 +175,7 @@ class SentinelCommands(CommandMixin[AnyStr]):
173
175
  )
174
176
  def sentinel_sentinels(
175
177
  self, service_name: StringT
176
- ) -> CommandRequest[tuple[dict[str, int | bool | str], ...]]:
178
+ ) -> CommandRequest[tuple[dict[str, ResponsePrimitive], ...]]:
177
179
  """Returns a list of sentinels for :paramref:`service_name`"""
178
180
 
179
181
  return CommandRequest(
@@ -205,7 +207,7 @@ class SentinelCommands(CommandMixin[AnyStr]):
205
207
  )
206
208
  def sentinel_slaves(
207
209
  self, service_name: StringT
208
- ) -> CommandRequest[tuple[dict[str, int | bool | str], ...]]:
210
+ ) -> CommandRequest[tuple[dict[str, ResponsePrimitive], ...]]:
209
211
  """Returns a list of slaves for paramref:`service_name`"""
210
212
 
211
213
  return CommandRequest(
@@ -217,7 +219,7 @@ class SentinelCommands(CommandMixin[AnyStr]):
217
219
  )
218
220
  def sentinel_replicas(
219
221
  self, service_name: StringT
220
- ) -> CommandRequest[tuple[dict[str, int | bool | str], ...]]:
222
+ ) -> CommandRequest[tuple[dict[str, ResponsePrimitive], ...]]:
221
223
  """Returns a list of replicas for :paramref:`service_name`"""
222
224
 
223
225
  return CommandRequest(