Skip to content

Commit 3a5e4e5

Browse files
zygizygi-anthropic
andauthored
feat: support providing a path prefix to S3 storage (#342)
* feat: support providing path prefix to S3 storage * Add CHANGELOG.md entry --------- Co-authored-by: Zygimantas Straznickas <zygi@anthropic.com>
1 parent 32ef45c commit 3a5e4e5

File tree

4 files changed

+28
-8
lines changed

4 files changed

+28
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Remove `types-redis` from dev dependencies (#336)
66
- Bump redis to 6.0.0 and address async `.close()` deprecation warning (#336)
77
- Avoid race condition when unlinking files in `FileStorage`. (#334)
8+
- Allow prodiving a `path_prefix` in `S3Storage` and `AsyncS3Storage`. (#342)
89

910
## 0.1.2 (5th April, 2025)
1011

hishel/_async/_storages.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,8 @@ class AsyncS3Storage(AsyncBaseStorage): # pragma: no cover
654654
:type check_ttl_every: tp.Union[int, float]
655655
:param client: A client for S3, defaults to None
656656
:type client: tp.Optional[tp.Any], optional
657+
:param path_prefix: A path prefix to use for S3 object keys, defaults to "hishel-"
658+
:type path_prefix: str, optional
657659
"""
658660

659661
def __init__(
@@ -663,6 +665,7 @@ def __init__(
663665
ttl: tp.Optional[tp.Union[int, float]] = None,
664666
check_ttl_every: tp.Union[int, float] = 60,
665667
client: tp.Optional[tp.Any] = None,
668+
path_prefix: str = "hishel-",
666669
) -> None:
667670
super().__init__(serializer, ttl)
668671

@@ -680,6 +683,7 @@ def __init__(
680683
bucket_name=bucket_name,
681684
is_binary=self._serializer.is_binary,
682685
check_ttl_every=check_ttl_every,
686+
path_prefix=path_prefix,
683687
)
684688
self._lock = AsyncLock()
685689

hishel/_s3.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,22 @@ def get_timestamp_in_ms() -> float:
1111

1212
class S3Manager:
1313
def __init__(
14-
self, client: tp.Any, bucket_name: str, check_ttl_every: tp.Union[int, float], is_binary: bool = False
14+
self,
15+
client: tp.Any,
16+
bucket_name: str,
17+
check_ttl_every: tp.Union[int, float],
18+
is_binary: bool = False,
19+
path_prefix: str = "hishel-",
1520
):
1621
self._client = client
1722
self._bucket_name = bucket_name
1823
self._is_binary = is_binary
1924
self._last_cleaned = time.monotonic()
2025
self._check_ttl_every = check_ttl_every
26+
self._path_prefix = path_prefix
2127

2228
def write_to(self, path: str, data: tp.Union[bytes, str], only_metadata: bool = False) -> None:
23-
path = "hishel-" + path
29+
path = self._path_prefix + path
2430
if isinstance(data, str):
2531
data = data.encode("utf-8")
2632

@@ -43,7 +49,7 @@ def write_to(self, path: str, data: tp.Union[bytes, str], only_metadata: bool =
4349
)
4450

4551
def read_from(self, path: str) -> tp.Union[bytes, str]:
46-
path = "hishel-" + path
52+
path = self._path_prefix + path
4753
response = self._client.get_object(
4854
Bucket=self._bucket_name,
4955
Key=path,
@@ -57,7 +63,7 @@ def read_from(self, path: str) -> tp.Union[bytes, str]:
5763
return tp.cast(str, content.decode("utf-8"))
5864

5965
def remove_expired(self, ttl: int, key: str) -> None:
60-
path = "hishel-" + key
66+
path = self._path_prefix + key
6167

6268
if time.monotonic() - self._last_cleaned < self._check_ttl_every:
6369
try:
@@ -72,7 +78,7 @@ def remove_expired(self, ttl: int, key: str) -> None:
7278

7379
self._last_cleaned = time.monotonic()
7480
for obj in self._client.list_objects(Bucket=self._bucket_name).get("Contents", []):
75-
if not obj["Key"].startswith("hishel-"): # pragma: no cover
81+
if not obj["Key"].startswith(self._path_prefix): # pragma: no cover
7682
continue
7783

7884
try:
@@ -88,15 +94,20 @@ def remove_expired(self, ttl: int, key: str) -> None:
8894
self._client.delete_object(Bucket=self._bucket_name, Key=obj["Key"])
8995

9096
def remove_entry(self, key: str) -> None:
91-
path = "hishel-" + key
97+
path = self._path_prefix + key
9298
self._client.delete_object(Bucket=self._bucket_name, Key=path)
9399

94100

95101
class AsyncS3Manager: # pragma: no cover
96102
def __init__(
97-
self, client: tp.Any, bucket_name: str, check_ttl_every: tp.Union[int, float], is_binary: bool = False
103+
self,
104+
client: tp.Any,
105+
bucket_name: str,
106+
check_ttl_every: tp.Union[int, float],
107+
is_binary: bool = False,
108+
path_prefix: str = "hishel-",
98109
):
99-
self._sync_manager = S3Manager(client, bucket_name, check_ttl_every, is_binary)
110+
self._sync_manager = S3Manager(client, bucket_name, check_ttl_every, is_binary, path_prefix)
100111

101112
async def write_to(self, path: str, data: tp.Union[bytes, str], only_metadata: bool = False) -> None:
102113
return await to_thread.run_sync(self._sync_manager.write_to, path, data, only_metadata)

hishel/_sync/_storages.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,8 @@ class S3Storage(BaseStorage): # pragma: no cover
654654
:type check_ttl_every: tp.Union[int, float]
655655
:param client: A client for S3, defaults to None
656656
:type client: tp.Optional[tp.Any], optional
657+
:param path_prefix: A path prefix to use for S3 object keys, defaults to "hishel-"
658+
:type path_prefix: str, optional
657659
"""
658660

659661
def __init__(
@@ -663,6 +665,7 @@ def __init__(
663665
ttl: tp.Optional[tp.Union[int, float]] = None,
664666
check_ttl_every: tp.Union[int, float] = 60,
665667
client: tp.Optional[tp.Any] = None,
668+
path_prefix: str = "hishel-",
666669
) -> None:
667670
super().__init__(serializer, ttl)
668671

@@ -680,6 +683,7 @@ def __init__(
680683
bucket_name=bucket_name,
681684
is_binary=self._serializer.is_binary,
682685
check_ttl_every=check_ttl_every,
686+
path_prefix=path_prefix,
683687
)
684688
self._lock = Lock()
685689

0 commit comments

Comments
 (0)