coredis 5.0.0rc2__tar.gz → 5.1.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.

Potentially problematic release.


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

Files changed (113) hide show
  1. {coredis-5.0.0rc2 → coredis-5.1.0}/HISTORY.rst +44 -6
  2. {coredis-5.0.0rc2/coredis.egg-info → coredis-5.1.0}/PKG-INFO +1 -2
  3. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/_protocols.py +0 -23
  4. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/_utils.py +8 -4
  5. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/_version.py +3 -3
  6. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/core.py +50 -8
  7. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/script.py +12 -16
  8. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/sentinel.py +9 -7
  9. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/pipeline.py +5 -6
  10. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/pool/basic.py +14 -11
  11. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/sentinel.py +28 -25
  12. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/vector_sets.py +44 -11
  13. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/sentinel.py +18 -5
  14. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/tokens.py +5 -0
  15. {coredis-5.0.0rc2 → coredis-5.1.0/coredis.egg-info}/PKG-INFO +1 -2
  16. {coredis-5.0.0rc2 → coredis-5.1.0}/pyproject.toml +1 -1
  17. {coredis-5.0.0rc2 → coredis-5.1.0}/requirements/dev.txt +1 -1
  18. {coredis-5.0.0rc2 → coredis-5.1.0}/requirements/docs.txt +3 -3
  19. {coredis-5.0.0rc2 → coredis-5.1.0}/setup.py +0 -1
  20. {coredis-5.0.0rc2 → coredis-5.1.0}/LICENSE +0 -0
  21. {coredis-5.0.0rc2 → coredis-5.1.0}/MANIFEST.in +0 -0
  22. {coredis-5.0.0rc2 → coredis-5.1.0}/README.md +0 -0
  23. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/__init__.py +0 -0
  24. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/_json.py +0 -0
  25. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/_packer.py +0 -0
  26. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/_py_311_typing.py +0 -0
  27. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/_py_312_typing.py +0 -0
  28. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/_sidecar.py +0 -0
  29. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/cache.py +0 -0
  30. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/client/__init__.py +0 -0
  31. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/client/basic.py +0 -0
  32. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/client/cluster.py +0 -0
  33. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/__init__.py +0 -0
  34. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/_key_spec.py +0 -0
  35. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/_utils.py +0 -0
  36. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/_validators.py +0 -0
  37. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/_wrappers.py +0 -0
  38. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/bitfield.py +0 -0
  39. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/constants.py +0 -0
  40. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/function.py +0 -0
  41. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/monitor.py +0 -0
  42. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/pubsub.py +0 -0
  43. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/commands/request.py +0 -0
  44. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/config.py +0 -0
  45. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/connection.py +0 -0
  46. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/constants.py +0 -0
  47. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/credentials.py +0 -0
  48. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/exceptions.py +0 -0
  49. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/experimental/__init__.py +0 -0
  50. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/globals.py +0 -0
  51. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/__init__.py +0 -0
  52. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/autocomplete.py +0 -0
  53. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/base.py +0 -0
  54. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/filters.py +0 -0
  55. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/graph.py +0 -0
  56. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/json.py +0 -0
  57. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/response/__init__.py +0 -0
  58. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/response/_callbacks/__init__.py +0 -0
  59. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/response/_callbacks/autocomplete.py +0 -0
  60. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/response/_callbacks/graph.py +0 -0
  61. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/response/_callbacks/json.py +0 -0
  62. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/response/_callbacks/search.py +0 -0
  63. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/response/_callbacks/timeseries.py +0 -0
  64. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/response/types.py +0 -0
  65. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/search.py +0 -0
  66. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/modules/timeseries.py +0 -0
  67. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/parser.py +0 -0
  68. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/pool/__init__.py +0 -0
  69. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/pool/cluster.py +0 -0
  70. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/pool/nodemanager.py +0 -0
  71. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/py.typed +0 -0
  72. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/recipes/__init__.py +0 -0
  73. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/recipes/credentials/__init__.py +0 -0
  74. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/recipes/credentials/iam_provider.py +0 -0
  75. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/recipes/locks/__init__.py +0 -0
  76. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/recipes/locks/extend.lua +0 -0
  77. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/recipes/locks/lua_lock.py +0 -0
  78. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/recipes/locks/release.lua +0 -0
  79. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/__init__.py +0 -0
  80. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/__init__.py +0 -0
  81. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/acl.py +0 -0
  82. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/cluster.py +0 -0
  83. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/command.py +0 -0
  84. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/connection.py +0 -0
  85. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/geo.py +0 -0
  86. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/hash.py +0 -0
  87. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/keys.py +0 -0
  88. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/module.py +0 -0
  89. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/script.py +0 -0
  90. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/server.py +0 -0
  91. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/sets.py +0 -0
  92. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/sorted_set.py +0 -0
  93. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/streams.py +0 -0
  94. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_callbacks/strings.py +0 -0
  95. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/_utils.py +0 -0
  96. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/response/types.py +0 -0
  97. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/retry.py +0 -0
  98. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/speedups.c +0 -0
  99. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/speedups.pyi +0 -0
  100. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/stream.py +0 -0
  101. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis/typing.py +0 -0
  102. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis.egg-info/SOURCES.txt +0 -0
  103. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis.egg-info/dependency_links.txt +0 -0
  104. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis.egg-info/requires.txt +0 -0
  105. {coredis-5.0.0rc2 → coredis-5.1.0}/coredis.egg-info/top_level.txt +0 -0
  106. {coredis-5.0.0rc2 → coredis-5.1.0}/requirements/ci.txt +0 -0
  107. {coredis-5.0.0rc2 → coredis-5.1.0}/requirements/dev_extra.txt +0 -0
  108. {coredis-5.0.0rc2 → coredis-5.1.0}/requirements/main.txt +0 -0
  109. {coredis-5.0.0rc2 → coredis-5.1.0}/requirements/publishing.txt +0 -0
  110. {coredis-5.0.0rc2 → coredis-5.1.0}/requirements/recipes.txt +0 -0
  111. {coredis-5.0.0rc2 → coredis-5.1.0}/requirements/test.txt +0 -0
  112. {coredis-5.0.0rc2 → coredis-5.1.0}/setup.cfg +0 -0
  113. {coredis-5.0.0rc2 → coredis-5.1.0}/versioneer.py +0 -0
@@ -3,6 +3,50 @@
3
3
  Changelog
4
4
  =========
5
5
 
6
+ v5.1.0
7
+ ------
8
+ Release Date: 2025-09-10
9
+
10
+ * Bug Fix
11
+
12
+ * Ensure ``ssl_context`` passed in kwargs of ``from_url`` factory
13
+ method is respected.
14
+
15
+ v5.0.1
16
+ ------
17
+ Release Date: 2025-07-18
18
+
19
+ * Bug Fix
20
+
21
+ * Fix regression caused by ``5.0.0`` which completely broke the use of Sentinel
22
+ with ``decode_responses=False``.
23
+
24
+ v5.0.0
25
+ ------
26
+ Release Date: 2025-07-16
27
+
28
+ * Features
29
+
30
+ * Add support for using custom types with redis commands
31
+ by registering serializers and deserializers
32
+ * Allow stacking pipeline commands synchronously
33
+ * Expose statically typed responses for pipeline commands
34
+
35
+
36
+ * Compatibility
37
+
38
+ * Redis command methods are no longer coroutines and instead
39
+ synchronous methods that return subclasses of ``Awaitable``
40
+ (``CommandRequest``) which can be awaited as before.
41
+ * Add support for redis 8.0 vector set commands
42
+ * Add support for redis 8.0 hash expiry commands
43
+ * Remove deprecated pubsub ``listen`` and threaded worker APIs
44
+ * Remove support for KeyDB
45
+
46
+ * Performance
47
+
48
+ * Streamline client side cache shrinking
49
+
6
50
  v5.0.0rc2
7
51
  ---------
8
52
  Release Date: 2025-07-10
@@ -1966,9 +2010,3 @@ v1.0.1
1966
2010
  * add more examples
1967
2011
  * change `Script.register` to `Script.execute`
1968
2012
 
1969
-
1970
-
1971
-
1972
-
1973
-
1974
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coredis
3
- Version: 5.0.0rc2
3
+ Version: 5.1.0
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-10T16:37:52-0700",
11
+ "date": "2025-09-10T09:08:52-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "500ec3f7c2c644928da3459bf3242872e672728c",
15
- "version": "5.0.0rc2"
14
+ "full-revisionid": "e958acd66930fab10e9517ba712e0b914f284a0b",
15
+ "version": "5.1.0"
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,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: SupportsScript[AnyStr] | None = None,
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: SupportsScript[AnyStr] | None = 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[RedisValueT] | None = None,
81
- client: SupportsScript[AnyStr] | None = None,
79
+ args: Parameters[ValueT] | None = None,
80
+ client: coredis.client.Client[AnyStr] | None = None,
82
81
  readonly: bool | None = None,
83
- ) -> CommandRequest[ResponseType]:
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 cast(
117
- CommandRequest[ResponseType],
118
- retryable(
119
- ConstantRetryPolicy((NoScriptError,), 1, 0),
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[RedisValueT] | None = None,
128
- client: SupportsScript[AnyStr] | None = None,
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
- 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(
@@ -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 hasattr(parent, "response"):
172
- return self.callback(await parent.response)
168
+ if (r := await self.parent) == self.client: # type: ignore
169
+ return r # type: ignore
173
170
  else:
174
- return await parent # type: ignore[no-any-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
 
@@ -155,17 +155,20 @@ class ConnectionPool:
155
155
  pass
156
156
 
157
157
  if parsed_url.scheme == "rediss":
158
- keyfile = cast(str | None, url_options.pop("ssl_keyfile", None))
159
- certfile = cast(str | None, url_options.pop("ssl_certfile", None))
160
- check_hostname = query_param_to_bool(url_options.pop("ssl_check_hostname", None))
161
- cert_reqs = cast(
162
- str | VerifyMode | None,
163
- url_options.pop("ssl_cert_reqs", None),
164
- )
165
- ca_certs = cast(str | None, url_options.pop("ssl_ca_certs", None))
166
- url_options["ssl_context"] = RedisSSLContext(
167
- keyfile, certfile, cert_reqs, ca_certs, check_hostname
168
- ).get()
158
+ if "ssl_context" not in kwargs:
159
+ keyfile = cast(str | None, url_options.pop("ssl_keyfile", None))
160
+ certfile = cast(str | None, url_options.pop("ssl_certfile", None))
161
+ check_hostname = query_param_to_bool(
162
+ url_options.pop("ssl_check_hostname", None)
163
+ )
164
+ cert_reqs = cast(
165
+ str | VerifyMode | None,
166
+ url_options.pop("ssl_cert_reqs", None),
167
+ )
168
+ ca_certs = cast(str | None, url_options.pop("ssl_ca_certs", None))
169
+ url_options["ssl_context"] = RedisSSLContext(
170
+ keyfile, certfile, cert_reqs, ca_certs, check_hostname
171
+ ).get()
169
172
 
170
173
  # last shot at the db value
171
174
  _db = url_options.get("db", db or 0)
@@ -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
- ) -> dict[str, str | int | bool]:
43
+ ) -> EncodingInsensitiveDict[str, str | int | bool]:
45
44
  it = iter(response)
46
- result: dict[str, str | int | bool] = {}
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: MutableMapping[str, int | str | bool],
58
- ) -> MutableMapping[str, int | str | bool]:
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
- ) -> MutableMapping[str, int | str | bool]:
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, str | int | bool],
84
+ dict[str, ResponsePrimitive],
86
85
  ]
87
86
  ):
88
87
  def transform(
89
88
  self,
90
89
  response: ResponseType,
91
- ) -> dict[str, str | int | bool]:
92
- return dict(parse_sentinel_state(cast(list[ResponsePrimitive], response)))
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, str | int | bool]:
98
- return dict(add_flags(EncodingInsensitiveDict(response)))
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, str | int | bool]],
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, str | int | bool]]:
112
- result: dict[str, dict[str, str | int | bool]] = {}
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, str | int | bool]]:
124
- states: dict[str, dict[str, str | int | bool]] = {}
122
+ ) -> dict[str, dict[str, ResponsePrimitive]]:
123
+ states: dict[str, dict[str, ResponsePrimitive]] = {}
125
124
  for state in response:
126
- proxy = add_flags(EncodingInsensitiveDict(state))
127
- states[nativestr(proxy["name"])] = dict(proxy)
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, str | bool | int], ...],
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, str | bool | int], ...]:
142
- return tuple(dict(parse_sentinel_state([nativestr(i) for i in item])) for item in response)
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, str | bool | int], ...]:
148
- return tuple(dict(add_flags(EncodingInsensitiveDict(state))) for state in response)
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, ResponseType]]],
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, ResponseType]]]:
178
+ ) -> dict[AnyStr, dict[int, dict[str, ResponsePrimitive]]]:
176
179
  return {response[0]: {r[0]: InfoCallback()(r[1]) for r in response[1]}}