Skip to content

Commit 542073d

Browse files
authored
Merge branch 'main' into claude/issue-48-20251027-0100
2 parents 7536540 + fefc363 commit 542073d

File tree

50 files changed

+495
-82
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+495
-82
lines changed

.github/workflows/claude-on-mention.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ jobs:
3636
- name: Install UV
3737
uses: astral-sh/setup-uv@v7
3838

39+
# Install dependencies
40+
- name: Install dependencies
41+
run: uv sync --all-packages --group dev
42+
3943
- name: Set triage prompt
4044
id: triage-prompt
4145
run: |

.github/workflows/claude-on-open-label.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ jobs:
3131
- name: Install UV
3232
uses: astral-sh/setup-uv@v7
3333

34+
# Install dependencies
35+
- name: Install dependencies
36+
run: uv sync --all-packages --group dev
37+
3438
- name: Set triage prompt
3539
id: triage-prompt
3640
run: |

.github/workflows/claude-on-test-failure.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ jobs:
3636
- name: Install UV
3737
uses: astral-sh/setup-uv@v7
3838

39+
# Install dependencies
40+
- name: Install dependencies
41+
run: uv sync --all-packages --group dev
42+
3943
- name: Set analysis prompt
4044
id: analysis-prompt
4145
run: |
@@ -138,7 +142,7 @@ jobs:
138142
"github-research-mcp"
139143
],
140144
"env": {
141-
"DISABLE_SUMMARIES": "true", # Disable verbose summaries for faster analysis
145+
"DISABLE_SUMMARIES": "true",
142146
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
143147
}
144148
}

docs/api/index.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# API Reference
2+
3+
Complete API reference documentation for py-key-value.
4+
5+
## Overview
6+
7+
The py-key-value API is organized into four main components:
8+
9+
- **[Protocols](protocols.md)** - Core interfaces for the key-value store
10+
- **[Stores](stores.md)** - Backend implementations for different storage systems
11+
- **[Wrappers](wrappers.md)** - Decorators that add functionality to stores
12+
- **[Adapters](adapters.md)** - Utilities that simplify working with stores
13+
14+
## Quick Links
15+
16+
### Core Protocols
17+
18+
The [`AsyncKeyValue`](protocols.md) protocol defines the async interface that all
19+
stores implement.
20+
21+
The [`KeyValue`](protocols.md) protocol is the synchronous version.
22+
23+
### Popular Stores
24+
25+
- [MemoryStore](stores.md) - In-memory storage
26+
- [RedisStore](stores.md) - Redis backend
27+
- [DiskStore](stores.md) - File-based storage
28+
29+
### Common Wrappers
30+
31+
- [LoggingWrapper](wrappers.md) - Add logging to any store
32+
- [CacheWrapper](wrappers.md) - Add caching layer
33+
- [RetryWrapper](wrappers.md) - Add automatic retry logic
34+
35+
## Using the API Reference
36+
37+
Each page provides:
38+
39+
- **Type signatures** - Full type information for all parameters and return values
40+
- **Docstrings** - Detailed descriptions of functionality
41+
- **Source links** - View the implementation on GitHub
42+
- **Cross-references** - Navigate between related components
43+
44+
## Example Usage
45+
46+
```python
47+
from key_value.aio.stores.memory import MemoryStore
48+
from key_value.aio.wrappers.logging import LoggingWrapper
49+
50+
# Create a store with logging
51+
store = LoggingWrapper(MemoryStore())
52+
53+
# Use the store
54+
await store.put("key", "value")
55+
result = await store.get("key")
56+
```
57+
58+
For more examples and guides, see the [User Guide](../stores.md).

key-value/key-value-aio/src/key_value/aio/stores/disk/multi_store.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,11 @@ async def _put_managed_entry(
132132
collection: str,
133133
managed_entry: ManagedEntry,
134134
) -> None:
135-
_ = self._cache[collection].set(key=key, value=self._serialization_adapter.dump_json(entry=managed_entry), expire=managed_entry.ttl)
135+
_ = self._cache[collection].set(
136+
key=key,
137+
value=self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection),
138+
expire=managed_entry.ttl,
139+
)
136140

137141
@override
138142
async def _delete_managed_entry(self, *, key: str, collection: str) -> bool:

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ async def _put_managed_entry(
107107
) -> None:
108108
combo_key: str = compound_key(collection=collection, key=key)
109109

110-
_ = self._cache.set(key=combo_key, value=self._serialization_adapter.dump_json(entry=managed_entry), expire=managed_entry.ttl)
110+
_ = self._cache.set(
111+
key=combo_key,
112+
value=self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection),
113+
expire=managed_entry.ttl,
114+
)
111115

112116
@override
113117
async def _delete_managed_entry(self, *, key: str, collection: str) -> bool:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ async def _put_managed_entry(
219219
managed_entry: ManagedEntry,
220220
) -> None:
221221
"""Store a managed entry in DynamoDB."""
222-
json_value = self._serialization_adapter.dump_json(entry=managed_entry)
222+
json_value = self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection)
223223

224224
item: dict[str, Any] = {
225225
"collection": {"S": collection},

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@
6767
"key": {
6868
"type": "keyword",
6969
},
70+
"version": {
71+
"type": "integer",
72+
},
7073
"value": {
7174
"properties": {
7275
"flattened": {
@@ -357,7 +360,7 @@ async def _put_managed_entry(
357360
index_name: str = self._get_index_name(collection=collection)
358361
document_id: str = self._get_document_id(key=key)
359362

360-
document: dict[str, Any] = self._serializer.dump_dict(entry=managed_entry)
363+
document: dict[str, Any] = self._serializer.dump_dict(entry=managed_entry, key=key, collection=collection)
361364

362365
try:
363366
_ = await self._client.index(
@@ -395,7 +398,7 @@ async def _put_managed_entries(
395398

396399
index_action: dict[str, Any] = new_bulk_action(action="index", index=index_name, document_id=document_id)
397400

398-
document: dict[str, Any] = self._serializer.dump_dict(entry=managed_entry)
401+
document: dict[str, Any] = self._serializer.dump_dict(entry=managed_entry, key=key, collection=collection)
399402

400403
operations.extend([index_action, document])
401404

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"""Python keyring-based key-value store."""
22

3+
import os
4+
5+
from key_value.shared.errors.key_value import ValueTooLargeError
36
from key_value.shared.utils.compound import compound_key
47
from key_value.shared.utils.managed_entry import ManagedEntry
58
from key_value.shared.utils.sanitization import HybridSanitizationStrategy, SanitizationStrategy
@@ -17,6 +20,16 @@
1720

1821
DEFAULT_KEYCHAIN_SERVICE = "py-key-value"
1922

23+
24+
def is_value_too_large(value: bytes) -> bool:
25+
value_length = len(value)
26+
if os.name == "nt":
27+
return value_length > WINDOWS_MAX_VALUE_LENGTH
28+
return False
29+
30+
31+
WINDOWS_MAX_VALUE_LENGTH = 2560 # bytes
32+
2033
MAX_KEY_COLLECTION_LENGTH = 256
2134
ALLOWED_KEY_COLLECTION_CHARACTERS: str = ALPHANUMERIC_CHARACTERS
2235

@@ -105,7 +118,11 @@ async def _put_managed_entry(self, *, key: str, collection: str, managed_entry:
105118

106119
combo_key: str = compound_key(collection=sanitized_collection, key=sanitized_key)
107120

108-
json_str: str = self._serialization_adapter.dump_json(entry=managed_entry)
121+
json_str: str = self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection)
122+
encoded_json_bytes: bytes = json_str.encode(encoding="utf-8")
123+
124+
if is_value_too_large(value=encoded_json_bytes):
125+
raise ValueTooLargeError(size=len(encoded_json_bytes), max_size=2560, collection=sanitized_collection, key=sanitized_key)
109126

110127
keyring.set_password(service_name=self._service_name, username=combo_key, password=json_str)
111128

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ async def _put_managed_entry(
125125
else:
126126
exptime = max(int(managed_entry.ttl), 1)
127127

128-
json_value: str = self._serialization_adapter.dump_json(entry=managed_entry)
128+
json_value: str = self._serialization_adapter.dump_json(entry=managed_entry, key=key, collection=collection)
129129

130130
_ = await self._client.set(
131131
key=combo_key.encode(encoding="utf-8"),

0 commit comments

Comments
 (0)