Skip to content

Commit 9edfb0d

Browse files
github-actions[bot]claudestrawgate
committed
refactor: switch key input parameters from list to Sequence
Switch all key input parameters in bulk operations from list[str] to Sequence[str] while keeping return types as list[str]. This follows the Liskov Substitution Principle - accept more general types as inputs, return more specific types as outputs. This change completes the pattern started in #103 (Mapping for values) and improves the API's flexibility by accepting any sequence-like object (list, tuple, etc.) as input while maintaining backward compatibility. All existing code continues to work since list is a subclass of Sequence. Changes: - Updated AsyncKeyValueProtocol with Sequence for bulk operation keys - Updated BaseStore and all wrapper implementations - Updated adapters - Updated shared utility functions - Regenerated sync library Fixes #116 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: William Easton <strawgate@users.noreply.github.com>
1 parent 1c8bf6f commit 9edfb0d

File tree

23 files changed

+107
-98
lines changed

23 files changed

+107
-98
lines changed

key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ async def get(self, key: str, *, collection: str | None = None) -> T | None:
8282

8383
return None
8484

85-
async def get_many(self, keys: list[str], *, collection: str | None = None) -> list[T | None]:
85+
async def get_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[T | None]:
8686
"""Batch get and validate models by keys, preserving order.
8787
8888
Returns:
@@ -110,7 +110,7 @@ async def put(self, key: str, value: T, *, collection: str | None = None, ttl: S
110110
await self._key_value.put(key=key, value=value_dict, collection=collection, ttl=ttl)
111111

112112
async def put_many(
113-
self, keys: list[str], values: Sequence[T], *, collection: str | None = None, ttl: Sequence[SupportsFloat | None] | None = None
113+
self, keys: Sequence[str], values: Sequence[T], *, collection: str | None = None, ttl: Sequence[SupportsFloat | None] | None = None
114114
) -> None:
115115
"""Serialize and store multiple models, preserving order alignment with keys."""
116116
collection = collection or self._default_collection
@@ -125,7 +125,7 @@ async def delete(self, key: str, *, collection: str | None = None) -> bool:
125125

126126
return await self._key_value.delete(key=key, collection=collection)
127127

128-
async def delete_many(self, keys: list[str], *, collection: str | None = None) -> int:
128+
async def delete_many(self, keys: Sequence[str], *, collection: str | None = None) -> int:
129129
"""Delete multiple models by key. Returns the count of deleted entries."""
130130
collection = collection or self._default_collection
131131

@@ -151,7 +151,7 @@ async def ttl(self, key: str, *, collection: str | None = None) -> tuple[T | Non
151151

152152
return (None, None)
153153

154-
async def ttl_many(self, keys: list[str], *, collection: str | None = None) -> list[tuple[T | None, float | None]]:
154+
async def ttl_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[tuple[T | None, float | None]]:
155155
"""Batch get models with TTLs. Each element is (model|None, ttl_seconds|None)."""
156156
collection = collection or self._default_collection
157157

key-value/key-value-aio/src/key_value/aio/adapters/raise_on_missing/adapter.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,16 @@ async def get(
5050

5151
@overload
5252
async def get_many(
53-
self, keys: list[str], *, collection: str | None = None, raise_on_missing: Literal[False] = False
53+
self, keys: Sequence[str], *, collection: str | None = None, raise_on_missing: Literal[False] = False
5454
) -> list[dict[str, Any] | None]: ...
5555

5656
@overload
5757
async def get_many(
58-
self, keys: list[str], *, collection: str | None = None, raise_on_missing: Literal[True]
58+
self, keys: Sequence[str], *, collection: str | None = None, raise_on_missing: Literal[True]
5959
) -> list[dict[str, Any]]: ...
6060

6161
async def get_many(
62-
self, keys: list[str], *, collection: str | None = None, raise_on_missing: bool = False
62+
self, keys: Sequence[str], *, collection: str | None = None, raise_on_missing: bool = False
6363
) -> list[dict[str, Any]] | list[dict[str, Any] | None]:
6464
"""Retrieve multiple values by key from the specified collection.
6565
@@ -113,16 +113,16 @@ async def ttl(
113113

114114
@overload
115115
async def ttl_many(
116-
self, keys: list[str], *, collection: str | None = None, raise_on_missing: Literal[False] = False
116+
self, keys: Sequence[str], *, collection: str | None = None, raise_on_missing: Literal[False] = False
117117
) -> list[tuple[dict[str, Any] | None, float | None]]: ...
118118

119119
@overload
120120
async def ttl_many(
121-
self, keys: list[str], *, collection: str | None = None, raise_on_missing: Literal[True]
121+
self, keys: Sequence[str], *, collection: str | None = None, raise_on_missing: Literal[True]
122122
) -> list[tuple[dict[str, Any], float | None]]: ...
123123

124124
async def ttl_many(
125-
self, keys: list[str], *, collection: str | None = None, raise_on_missing: bool = False
125+
self, keys: Sequence[str], *, collection: str | None = None, raise_on_missing: bool = False
126126
) -> list[tuple[dict[str, Any], float | None]] | list[tuple[dict[str, Any] | None, float | None]]:
127127
"""Retrieve multiple values and TTL information by key from the specified collection.
128128
@@ -152,7 +152,7 @@ async def put(self, key: str, value: Mapping[str, Any], *, collection: str | Non
152152

153153
async def put_many(
154154
self,
155-
keys: list[str],
155+
keys: Sequence[str],
156156
values: Sequence[Mapping[str, Any]],
157157
*,
158158
collection: str | None = None,
@@ -178,7 +178,7 @@ async def delete(self, key: str, *, collection: str | None = None) -> bool:
178178
"""
179179
return await self.key_value.delete(key=key, collection=collection)
180180

181-
async def delete_many(self, keys: list[str], *, collection: str | None = None) -> int:
181+
async def delete_many(self, keys: Sequence[str], *, collection: str | None = None) -> int:
182182
"""Delete multiple key-value pairs from the specified collection.
183183
184184
Args:

key-value/key-value-aio/src/key_value/aio/protocols/key_value.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ async def delete(self, key: str, *, collection: str | None = None) -> bool:
5757
"""
5858
...
5959

60-
async def get_many(self, keys: list[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
60+
async def get_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
6161
"""Retrieve multiple values by key from the specified collection.
6262
6363
Args:
@@ -69,7 +69,7 @@ async def get_many(self, keys: list[str], *, collection: str | None = None) -> l
6969
"""
7070
...
7171

72-
async def ttl_many(self, keys: list[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
72+
async def ttl_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
7373
"""Retrieve multiple values and TTL information by key from the specified collection.
7474
7575
Args:
@@ -84,7 +84,7 @@ async def ttl_many(self, keys: list[str], *, collection: str | None = None) -> l
8484

8585
async def put_many(
8686
self,
87-
keys: list[str],
87+
keys: Sequence[str],
8888
values: Sequence[Mapping[str, Any]],
8989
*,
9090
collection: str | None = None,
@@ -101,7 +101,7 @@ async def put_many(
101101
"""
102102
...
103103

104-
async def delete_many(self, keys: list[str], *, collection: str | None = None) -> int:
104+
async def delete_many(self, keys: Sequence[str], *, collection: str | None = None) -> int:
105105
"""Delete multiple key-value pairs from the specified collection.
106106
107107
Args:

key-value/key-value-aio/src/key_value/aio/stores/base.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ async def setup_collection(self, *, collection: str) -> None:
102102
async def _get_managed_entry(self, *, collection: str, key: str) -> ManagedEntry | None:
103103
"""Retrieve a cache entry by key from the specified collection."""
104104

105-
async def _get_managed_entries(self, *, collection: str, keys: list[str]) -> list[ManagedEntry | None]:
105+
async def _get_managed_entries(self, *, collection: str, keys: Sequence[str]) -> list[ManagedEntry | None]:
106106
"""Retrieve multiple managed entries by key from the specified collection."""
107107

108108
return [await self._get_managed_entry(collection=collection, key=key) for key in keys]
@@ -137,7 +137,7 @@ async def get(
137137
return dict(managed_entry.value)
138138

139139
@override
140-
async def get_many(self, keys: list[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
140+
async def get_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
141141
collection = collection or self.default_collection
142142
await self.setup_collection(collection=collection)
143143

@@ -159,7 +159,7 @@ async def ttl(self, key: str, *, collection: str | None = None) -> tuple[dict[st
159159
@override
160160
async def ttl_many(
161161
self,
162-
keys: list[str],
162+
keys: Sequence[str],
163163
*,
164164
collection: str | None = None,
165165
) -> list[tuple[dict[str, Any] | None, float | None]]:
@@ -179,7 +179,7 @@ async def _put_managed_entry(self, *, collection: str, key: str, managed_entry:
179179
"""Store a managed entry by key in the specified collection."""
180180
...
181181

182-
async def _put_managed_entries(self, *, collection: str, keys: list[str], managed_entries: Sequence[ManagedEntry]) -> None:
182+
async def _put_managed_entries(self, *, collection: str, keys: Sequence[str], managed_entries: Sequence[ManagedEntry]) -> None:
183183
"""Store multiple managed entries by key in the specified collection."""
184184

185185
for key, managed_entry in zip(keys, managed_entries, strict=True):
@@ -204,8 +204,8 @@ async def put(self, key: str, value: Mapping[str, Any], *, collection: str | Non
204204
)
205205

206206
def _prepare_put_many(
207-
self, *, keys: list[str], values: Sequence[Mapping[str, Any]], ttl: Sequence[SupportsFloat | None] | SupportsFloat | None
208-
) -> tuple[list[str], Sequence[Mapping[str, Any]], list[float | None]]:
207+
self, *, keys: Sequence[str], values: Sequence[Mapping[str, Any]], ttl: Sequence[SupportsFloat | None] | SupportsFloat | None
208+
) -> tuple[Sequence[str], Sequence[Mapping[str, Any]], list[float | None]]:
209209
"""Prepare multiple managed entries for a put_many operation.
210210
211211
Inheriting classes can use this method if they need to modify a put_many operation."""
@@ -225,7 +225,7 @@ def _prepare_put_many(
225225
@override
226226
async def put_many(
227227
self,
228-
keys: list[str],
228+
keys: Sequence[str],
229229
values: Sequence[Mapping[str, Any]],
230230
*,
231231
collection: str | None = None,
@@ -249,7 +249,7 @@ async def _delete_managed_entry(self, *, key: str, collection: str) -> bool:
249249
"""Delete a managed entry by key from the specified collection."""
250250
...
251251

252-
async def _delete_managed_entries(self, *, keys: list[str], collection: str) -> int:
252+
async def _delete_managed_entries(self, *, keys: Sequence[str], collection: str) -> int:
253253
"""Delete multiple managed entries by key from the specified collection."""
254254

255255
deleted_count: int = 0
@@ -268,7 +268,7 @@ async def delete(self, key: str, *, collection: str | None = None) -> bool:
268268
return await self._delete_managed_entry(key=key, collection=collection)
269269

270270
@override
271-
async def delete_many(self, keys: list[str], *, collection: str | None = None) -> int:
271+
async def delete_many(self, keys: Sequence[str], *, collection: str | None = None) -> int:
272272
"""Delete multiple managed entries by key from the specified collection."""
273273
collection = collection or self.default_collection
274274
await self.setup_collection(collection=collection)

key-value/key-value-aio/src/key_value/aio/stores/elasticsearch/store.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from typing import TYPE_CHECKING, Any, overload
22

3+
if TYPE_CHECKING:
4+
from collections.abc import Sequence
5+
36
from key_value.shared.utils.compound import compound_key
47
from key_value.shared.utils.managed_entry import ManagedEntry, load_from_json
58
from key_value.shared.utils.sanitize import (
@@ -253,7 +256,7 @@ async def _get_collection_keys(self, *, collection: str, limit: int | None = Non
253256
if not (hits := get_hits_from_response(response=result)):
254257
return []
255258

256-
all_keys: list[str] = []
259+
all_keys: Sequence[str] = []
257260

258261
for hit in hits:
259262
if not (key := get_first_value_from_field_in_hit(hit=hit, field="key", value_type=str)):

key-value/key-value-aio/src/key_value/aio/stores/redis/store.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
from typing import Any, overload
1+
from typing import TYPE_CHECKING, Any, overload
22
from urllib.parse import urlparse
33

4+
if TYPE_CHECKING:
5+
from collections.abc import Sequence
6+
47
from key_value.shared.type_checking.bear_spray import bear_spray
58
from key_value.shared.utils.compound import compound_key, get_keys_from_compound_keys
69
from key_value.shared.utils.managed_entry import ManagedEntry
@@ -128,7 +131,7 @@ async def _get_collection_keys(self, *, collection: str, limit: int | None = Non
128131

129132
# redis.asyncio scan returns tuple(cursor, keys)
130133
_cursor: int
131-
keys: list[str]
134+
keys: Sequence[str]
132135
_cursor, keys = await self._client.scan(cursor=0, match=pattern, count=limit) # pyright: ignore[reportUnknownMemberType, reportAny]
133136

134137
return get_keys_from_compound_keys(compound_keys=keys, collection=collection)

key-value/key-value-aio/src/key_value/aio/wrappers/base.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ async def get(self, key: str, *, collection: str | None = None) -> dict[str, Any
1616
return await self.key_value.get(collection=collection, key=key)
1717

1818
@override
19-
async def get_many(self, keys: list[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
19+
async def get_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
2020
return await self.key_value.get_many(collection=collection, keys=keys)
2121

2222
@override
2323
async def ttl(self, key: str, *, collection: str | None = None) -> tuple[dict[str, Any] | None, float | None]:
2424
return await self.key_value.ttl(collection=collection, key=key)
2525

2626
@override
27-
async def ttl_many(self, keys: list[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
27+
async def ttl_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
2828
return await self.key_value.ttl_many(collection=collection, keys=keys)
2929

3030
@override
@@ -34,7 +34,7 @@ async def put(self, key: str, value: Mapping[str, Any], *, collection: str | Non
3434
@override
3535
async def put_many(
3636
self,
37-
keys: list[str],
37+
keys: Sequence[str],
3838
values: Sequence[Mapping[str, Any]],
3939
*,
4040
collection: str | None = None,
@@ -47,5 +47,5 @@ async def delete(self, key: str, *, collection: str | None = None) -> bool:
4747
return await self.key_value.delete(collection=collection, key=key)
4848

4949
@override
50-
async def delete_many(self, keys: list[str], *, collection: str | None = None) -> int:
50+
async def delete_many(self, keys: Sequence[str], *, collection: str | None = None) -> int:
5151
return await self.key_value.delete_many(keys=keys, collection=collection)

key-value/key-value-aio/src/key_value/aio/wrappers/compression/wrapper.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ async def get(self, key: str, *, collection: str | None = None) -> dict[str, Any
116116
return self._decompress_value(value)
117117

118118
@override
119-
async def get_many(self, keys: list[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
119+
async def get_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
120120
values = await self.key_value.get_many(keys=keys, collection=collection)
121121
return [self._decompress_value(value) for value in values]
122122

@@ -126,7 +126,7 @@ async def ttl(self, key: str, *, collection: str | None = None) -> tuple[dict[st
126126
return self._decompress_value(value), ttl
127127

128128
@override
129-
async def ttl_many(self, keys: list[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
129+
async def ttl_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
130130
results = await self.key_value.ttl_many(keys=keys, collection=collection)
131131
return [(self._decompress_value(value), ttl) for value, ttl in results]
132132

@@ -138,7 +138,7 @@ async def put(self, key: str, value: Mapping[str, Any], *, collection: str | Non
138138
@override
139139
async def put_many(
140140
self,
141-
keys: list[str],
141+
keys: Sequence[str],
142142
values: Sequence[Mapping[str, Any]],
143143
*,
144144
collection: str | None = None,

key-value/key-value-aio/src/key_value/aio/wrappers/encryption/base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ async def get(self, key: str, *, collection: str | None = None) -> dict[str, Any
143143
return self._decrypt_value(value)
144144

145145
@override
146-
async def get_many(self, keys: list[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
146+
async def get_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
147147
values = await self.key_value.get_many(keys=keys, collection=collection)
148148
return [self._decrypt_value(value) for value in values]
149149

@@ -153,7 +153,7 @@ async def ttl(self, key: str, *, collection: str | None = None) -> tuple[dict[st
153153
return self._decrypt_value(value), ttl
154154

155155
@override
156-
async def ttl_many(self, keys: list[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
156+
async def ttl_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
157157
results = await self.key_value.ttl_many(keys=keys, collection=collection)
158158
return [(self._decrypt_value(value), ttl) for value, ttl in results]
159159

@@ -165,7 +165,7 @@ async def put(self, key: str, value: Mapping[str, Any], *, collection: str | Non
165165
@override
166166
async def put_many(
167167
self,
168-
keys: list[str],
168+
keys: Sequence[str],
169169
values: Sequence[Mapping[str, Any]],
170170
*,
171171
collection: str | None = None,

key-value/key-value-aio/src/key_value/aio/wrappers/fallback/wrapper.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ async def get(self, key: str, *, collection: str | None = None) -> dict[str, Any
5050
return await self.fallback_key_value.get(key=key, collection=collection)
5151

5252
@override
53-
async def get_many(self, keys: list[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
53+
async def get_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[dict[str, Any] | None]:
5454
try:
5555
return await self.primary_key_value.get_many(keys=keys, collection=collection)
5656
except self.fallback_on:
@@ -64,7 +64,7 @@ async def ttl(self, key: str, *, collection: str | None = None) -> tuple[dict[st
6464
return await self.fallback_key_value.ttl(key=key, collection=collection)
6565

6666
@override
67-
async def ttl_many(self, keys: list[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
67+
async def ttl_many(self, keys: Sequence[str], *, collection: str | None = None) -> list[tuple[dict[str, Any] | None, float | None]]:
6868
try:
6969
return await self.primary_key_value.ttl_many(keys=keys, collection=collection)
7070
except self.fallback_on:
@@ -83,7 +83,7 @@ async def put(self, key: str, value: Mapping[str, Any], *, collection: str | Non
8383
@override
8484
async def put_many(
8585
self,
86-
keys: list[str],
86+
keys: Sequence[str],
8787
values: Sequence[Mapping[str, Any]],
8888
*,
8989
collection: str | None = None,
@@ -108,7 +108,7 @@ async def delete(self, key: str, *, collection: str | None = None) -> bool:
108108
return await self.primary_key_value.delete(key=key, collection=collection)
109109

110110
@override
111-
async def delete_many(self, keys: list[str], *, collection: str | None = None) -> int:
111+
async def delete_many(self, keys: Sequence[str], *, collection: str | None = None) -> int:
112112
if self.write_to_fallback:
113113
try:
114114
return await self.primary_key_value.delete_many(keys=keys, collection=collection)

0 commit comments

Comments
 (0)