coredis 5.0.0rc2__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.
- {coredis-5.0.0rc2 → coredis-5.0.1}/HISTORY.rst +35 -7
- {coredis-5.0.0rc2/coredis.egg-info → coredis-5.0.1}/PKG-INFO +1 -2
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/_protocols.py +0 -23
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/_utils.py +8 -4
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/_version.py +3 -3
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/core.py +50 -8
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/script.py +12 -16
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/sentinel.py +9 -7
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/pipeline.py +5 -6
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/sentinel.py +28 -25
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/vector_sets.py +44 -11
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/sentinel.py +18 -5
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/tokens.py +5 -0
- {coredis-5.0.0rc2 → coredis-5.0.1/coredis.egg-info}/PKG-INFO +1 -2
- {coredis-5.0.0rc2 → coredis-5.0.1}/pyproject.toml +1 -1
- {coredis-5.0.0rc2 → coredis-5.0.1}/requirements/dev.txt +1 -1
- {coredis-5.0.0rc2 → coredis-5.0.1}/setup.py +0 -1
- {coredis-5.0.0rc2 → coredis-5.0.1}/LICENSE +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/MANIFEST.in +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/README.md +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/_json.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/_packer.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/_py_311_typing.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/_py_312_typing.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/_sidecar.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/cache.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/client/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/client/basic.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/client/cluster.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/_key_spec.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/_utils.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/_validators.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/_wrappers.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/bitfield.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/constants.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/function.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/monitor.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/pubsub.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/commands/request.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/config.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/connection.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/constants.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/credentials.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/exceptions.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/experimental/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/globals.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/autocomplete.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/base.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/filters.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/graph.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/json.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/response/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/response/_callbacks/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/response/_callbacks/autocomplete.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/response/_callbacks/graph.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/response/_callbacks/json.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/response/_callbacks/search.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/response/_callbacks/timeseries.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/response/types.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/search.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/modules/timeseries.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/parser.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/pool/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/pool/basic.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/pool/cluster.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/pool/nodemanager.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/py.typed +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/recipes/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/recipes/credentials/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/recipes/credentials/iam_provider.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/recipes/locks/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/recipes/locks/extend.lua +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/recipes/locks/lua_lock.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/recipes/locks/release.lua +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/__init__.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/acl.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/cluster.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/command.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/connection.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/geo.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/hash.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/keys.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/module.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/script.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/server.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/sets.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/sorted_set.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/streams.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_callbacks/strings.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/_utils.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/response/types.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/retry.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/speedups.c +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/speedups.pyi +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/stream.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis/typing.py +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis.egg-info/SOURCES.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis.egg-info/dependency_links.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis.egg-info/requires.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/coredis.egg-info/top_level.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/requirements/ci.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/requirements/dev_extra.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/requirements/docs.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/requirements/main.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/requirements/publishing.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/requirements/recipes.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/requirements/test.txt +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/setup.cfg +0 -0
- {coredis-5.0.0rc2 → coredis-5.0.1}/versioneer.py +0 -0
|
@@ -3,6 +3,41 @@
|
|
|
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
|
+
|
|
6
41
|
v5.0.0rc2
|
|
7
42
|
---------
|
|
8
43
|
Release Date: 2025-07-10
|
|
@@ -1965,10 +2000,3 @@ v1.0.1
|
|
|
1965
2000
|
* fix bug of `PubSub.run_in_thread`
|
|
1966
2001
|
* add more examples
|
|
1967
2002
|
* change `Script.register` to `Script.execute`
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coredis
|
|
3
|
-
Version: 5.0.
|
|
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
|
|
@@ -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
|
-
def evalsha(
|
|
53
|
-
self,
|
|
54
|
-
sha1: StringT,
|
|
55
|
-
keys: Parameters[KeyT] | None = ...,
|
|
56
|
-
args: Parameters[RedisValueT] | None = ...,
|
|
57
|
-
) -> CommandRequest[ResponseType]: ...
|
|
58
|
-
|
|
59
|
-
def evalsha_ro(
|
|
60
|
-
self,
|
|
61
|
-
sha1: StringT,
|
|
62
|
-
keys: Parameters[KeyT] | None = ...,
|
|
63
|
-
args: Parameters[RedisValueT] | None = ...,
|
|
64
|
-
) -> CommandRequest[ResponseType]: ...
|
|
65
|
-
|
|
66
|
-
def script_load(self, script: StringT) -> CommandRequest[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-
|
|
11
|
+
"date": "2025-07-18T09:26:51-0700",
|
|
12
12
|
"dirty": false,
|
|
13
13
|
"error": null,
|
|
14
|
-
"full-revisionid": "
|
|
15
|
-
"version": "5.0.
|
|
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
|
|
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
|
-
|
|
5273
|
-
|
|
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
|
-
|
|
5302
|
-
|
|
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[
|
|
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,7 @@ 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
|
-
from coredis.commands import CommandRequest
|
|
14
12
|
from coredis.exceptions import NoScriptError
|
|
15
13
|
from coredis.retry import ConstantRetryPolicy, retryable
|
|
16
14
|
from coredis.typing import (
|
|
@@ -25,6 +23,7 @@ from coredis.typing import (
|
|
|
25
23
|
RedisValueT,
|
|
26
24
|
ResponseType,
|
|
27
25
|
StringT,
|
|
26
|
+
ValueT,
|
|
28
27
|
add_runtime_checks,
|
|
29
28
|
safe_beartype,
|
|
30
29
|
)
|
|
@@ -53,7 +52,7 @@ class Script(Generic[AnyStr]):
|
|
|
53
52
|
|
|
54
53
|
def __init__(
|
|
55
54
|
self,
|
|
56
|
-
registered_client:
|
|
55
|
+
registered_client: coredis.client.Client[AnyStr] | None = None,
|
|
57
56
|
script: StringT | None = None,
|
|
58
57
|
readonly: bool = False,
|
|
59
58
|
):
|
|
@@ -66,7 +65,7 @@ class Script(Generic[AnyStr]):
|
|
|
66
65
|
:param readonly: If ``True`` the script will be called with
|
|
67
66
|
:meth:`coredis.Redis.evalsha_ro` instead of :meth:`coredis.Redis.evalsha`
|
|
68
67
|
"""
|
|
69
|
-
self.registered_client:
|
|
68
|
+
self.registered_client: coredis.client.Client[AnyStr] | None = registered_client
|
|
70
69
|
self.script: StringT
|
|
71
70
|
if not script:
|
|
72
71
|
raise RuntimeError("No script provided")
|
|
@@ -77,10 +76,10 @@ class Script(Generic[AnyStr]):
|
|
|
77
76
|
def __call__(
|
|
78
77
|
self,
|
|
79
78
|
keys: Parameters[KeyT] | None = None,
|
|
80
|
-
args: Parameters[
|
|
81
|
-
client:
|
|
79
|
+
args: Parameters[ValueT] | None = None,
|
|
80
|
+
client: coredis.client.Client[AnyStr] | None = None,
|
|
82
81
|
readonly: bool | None = None,
|
|
83
|
-
) ->
|
|
82
|
+
) -> Awaitable[ResponseType]:
|
|
84
83
|
"""
|
|
85
84
|
Executes the script registered in :paramref:`Script.script` using
|
|
86
85
|
:meth:`coredis.Redis.evalsha`. Additionally, if the script was not yet
|
|
@@ -113,19 +112,16 @@ class Script(Generic[AnyStr]):
|
|
|
113
112
|
cast(Pipeline[AnyStr], client).scripts.add(self)
|
|
114
113
|
return method(self.sha, keys=keys, args=args)
|
|
115
114
|
else:
|
|
116
|
-
return
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
failure_hook=lambda _: client.script_load(self.script),
|
|
121
|
-
)(method)(self.sha, keys=keys, args=args),
|
|
122
|
-
)
|
|
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)
|
|
123
119
|
|
|
124
120
|
async def execute(
|
|
125
121
|
self,
|
|
126
122
|
keys: Parameters[KeyT] | None = None,
|
|
127
|
-
args: Parameters[
|
|
128
|
-
client:
|
|
123
|
+
args: Parameters[ValueT] | None = None,
|
|
124
|
+
client: coredis.client.Client[AnyStr] | None = None,
|
|
129
125
|
readonly: bool | None = None,
|
|
130
126
|
) -> ResponseType:
|
|
131
127
|
"""
|
|
@@ -16,7 +16,7 @@ from coredis.response._callbacks.sentinel import (
|
|
|
16
16
|
from coredis.typing import (
|
|
17
17
|
AnyStr,
|
|
18
18
|
RedisValueT,
|
|
19
|
-
|
|
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,
|
|
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(
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
222
|
+
) -> CommandRequest[tuple[dict[str, ResponsePrimitive], ...]]:
|
|
221
223
|
"""Returns a list of replicas for :paramref:`service_name`"""
|
|
222
224
|
|
|
223
225
|
return CommandRequest(
|
|
@@ -89,8 +89,6 @@ def wrap_pipeline_method(
|
|
|
89
89
|
def wrapper(*args: P.args, **kwargs: P.kwargs) -> Awaitable[R]:
|
|
90
90
|
return func(*args, **kwargs)
|
|
91
91
|
|
|
92
|
-
wrapper.__annotations__ = wrapper.__annotations__.copy()
|
|
93
|
-
wrapper.__annotations__["return"] = kls
|
|
94
92
|
wrapper.__doc__ = textwrap.dedent(wrapper.__doc__ or "")
|
|
95
93
|
wrapper.__doc__ = f"""
|
|
96
94
|
.. note:: Pipeline variant of :meth:`coredis.Redis.{func.__name__}` that does not execute
|
|
@@ -165,13 +163,12 @@ class PipelineCommandRequest(CommandRequest[CommandResponseT]):
|
|
|
165
163
|
if hasattr(self, "response"):
|
|
166
164
|
return self.response.__await__()
|
|
167
165
|
elif self.parent:
|
|
168
|
-
parent = self.parent
|
|
169
166
|
|
|
170
167
|
async def _transformed() -> CommandResponseT:
|
|
171
|
-
if
|
|
172
|
-
return
|
|
168
|
+
if (r := await self.parent) == self.client: # type: ignore
|
|
169
|
+
return r # type: ignore
|
|
173
170
|
else:
|
|
174
|
-
return
|
|
171
|
+
return self.callback(r)
|
|
175
172
|
|
|
176
173
|
return _transformed().__await__()
|
|
177
174
|
else:
|
|
@@ -541,6 +538,8 @@ class Pipeline(Client[AnyStr], metaclass=PipelineMeta):
|
|
|
541
538
|
) -> None:
|
|
542
539
|
"""
|
|
543
540
|
Queue a command for execution on the next `execute()` call.
|
|
541
|
+
|
|
542
|
+
:meta private:
|
|
544
543
|
"""
|
|
545
544
|
self.command_stack.append(command)
|
|
546
545
|
|
|
@@ -7,7 +7,6 @@ from coredis.response._callbacks import ResponseCallback
|
|
|
7
7
|
from coredis.response._callbacks.server import InfoCallback
|
|
8
8
|
from coredis.typing import (
|
|
9
9
|
AnyStr,
|
|
10
|
-
MutableMapping,
|
|
11
10
|
ResponsePrimitive,
|
|
12
11
|
ResponseType,
|
|
13
12
|
)
|
|
@@ -41,9 +40,9 @@ SENTINEL_STATE_INT_FIELDS = {
|
|
|
41
40
|
|
|
42
41
|
def sentinel_state_typed(
|
|
43
42
|
response: list[str],
|
|
44
|
-
) ->
|
|
43
|
+
) -> EncodingInsensitiveDict[str, str | int | bool]:
|
|
45
44
|
it = iter(response)
|
|
46
|
-
result:
|
|
45
|
+
result: EncodingInsensitiveDict[str, str | int | bool] = EncodingInsensitiveDict()
|
|
47
46
|
|
|
48
47
|
for key, value in zip(it, it):
|
|
49
48
|
if key in SENTINEL_STATE_INT_FIELDS:
|
|
@@ -54,8 +53,8 @@ def sentinel_state_typed(
|
|
|
54
53
|
|
|
55
54
|
|
|
56
55
|
def add_flags(
|
|
57
|
-
result:
|
|
58
|
-
) ->
|
|
56
|
+
result: EncodingInsensitiveDict[str, int | str | bool],
|
|
57
|
+
) -> EncodingInsensitiveDict[str, int | str | bool]:
|
|
59
58
|
flags = set(nativestr(result["flags"]).split(","))
|
|
60
59
|
for name, flag in (
|
|
61
60
|
("is_master", "master"),
|
|
@@ -72,7 +71,7 @@ def add_flags(
|
|
|
72
71
|
|
|
73
72
|
def parse_sentinel_state(
|
|
74
73
|
item: list[ResponsePrimitive],
|
|
75
|
-
) ->
|
|
74
|
+
) -> EncodingInsensitiveDict[str, int | str | bool]:
|
|
76
75
|
result = sentinel_state_typed([nativestr(k) for k in item])
|
|
77
76
|
result = add_flags(result)
|
|
78
77
|
return result
|
|
@@ -82,34 +81,34 @@ class PrimaryCallback(
|
|
|
82
81
|
ResponseCallback[
|
|
83
82
|
ResponseType,
|
|
84
83
|
dict[ResponsePrimitive, ResponsePrimitive],
|
|
85
|
-
dict[str,
|
|
84
|
+
dict[str, ResponsePrimitive],
|
|
86
85
|
]
|
|
87
86
|
):
|
|
88
87
|
def transform(
|
|
89
88
|
self,
|
|
90
89
|
response: ResponseType,
|
|
91
|
-
) -> dict[str,
|
|
92
|
-
return
|
|
90
|
+
) -> dict[str, ResponsePrimitive]:
|
|
91
|
+
return parse_sentinel_state(cast(list[ResponsePrimitive], response)).stringify_keys()
|
|
93
92
|
|
|
94
93
|
def transform_3(
|
|
95
94
|
self,
|
|
96
95
|
response: dict[ResponsePrimitive, ResponsePrimitive],
|
|
97
|
-
) -> dict[str,
|
|
98
|
-
return
|
|
96
|
+
) -> dict[str, ResponsePrimitive]:
|
|
97
|
+
return add_flags(EncodingInsensitiveDict(response)).stringify_keys()
|
|
99
98
|
|
|
100
99
|
|
|
101
100
|
class PrimariesCallback(
|
|
102
101
|
ResponseCallback[
|
|
103
102
|
list[ResponseType],
|
|
104
103
|
list[ResponseType],
|
|
105
|
-
dict[str, dict[str,
|
|
104
|
+
dict[str, dict[str, ResponsePrimitive]],
|
|
106
105
|
]
|
|
107
106
|
):
|
|
108
107
|
def transform(
|
|
109
108
|
self,
|
|
110
109
|
response: list[ResponseType] | dict[ResponsePrimitive, ResponsePrimitive],
|
|
111
|
-
) -> dict[str, dict[str,
|
|
112
|
-
result: dict[str, dict[str,
|
|
110
|
+
) -> dict[str, dict[str, ResponsePrimitive]]:
|
|
111
|
+
result: dict[str, dict[str, ResponseType]] = {}
|
|
113
112
|
|
|
114
113
|
for item in response:
|
|
115
114
|
state = PrimaryCallback()(item)
|
|
@@ -120,11 +119,11 @@ class PrimariesCallback(
|
|
|
120
119
|
def transform_3(
|
|
121
120
|
self,
|
|
122
121
|
response: list[ResponseType],
|
|
123
|
-
) -> dict[str, dict[str,
|
|
124
|
-
states: dict[str, dict[str,
|
|
122
|
+
) -> dict[str, dict[str, ResponsePrimitive]]:
|
|
123
|
+
states: dict[str, dict[str, ResponsePrimitive]] = {}
|
|
125
124
|
for state in response:
|
|
126
|
-
|
|
127
|
-
states[nativestr(
|
|
125
|
+
state = add_flags(EncodingInsensitiveDict(state)).stringify_keys()
|
|
126
|
+
states[nativestr(state["name"])] = state
|
|
128
127
|
return states
|
|
129
128
|
|
|
130
129
|
|
|
@@ -132,20 +131,24 @@ class SentinelsStateCallback(
|
|
|
132
131
|
ResponseCallback[
|
|
133
132
|
list[ResponseType],
|
|
134
133
|
list[ResponseType],
|
|
135
|
-
tuple[dict[str,
|
|
134
|
+
tuple[dict[str, ResponsePrimitive], ...],
|
|
136
135
|
]
|
|
137
136
|
):
|
|
138
137
|
def transform(
|
|
139
138
|
self,
|
|
140
139
|
response: list[ResponseType],
|
|
141
|
-
) -> tuple[dict[str,
|
|
142
|
-
return tuple(
|
|
140
|
+
) -> tuple[dict[str, ResponsePrimitive], ...]:
|
|
141
|
+
return tuple(
|
|
142
|
+
parse_sentinel_state([nativestr(i) for i in item]).stringify_keys() for item in response
|
|
143
|
+
)
|
|
143
144
|
|
|
144
145
|
def transform_3(
|
|
145
146
|
self,
|
|
146
147
|
response: list[ResponseType],
|
|
147
|
-
) -> tuple[dict[str,
|
|
148
|
-
return tuple(
|
|
148
|
+
) -> tuple[dict[str, ResponsePrimitive], ...]:
|
|
149
|
+
return tuple(
|
|
150
|
+
add_flags(EncodingInsensitiveDict(state)).stringify_keys() for state in response
|
|
151
|
+
)
|
|
149
152
|
|
|
150
153
|
|
|
151
154
|
class GetPrimaryCallback(
|
|
@@ -166,11 +169,11 @@ class SentinelInfoCallback(
|
|
|
166
169
|
ResponseCallback[
|
|
167
170
|
list[ResponseType],
|
|
168
171
|
list[ResponseType],
|
|
169
|
-
dict[AnyStr, dict[int, dict[str,
|
|
172
|
+
dict[AnyStr, dict[int, dict[str, ResponsePrimitive]]],
|
|
170
173
|
]
|
|
171
174
|
):
|
|
172
175
|
def transform(
|
|
173
176
|
self,
|
|
174
177
|
response: list[ResponseType],
|
|
175
|
-
) -> dict[AnyStr, dict[int, dict[str,
|
|
178
|
+
) -> dict[AnyStr, dict[int, dict[str, ResponsePrimitive]]]:
|
|
176
179
|
return {response[0]: {r[0]: InfoCallback()(r[1]) for r in response[1]}}
|
|
@@ -1,36 +1,69 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from coredis._json import json
|
|
3
4
|
from coredis._utils import nativestr
|
|
4
5
|
from coredis.response._callbacks import ResponseCallback
|
|
5
6
|
from coredis.response._utils import flat_pairs_to_dict
|
|
6
7
|
from coredis.response.types import VectorData
|
|
7
|
-
from coredis.typing import AnyStr, ResponsePrimitive, StringT
|
|
8
|
+
from coredis.typing import AnyStr, JsonType, ResponsePrimitive, StringT
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class VSimCallback(
|
|
11
12
|
ResponseCallback[
|
|
12
13
|
list[AnyStr],
|
|
13
|
-
list[AnyStr] | dict[AnyStr, float],
|
|
14
|
-
tuple[AnyStr, ...]
|
|
14
|
+
list[AnyStr] | dict[AnyStr, float | list[float | JsonType]],
|
|
15
|
+
tuple[AnyStr, ...]
|
|
16
|
+
| dict[AnyStr, float]
|
|
17
|
+
| dict[AnyStr, JsonType]
|
|
18
|
+
| dict[AnyStr, tuple[float, JsonType]],
|
|
15
19
|
],
|
|
16
20
|
):
|
|
17
21
|
def transform(
|
|
18
22
|
self,
|
|
19
23
|
response: list[AnyStr],
|
|
20
|
-
) ->
|
|
21
|
-
|
|
24
|
+
) -> (
|
|
25
|
+
tuple[AnyStr, ...]
|
|
26
|
+
| dict[AnyStr, float]
|
|
27
|
+
| dict[AnyStr, JsonType]
|
|
28
|
+
| dict[AnyStr, tuple[float, JsonType]]
|
|
29
|
+
):
|
|
30
|
+
withscores, withattribs = self.options.get("withscores"), self.options.get("withattribs")
|
|
31
|
+
if withscores or withattribs:
|
|
22
32
|
it = iter(response)
|
|
23
|
-
|
|
33
|
+
match withscores, withattribs:
|
|
34
|
+
case True, None | False:
|
|
35
|
+
return dict(list(zip(it, map(float, it))))
|
|
36
|
+
case None | False, True:
|
|
37
|
+
return dict(list(zip(it, map(json.loads, it))))
|
|
38
|
+
case True, True:
|
|
39
|
+
return dict(
|
|
40
|
+
list(zip(it, map(lambda x: (float(x[0]), json.loads(x[1])), zip(it, it))))
|
|
41
|
+
)
|
|
24
42
|
else:
|
|
25
|
-
return
|
|
43
|
+
return self.transform_3(response)
|
|
26
44
|
|
|
27
45
|
def transform_3(
|
|
28
46
|
self,
|
|
29
|
-
response: list[AnyStr]
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
response: list[AnyStr]
|
|
48
|
+
| dict[AnyStr, float]
|
|
49
|
+
| dict[AnyStr, AnyStr]
|
|
50
|
+
| dict[AnyStr, list[float | AnyStr]],
|
|
51
|
+
) -> (
|
|
52
|
+
tuple[AnyStr, ...]
|
|
53
|
+
| dict[AnyStr, float]
|
|
54
|
+
| dict[AnyStr, JsonType]
|
|
55
|
+
| dict[AnyStr, tuple[float, JsonType]]
|
|
56
|
+
):
|
|
57
|
+
withscores, withattribs = self.options.get("withscores"), self.options.get("withattribs")
|
|
58
|
+
if withscores or withattribs:
|
|
32
59
|
assert isinstance(response, dict)
|
|
33
|
-
|
|
60
|
+
match withscores, withattribs:
|
|
61
|
+
case None | False, True:
|
|
62
|
+
return {k: json.loads(v) for k, v in response.items()}
|
|
63
|
+
case True, True:
|
|
64
|
+
return {k: (v[0], json.loads(v[1])) for k, v in response.items()}
|
|
65
|
+
case _:
|
|
66
|
+
return response
|
|
34
67
|
else:
|
|
35
68
|
return tuple(response)
|
|
36
69
|
|
|
@@ -23,7 +23,9 @@ from coredis.typing import (
|
|
|
23
23
|
Generic,
|
|
24
24
|
Iterable,
|
|
25
25
|
Literal,
|
|
26
|
+
ResponsePrimitive,
|
|
26
27
|
StringT,
|
|
28
|
+
TypeAdapter,
|
|
27
29
|
)
|
|
28
30
|
|
|
29
31
|
|
|
@@ -195,6 +197,7 @@ class Sentinel(Generic[AnyStr]):
|
|
|
195
197
|
sentinel_kwargs: dict[str, Any] | None = ...,
|
|
196
198
|
decode_responses: Literal[False] = ...,
|
|
197
199
|
cache: AbstractCache | None = None,
|
|
200
|
+
type_adapter: TypeAdapter | None = ...,
|
|
198
201
|
**connection_kwargs: Any,
|
|
199
202
|
) -> None: ...
|
|
200
203
|
|
|
@@ -206,6 +209,7 @@ class Sentinel(Generic[AnyStr]):
|
|
|
206
209
|
sentinel_kwargs: dict[str, Any] | None = ...,
|
|
207
210
|
decode_responses: Literal[True] = ...,
|
|
208
211
|
cache: AbstractCache | None = None,
|
|
212
|
+
type_adapter: TypeAdapter | None = None,
|
|
209
213
|
**connection_kwargs: Any,
|
|
210
214
|
) -> None: ...
|
|
211
215
|
|
|
@@ -216,6 +220,7 @@ class Sentinel(Generic[AnyStr]):
|
|
|
216
220
|
sentinel_kwargs: dict[str, Any] | None = None,
|
|
217
221
|
decode_responses: bool = False,
|
|
218
222
|
cache: AbstractCache | None = None,
|
|
223
|
+
type_adapter: TypeAdapter | None = None,
|
|
219
224
|
**connection_kwargs: Any,
|
|
220
225
|
) -> None:
|
|
221
226
|
"""
|
|
@@ -236,6 +241,9 @@ class Sentinel(Generic[AnyStr]):
|
|
|
236
241
|
and ``protocol_version`` options specified in :paramref:`connection_kwargs` will be used.
|
|
237
242
|
:param cache: If provided the cache will be shared between both primaries and replicas
|
|
238
243
|
returned by this sentinel.
|
|
244
|
+
:param type_adapter: The adapter to use for serializing / deserializing customs types
|
|
245
|
+
when interacting with redis commands. If provided this adapter will be used for both
|
|
246
|
+
primaries and replicas returned by this sentinel.
|
|
239
247
|
:param connection_kwargs: are keyword arguments that will be used when
|
|
240
248
|
establishing a connection to a Redis server (i.e. are passed on to the
|
|
241
249
|
constructor of :class:`Redis` for all primary and replicas).
|
|
@@ -260,6 +268,7 @@ class Sentinel(Generic[AnyStr]):
|
|
|
260
268
|
self.min_other_sentinels = min_other_sentinels
|
|
261
269
|
self.connection_kwargs = connection_kwargs
|
|
262
270
|
self.__cache = cache
|
|
271
|
+
self.__type_adapter = type_adapter
|
|
263
272
|
self.connection_kwargs["decode_responses"] = self.sentinel_kwargs["decode_responses"] = (
|
|
264
273
|
decode_responses
|
|
265
274
|
)
|
|
@@ -283,18 +292,18 @@ class Sentinel(Generic[AnyStr]):
|
|
|
283
292
|
|
|
284
293
|
def __check_primary_state(
|
|
285
294
|
self,
|
|
286
|
-
state: dict[str,
|
|
295
|
+
state: dict[str, ResponsePrimitive],
|
|
287
296
|
) -> bool:
|
|
288
297
|
if not state["is_master"] or state["is_sdown"] or state["is_odown"]:
|
|
289
298
|
return False
|
|
290
299
|
|
|
291
|
-
if int(state["num-other-sentinels"]) < self.min_other_sentinels:
|
|
300
|
+
if int(state["num-other-sentinels"] or 0) < self.min_other_sentinels:
|
|
292
301
|
return False
|
|
293
302
|
|
|
294
303
|
return True
|
|
295
304
|
|
|
296
305
|
def __filter_replicas(
|
|
297
|
-
self, replicas: Iterable[dict[str,
|
|
306
|
+
self, replicas: Iterable[dict[str, ResponsePrimitive]]
|
|
298
307
|
) -> list[tuple[str, int]]:
|
|
299
308
|
"""Removes replicas that are in an ODOWN or SDOWN state"""
|
|
300
309
|
replicas_alive: list[tuple[str, int]] = []
|
|
@@ -302,7 +311,9 @@ class Sentinel(Generic[AnyStr]):
|
|
|
302
311
|
for replica in replicas:
|
|
303
312
|
if replica["is_odown"] or replica["is_sdown"]:
|
|
304
313
|
continue
|
|
305
|
-
|
|
314
|
+
ip, port = replica["ip"], replica["port"]
|
|
315
|
+
assert ip and port
|
|
316
|
+
replicas_alive.append((nativestr(ip), int(port)))
|
|
306
317
|
|
|
307
318
|
return replicas_alive
|
|
308
319
|
|
|
@@ -329,7 +340,7 @@ class Sentinel(Generic[AnyStr]):
|
|
|
329
340
|
self.sentinels[0],
|
|
330
341
|
)
|
|
331
342
|
|
|
332
|
-
return nativestr(state["ip"]), int(state["port"])
|
|
343
|
+
return nativestr(state["ip"]), int(state["port"] or -1)
|
|
333
344
|
raise PrimaryNotFoundError(f"No primary found for {service_name!r}")
|
|
334
345
|
|
|
335
346
|
async def discover_replicas(self, service_name: str) -> list[tuple[str, int]]:
|
|
@@ -406,6 +417,7 @@ class Sentinel(Generic[AnyStr]):
|
|
|
406
417
|
**connection_kwargs,
|
|
407
418
|
),
|
|
408
419
|
cache=self.__cache,
|
|
420
|
+
type_adapter=self.__type_adapter,
|
|
409
421
|
)
|
|
410
422
|
|
|
411
423
|
@overload
|
|
@@ -461,4 +473,5 @@ class Sentinel(Generic[AnyStr]):
|
|
|
461
473
|
**connection_kwargs,
|
|
462
474
|
),
|
|
463
475
|
cache=self.__cache,
|
|
476
|
+
type_adapter=self.__type_adapter,
|
|
464
477
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coredis
|
|
3
|
-
Version: 5.0.
|
|
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
|
|
@@ -147,7 +147,6 @@ setup(
|
|
|
147
147
|
classifiers=[
|
|
148
148
|
"Development Status :: 5 - Production/Stable",
|
|
149
149
|
"Intended Audience :: Developers",
|
|
150
|
-
"License :: OSI Approved :: MIT License",
|
|
151
150
|
"Operating System :: OS Independent",
|
|
152
151
|
"Programming Language :: Python",
|
|
153
152
|
"Programming Language :: Python :: 3.10",
|
|
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
|
|
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
|