coredis 4.24.0__tar.gz → 5.0.0rc1__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 (119) hide show
  1. {coredis-4.24.0 → coredis-5.0.0rc1}/HISTORY.rst +24 -0
  2. {coredis-4.24.0/coredis.egg-info → coredis-5.0.0rc1}/PKG-INFO +4 -5
  3. {coredis-4.24.0 → coredis-5.0.0rc1}/README.md +1 -2
  4. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/__init__.py +1 -3
  5. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/_packer.py +10 -10
  6. coredis-5.0.0rc1/coredis/_protocols.py +73 -0
  7. coredis-5.0.0rc1/coredis/_py_311_typing.py +20 -0
  8. coredis-5.0.0rc1/coredis/_py_312_typing.py +17 -0
  9. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/_utils.py +49 -51
  10. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/_version.py +3 -3
  11. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/cache.py +57 -82
  12. coredis-5.0.0rc1/coredis/client/__init__.py +6 -0
  13. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/client/basic.py +129 -56
  14. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/client/cluster.py +147 -70
  15. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/__init__.py +27 -7
  16. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/_key_spec.py +11 -10
  17. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/_utils.py +1 -1
  18. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/_validators.py +30 -20
  19. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/_wrappers.py +19 -99
  20. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/bitfield.py +10 -2
  21. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/constants.py +20 -3
  22. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/core.py +1627 -1246
  23. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/function.py +21 -19
  24. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/monitor.py +0 -71
  25. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/pubsub.py +7 -142
  26. coredis-5.0.0rc1/coredis/commands/request.py +108 -0
  27. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/script.py +9 -9
  28. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/commands/sentinel.py +60 -49
  29. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/connection.py +14 -15
  30. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/exceptions.py +2 -2
  31. coredis-5.0.0rc1/coredis/experimental/__init__.py +1 -0
  32. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/globals.py +3 -0
  33. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/autocomplete.py +28 -30
  34. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/base.py +15 -31
  35. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/filters.py +269 -245
  36. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/graph.py +61 -62
  37. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/json.py +172 -140
  38. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/response/_callbacks/autocomplete.py +5 -4
  39. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/response/_callbacks/graph.py +34 -29
  40. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/response/_callbacks/json.py +5 -3
  41. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/response/_callbacks/search.py +49 -53
  42. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/response/_callbacks/timeseries.py +18 -30
  43. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/response/types.py +1 -5
  44. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/search.py +186 -169
  45. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/timeseries.py +184 -164
  46. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/parser.py +6 -19
  47. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/pipeline.py +391 -422
  48. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/pool/basic.py +7 -7
  49. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/pool/cluster.py +3 -3
  50. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/pool/nodemanager.py +10 -3
  51. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/__init__.py +76 -57
  52. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/acl.py +0 -3
  53. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/cluster.py +25 -16
  54. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/command.py +8 -6
  55. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/connection.py +4 -3
  56. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/geo.py +17 -13
  57. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/hash.py +13 -11
  58. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/keys.py +9 -5
  59. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/module.py +2 -3
  60. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/script.py +6 -8
  61. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/sentinel.py +21 -17
  62. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/server.py +36 -14
  63. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/sets.py +3 -4
  64. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/sorted_set.py +27 -24
  65. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/streams.py +22 -13
  66. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_callbacks/strings.py +7 -6
  67. coredis-5.0.0rc1/coredis/response/_callbacks/vector_sets.py +126 -0
  68. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/types.py +13 -4
  69. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/sentinel.py +1 -1
  70. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/stream.py +4 -3
  71. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/tokens.py +343 -16
  72. coredis-5.0.0rc1/coredis/typing.py +582 -0
  73. {coredis-4.24.0 → coredis-5.0.0rc1/coredis.egg-info}/PKG-INFO +4 -5
  74. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis.egg-info/SOURCES.txt +4 -2
  75. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis.egg-info/requires.txt +2 -2
  76. {coredis-4.24.0 → coredis-5.0.0rc1}/pyproject.toml +3 -2
  77. {coredis-4.24.0 → coredis-5.0.0rc1}/requirements/dev.txt +1 -1
  78. {coredis-4.24.0 → coredis-5.0.0rc1}/requirements/docs.txt +1 -1
  79. {coredis-4.24.0 → coredis-5.0.0rc1}/requirements/main.txt +2 -2
  80. coredis-4.24.0/coredis/_protocols.py +0 -82
  81. coredis-4.24.0/coredis/client/__init__.py +0 -7
  82. coredis-4.24.0/coredis/client/keydb.py +0 -336
  83. coredis-4.24.0/coredis/experimental/__init__.py +0 -5
  84. coredis-4.24.0/coredis/pipeline.pyi +0 -2103
  85. coredis-4.24.0/coredis/typing.py +0 -229
  86. {coredis-4.24.0 → coredis-5.0.0rc1}/LICENSE +0 -0
  87. {coredis-4.24.0 → coredis-5.0.0rc1}/MANIFEST.in +0 -0
  88. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/_json.py +0 -0
  89. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/_sidecar.py +0 -0
  90. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/config.py +0 -0
  91. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/constants.py +0 -0
  92. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/credentials.py +0 -0
  93. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/__init__.py +0 -0
  94. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/response/__init__.py +0 -0
  95. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/modules/response/_callbacks/__init__.py +0 -0
  96. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/pool/__init__.py +0 -0
  97. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/py.typed +0 -0
  98. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/recipes/__init__.py +0 -0
  99. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/recipes/credentials/__init__.py +0 -0
  100. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/recipes/credentials/iam_provider.py +0 -0
  101. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/recipes/locks/__init__.py +0 -0
  102. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/recipes/locks/extend.lua +0 -0
  103. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/recipes/locks/lua_lock.py +0 -0
  104. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/recipes/locks/release.lua +0 -0
  105. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/__init__.py +0 -0
  106. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/response/_utils.py +0 -0
  107. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/retry.py +0 -0
  108. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/speedups.c +0 -0
  109. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis/speedups.pyi +0 -0
  110. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis.egg-info/dependency_links.txt +0 -0
  111. {coredis-4.24.0 → coredis-5.0.0rc1}/coredis.egg-info/top_level.txt +0 -0
  112. {coredis-4.24.0 → coredis-5.0.0rc1}/requirements/ci.txt +0 -0
  113. {coredis-4.24.0 → coredis-5.0.0rc1}/requirements/dev_extra.txt +0 -0
  114. {coredis-4.24.0 → coredis-5.0.0rc1}/requirements/publishing.txt +0 -0
  115. {coredis-4.24.0 → coredis-5.0.0rc1}/requirements/recipes.txt +0 -0
  116. {coredis-4.24.0 → coredis-5.0.0rc1}/requirements/test.txt +0 -0
  117. {coredis-4.24.0 → coredis-5.0.0rc1}/setup.cfg +0 -0
  118. {coredis-4.24.0 → coredis-5.0.0rc1}/setup.py +0 -0
  119. {coredis-4.24.0 → coredis-5.0.0rc1}/versioneer.py +0 -0
@@ -3,6 +3,29 @@
3
3
  Changelog
4
4
  =========
5
5
 
6
+ v5.0.0rc1
7
+ ---------
8
+ Release Date: 2025-07-07
9
+
10
+ * Features
11
+
12
+ * Add support for using custom types with redis commands
13
+ by registering serializers and deserializers
14
+ * Allow stacking pipeline commands syncronously
15
+ * Expose statically types responses for pipeline commands
16
+
17
+
18
+ * Compatibility
19
+
20
+ * Add support for redis 8.0 vector set commands
21
+ * Add support for redis 8.0 hash expiry commands
22
+ * Remove deprecated pubsub ``listen`` and threaded worker APIs
23
+ * Remove support for KeyDB
24
+
25
+ * Performance
26
+
27
+ * Streamline client side cache shrinking
28
+
6
29
  v4.24.0
7
30
  -------
8
31
  Release Date: 2025-07-05
@@ -1939,3 +1962,4 @@ v1.0.1
1939
1962
 
1940
1963
 
1941
1964
 
1965
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coredis
3
- Version: 4.24.0
3
+ Version: 5.0.0rc1
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
@@ -26,11 +26,11 @@ Requires-Python: >=3.10
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: async_timeout<6,>4
29
+ Requires-Dist: beartype>=0.20
29
30
  Requires-Dist: deprecated>=1.2
30
- Requires-Dist: typing_extensions>=4.3
31
+ Requires-Dist: typing_extensions>=4.13
31
32
  Requires-Dist: packaging<26,>=21
32
33
  Requires-Dist: pympler<2,>1
33
- Requires-Dist: wrapt<2,>=1.1.0
34
34
  Provides-Extra: recipes
35
35
  Requires-Dist: aiobotocore>=2.15.2; extra == "recipes"
36
36
  Requires-Dist: asyncache>=0.3.1; extra == "recipes"
@@ -201,7 +201,7 @@ Details about supported Redis modules and their commands can be found
201
201
 
202
202
  ## Compatibility
203
203
 
204
- coredis is tested against redis versions >= `6.4`
204
+ coredis is tested against redis versions >= `7.0`
205
205
  The test matrix status can be reviewed
206
206
  [here](https://github.com/alisaifee/coredis/actions/workflows/main.yml)
207
207
 
@@ -221,7 +221,6 @@ coredis is additionally tested against:
221
221
 
222
222
  **coredis** is known to work with the following databases that have redis protocol compatibility:
223
223
 
224
- - [KeyDB](https://docs.keydb.dev/)
225
224
  - [Dragonfly](https://dragonflydb.io/)
226
225
  - [Redict](https://redict.io/)
227
226
  - [Valkey](https://github.com/valkey-io/valkey)
@@ -148,7 +148,7 @@ Details about supported Redis modules and their commands can be found
148
148
 
149
149
  ## Compatibility
150
150
 
151
- coredis is tested against redis versions >= `6.4`
151
+ coredis is tested against redis versions >= `7.0`
152
152
  The test matrix status can be reviewed
153
153
  [here](https://github.com/alisaifee/coredis/actions/workflows/main.yml)
154
154
 
@@ -168,7 +168,6 @@ coredis is additionally tested against:
168
168
 
169
169
  **coredis** is known to work with the following databases that have redis protocol compatibility:
170
170
 
171
- - [KeyDB](https://docs.keydb.dev/)
172
171
  - [Dragonfly](https://dragonflydb.io/)
173
172
  - [Redict](https://redict.io/)
174
173
  - [Valkey](https://github.com/valkey-io/valkey)
@@ -10,7 +10,7 @@ from __future__ import annotations
10
10
 
11
11
  from typing import cast
12
12
 
13
- from coredis.client import KeyDB, KeyDBCluster, Redis, RedisCluster
13
+ from coredis.client import Redis, RedisCluster
14
14
  from coredis.config import Config
15
15
  from coredis.connection import (
16
16
  BaseConnection,
@@ -30,8 +30,6 @@ from . import _version
30
30
 
31
31
  __all__ = [
32
32
  "Config",
33
- "KeyDB",
34
- "KeyDBCluster",
35
33
  "Redis",
36
34
  "RedisCluster",
37
35
  "BaseConnection",
@@ -1,14 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from coredis.constants import SYM_CRLF, SYM_DOLLAR, SYM_EMPTY, SYM_STAR
4
- from coredis.typing import ValueT
4
+ from coredis.typing import RedisValueT
5
5
 
6
6
 
7
7
  class Packer:
8
8
  def __init__(self, encoding: str):
9
9
  self.encoding = encoding
10
10
 
11
- def encode(self, value: ValueT) -> bytes:
11
+ def encode(self, value: RedisValueT) -> bytes:
12
12
  """Returns a bytestring representation of the value"""
13
13
  if isinstance(value, str):
14
14
  return value.encode(self.encoding)
@@ -18,7 +18,7 @@ class Packer:
18
18
  return b"%.15g" % value
19
19
  return value
20
20
 
21
- def pack_command(self, command: bytes, *args: ValueT) -> list[bytes]:
21
+ def pack_command(self, command: bytes, *args: RedisValueT) -> list[bytes]:
22
22
  "Pack a series of arguments into the Redis protocol"
23
23
  output: list[bytes] = []
24
24
  # the client might have included 1 or more literal arguments in
@@ -50,22 +50,22 @@ class Packer:
50
50
  output.append(buff)
51
51
  return output
52
52
 
53
- def pack_commands(self, commands: list[tuple[ValueT, ...]]) -> list[bytes]:
53
+ def pack_commands(self, commands: list[tuple[RedisValueT, ...]]) -> list[bytes]:
54
54
  output: list[bytes] = []
55
- pieces: list[bytes] = []
55
+ command_arguments: list[bytes] = []
56
56
  buffer_length = 0
57
57
 
58
58
  for cmd in commands:
59
59
  for chunk in self.pack_command(self.encode(cmd[0]), *cmd[1:]):
60
- pieces.append(chunk)
60
+ command_arguments.append(chunk)
61
61
  buffer_length += len(chunk)
62
62
 
63
63
  if buffer_length > 6000:
64
- output.append(SYM_EMPTY.join(pieces))
64
+ output.append(SYM_EMPTY.join(command_arguments))
65
65
  buffer_length = 0
66
- pieces = []
66
+ command_arguments = []
67
67
 
68
- if pieces:
69
- output.append(SYM_EMPTY.join(pieces))
68
+ if command_arguments:
69
+ output.append(SYM_EMPTY.join(command_arguments))
70
70
 
71
71
  return output
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+
5
+ from typing_extensions import runtime_checkable
6
+
7
+ from coredis.response._callbacks import NoopCallback
8
+ from coredis.typing import (
9
+ TYPE_CHECKING,
10
+ Awaitable,
11
+ Callable,
12
+ ExecutionParameters,
13
+ KeyT,
14
+ Parameters,
15
+ Protocol,
16
+ R,
17
+ RedisCommandP,
18
+ RedisValueT,
19
+ ResponseType,
20
+ StringT,
21
+ TypeVar,
22
+ Unpack,
23
+ ValueT,
24
+ )
25
+
26
+ T_co = TypeVar("T_co", covariant=True)
27
+
28
+
29
+ if TYPE_CHECKING:
30
+ from coredis.commands import CommandRequest
31
+
32
+
33
+ class AbstractExecutor(Protocol):
34
+ def execute_command(
35
+ self,
36
+ command: RedisCommandP,
37
+ callback: Callable[..., R] = NoopCallback(),
38
+ **options: Unpack[ExecutionParameters],
39
+ ) -> Awaitable[R]: ...
40
+
41
+ def create_request(
42
+ self,
43
+ name: bytes,
44
+ *arguments: ValueT,
45
+ callback: Callable[..., R],
46
+ execution_parameters: ExecutionParameters | None = None,
47
+ ) -> CommandRequest[R]: ...
48
+
49
+
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
+ @runtime_checkable
70
+ class ConnectionP(Protocol):
71
+ decode_responses: bool
72
+ encoding: str
73
+ push_messages: asyncio.Queue[ResponseType]
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Hashable
4
+ from typing import Any, TypeAlias
5
+
6
+ from .typing import MutableSet, RedisError, ResponsePrimitive
7
+
8
+ #: Represents the total structure of any response for any redis command.
9
+ ResponseType: TypeAlias = (
10
+ ResponsePrimitive
11
+ | list[Any]
12
+ | MutableSet[Hashable]
13
+ | dict[
14
+ Hashable,
15
+ Any,
16
+ ]
17
+ | RedisError
18
+ )
19
+ #: Type alias for valid python types that can be represented as json
20
+ JsonType: TypeAlias = str | int | float | bool | dict[str, Any] | list[Any] | None
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Hashable
4
+
5
+ from .typing import MutableSet, RedisError, ResponsePrimitive
6
+
7
+ #: Type alias for valid python types that can be represented as json
8
+ type JsonType = str | int | float | bool | dict[str, JsonType] | list[JsonType] | None
9
+
10
+ #: Represents the total structure of any response for any redis command.
11
+ type ResponseType = (
12
+ ResponsePrimitive
13
+ | RedisError
14
+ | list[ResponseType]
15
+ | MutableSet[Hashable]
16
+ | dict[Hashable, ResponseType]
17
+ )
@@ -1,14 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import enum
4
+ from collections import UserDict
4
5
  from typing import Any
5
6
 
6
- from wrapt import ObjectProxy
7
-
8
7
  from coredis.typing import (
9
8
  Hashable,
10
9
  Iterable,
11
10
  Mapping,
11
+ MutableMapping,
12
12
  ResponseType,
13
13
  StringT,
14
14
  TypeVar,
@@ -18,61 +18,59 @@ T = TypeVar("T")
18
18
  U = TypeVar("U")
19
19
 
20
20
 
21
- class EncodingInsensitiveDict(ObjectProxy): # type: ignore
21
+ class EncodingInsensitiveDict(UserDict[Any, Any]):
22
22
  def __init__(
23
23
  self,
24
- dict: Mapping[Any, Any] | ResponseType | None = None,
24
+ initial: MutableMapping[Any, Any] | None = None,
25
25
  encoding: str = "utf-8",
26
26
  ):
27
- d = dict or {}
28
- super().__init__(d)
29
- self._self_encoding = encoding
30
-
31
- def __getitem__(self, item: StringT) -> Any:
32
- if isinstance(item, str) and item not in self.__wrapped__:
33
- return self.__wrapped__.get(
34
- item, self.__wrapped__.get(item.encode(self._self_encoding))
35
- )
36
- elif isinstance(item, bytes) and item not in self.__wrapped__:
37
- return self.__wrapped__.get(
38
- item, self.__wrapped__.get(item.decode(self._self_encoding))
39
- )
40
- return self.__wrapped__[item]
41
-
42
- def get(self, item: StringT, default: object | None = None) -> Any:
43
- return self.__getitem__(item) or default
44
-
45
- def pop(self, item: StringT, default: object | None = None) -> Any:
46
- if item in self.__wrapped__:
47
- return self.__wrapped__.pop(item)
48
- if isinstance(item, str):
49
- return self.__wrapped__.pop(item.encode(self._self_encoding), default)
50
-
51
- def clear(self) -> None:
52
- self.__wrapped__.clear()
53
-
54
- def update(self, updates: Mapping[Any, Any]) -> None:
55
- self.__wrapped__.update(updates)
56
-
57
- def __setitem__(self, item: StringT, value: object) -> None:
58
- if item in self.__wrapped__:
59
- self.__wrapped__[item] = value
60
- elif isinstance(item, str) and item.encode(self._self_encoding) in self.__wrapped__:
61
- self.__wrapped__[item.encode(self._self_encoding)] = value
62
- elif isinstance(item, bytes) and item.decode(self._self_encoding) in self.__wrapped__:
63
- self.__wrapped__[item.decode(self._self_encoding)] = value
64
- else:
65
- self.__wrapped__[item] = value
66
-
67
- def __contains__(self, key: StringT) -> bool:
27
+ self._encoding = encoding
28
+ super().__init__(initial or {})
29
+
30
+ def _alt_key(self, key: StringT) -> StringT:
68
31
  if isinstance(key, str):
69
- return key in self.__wrapped__ or key.encode(self._self_encoding) in self.__wrapped__
32
+ try:
33
+ byte_alt = key.encode(self._encoding)
34
+ if byte_alt in self.data:
35
+ return byte_alt
36
+ except UnicodeEncodeError:
37
+ pass
70
38
  elif isinstance(key, bytes):
71
- return key in self.__wrapped__ or key.decode(self._self_encoding) in self.__wrapped__
72
- return key in self.__wrapped__
73
-
74
- def __repr__(self) -> str:
75
- return repr(self.__wrapped__)
39
+ try:
40
+ str_alt = key.decode(self._encoding)
41
+ if str_alt in self.data:
42
+ return str_alt
43
+ except UnicodeDecodeError:
44
+ pass
45
+ return key
46
+
47
+ def __getitem__(self, key: StringT) -> Any:
48
+ if key in self.data:
49
+ return self.data[key]
50
+ alt = self._alt_key(key)
51
+ if alt in self.data:
52
+ return self.data[alt]
53
+ raise KeyError(key)
54
+
55
+ def __setitem__(self, key: StringT, value: Any) -> None:
56
+ alt = self._alt_key(key)
57
+ self.data[alt] = value
58
+
59
+ def __delitem__(self, key: StringT) -> None:
60
+ alt = self._alt_key(key)
61
+ del self.data[alt]
62
+
63
+ def __contains__(self, key: Any) -> bool:
64
+ return key in self.data or self._alt_key(key) in self.data
65
+
66
+ def get(self, key: StringT, default: Any = None) -> Any:
67
+ return self.data.get(key, self.data.get(self._alt_key(key), default))
68
+
69
+ def pop(self, key: StringT, default: Any = None) -> Any:
70
+ if key in self.data:
71
+ return self.data.pop(key)
72
+ alt = self._alt_key(key)
73
+ return self.data.pop(alt, default)
76
74
 
77
75
 
78
76
  @enum.unique
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-07-05T19:34:23-0700",
11
+ "date": "2025-07-07T11:41:52-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "cd4583e1ad0eb6d8ca23e80203ab230efe1e3b2e",
15
- "version": "4.24.0"
14
+ "full-revisionid": "e2034feabddbed8c1a549ccc53976ca41d724a00",
15
+ "version": "5.0.0rc1"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18