hishel 0.1.2__tar.gz → 0.1.4__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.
Files changed (45) hide show
  1. {hishel-0.1.2 → hishel-0.1.4}/.gitignore +1 -0
  2. {hishel-0.1.2 → hishel-0.1.4}/CHANGELOG.md +32 -1
  3. {hishel-0.1.2 → hishel-0.1.4}/PKG-INFO +50 -6
  4. {hishel-0.1.2 → hishel-0.1.4}/README.md +6 -2
  5. hishel-0.1.4/hishel/__init__.py +57 -0
  6. {hishel-0.1.2 → hishel-0.1.4}/hishel/_async/_client.py +1 -1
  7. {hishel-0.1.2 → hishel-0.1.4}/hishel/_async/_storages.py +17 -17
  8. {hishel-0.1.2 → hishel-0.1.4}/hishel/_async/_transports.py +9 -4
  9. {hishel-0.1.2 → hishel-0.1.4}/hishel/_controller.py +2 -3
  10. hishel-0.1.4/hishel/_lmdb_types_.pyi +53 -0
  11. {hishel-0.1.2 → hishel-0.1.4}/hishel/_s3.py +19 -8
  12. {hishel-0.1.2 → hishel-0.1.4}/hishel/_serializers.py +2 -2
  13. {hishel-0.1.2 → hishel-0.1.4}/hishel/_sync/_client.py +1 -1
  14. {hishel-0.1.2 → hishel-0.1.4}/hishel/_sync/_storages.py +16 -16
  15. {hishel-0.1.2 → hishel-0.1.4}/hishel/_sync/_transports.py +9 -4
  16. hishel-0.1.4/hishel/_utils.py +458 -0
  17. hishel-0.1.4/hishel/beta/__init__.py +59 -0
  18. hishel-0.1.4/hishel/beta/_async_cache.py +167 -0
  19. hishel-0.1.4/hishel/beta/_core/_async/_storages/_sqlite.py +411 -0
  20. hishel-0.1.4/hishel/beta/_core/_base/_storages/_base.py +260 -0
  21. hishel-0.1.4/hishel/beta/_core/_base/_storages/_packing.py +165 -0
  22. hishel-0.1.4/hishel/beta/_core/_headers.py +301 -0
  23. hishel-0.1.4/hishel/beta/_core/_spec.py +2291 -0
  24. hishel-0.1.4/hishel/beta/_core/_sync/_storages/_sqlite.py +411 -0
  25. hishel-0.1.4/hishel/beta/_core/models.py +176 -0
  26. hishel-0.1.4/hishel/beta/_sync_cache.py +167 -0
  27. hishel-0.1.4/hishel/beta/httpx.py +317 -0
  28. hishel-0.1.4/hishel/beta/requests.py +193 -0
  29. hishel-0.1.4/hishel/py.typed +0 -0
  30. {hishel-0.1.2 → hishel-0.1.4}/pyproject.toml +44 -45
  31. hishel-0.1.2/hishel/__init__.py +0 -17
  32. hishel-0.1.2/hishel/_utils.py +0 -118
  33. {hishel-0.1.2 → hishel-0.1.4}/LICENSE +0 -0
  34. {hishel-0.1.2 → hishel-0.1.4}/hishel/_async/__init__.py +0 -0
  35. {hishel-0.1.2 → hishel-0.1.4}/hishel/_async/_mock.py +0 -0
  36. {hishel-0.1.2 → hishel-0.1.4}/hishel/_async/_pool.py +0 -0
  37. {hishel-0.1.2 → hishel-0.1.4}/hishel/_exceptions.py +0 -0
  38. {hishel-0.1.2 → hishel-0.1.4}/hishel/_files.py +0 -0
  39. {hishel-0.1.2 → hishel-0.1.4}/hishel/_headers.py +0 -0
  40. {hishel-0.1.2 → hishel-0.1.4}/hishel/_lfu_cache.py +0 -0
  41. {hishel-0.1.2 → hishel-0.1.4}/hishel/_sync/__init__.py +0 -0
  42. {hishel-0.1.2 → hishel-0.1.4}/hishel/_sync/_mock.py +0 -0
  43. {hishel-0.1.2 → hishel-0.1.4}/hishel/_sync/_pool.py +0 -0
  44. {hishel-0.1.2 → hishel-0.1.4}/hishel/_synchronization.py +0 -0
  45. /hishel-0.1.2/hishel/py.typed → /hishel-0.1.4/hishel/beta/_core/__init__.py +0 -0
@@ -1,3 +1,4 @@
1
+ .venv/
1
2
  venv/
2
3
  __pycache__/
3
4
  .coverage
@@ -1,4 +1,35 @@
1
- # Changelog
1
+ ## [0.1.4] - 2025-10-14
2
+
3
+ ### 🚀 Features
4
+
5
+ - Add support for requests library
6
+ - Add support for Python 3.14
7
+ - Add support for a sans-IO API (#366)
8
+ - Allow already consumed streams with `CacheTransport` (#377)
9
+ - Add sqlite storage for beta storages
10
+ - Get rid of some locks from sqlite storage
11
+ - Better async implemetation for sqlite storage
12
+ - Added `Metadata` to public API. (#363)
13
+ - Fix race condition in FileStorage initialization. (#353)
14
+
15
+ ### 🐛 Bug Fixes
16
+
17
+ - Create an sqlite file in a cache folder
18
+ - Fix beta imports
19
+
20
+ ### ⚙️ Miscellaneous Tasks
21
+
22
+ - Improve CI (#369)
23
+ - *(internal)* Remove src folder (#373)
24
+ - *(internal)* Temporary remove python3.14 from CI
25
+ - *(tests)* Add sqlite tests for new storage
26
+ - *(tests)* Move some tests to beta
27
+ ## 0.1.3 (1st July, 2025)
28
+
29
+ - Remove `types-redis` from dev dependencies (#336)
30
+ - Bump redis to 6.0.0 and address async `.close()` deprecation warning (#336)
31
+ - Avoid race condition when unlinking files in `FileStorage`. (#334)
32
+ - Allow prodiving a `path_prefix` in `S3Storage` and `AsyncS3Storage`. (#342)
2
33
 
3
34
  ## 0.1.2 (5th April, 2025)
4
35
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hishel
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Persistent cache implementation for httpx and httpcore
5
5
  Project-URL: Homepage, https://hishel.com
6
6
  Project-URL: Source, https://github.com/karpetrosyan/hishel
@@ -21,18 +21,27 @@ Classifier: Programming Language :: Python :: 3.10
21
21
  Classifier: Programming Language :: Python :: 3.11
22
22
  Classifier: Programming Language :: Python :: 3.12
23
23
  Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
24
25
  Classifier: Topic :: Internet :: WWW/HTTP
25
26
  Requires-Python: >=3.9
27
+ Requires-Dist: anyio>=4.9.0
28
+ Requires-Dist: anysqlite>=0.0.5
26
29
  Requires-Dist: httpx>=0.28.0
30
+ Requires-Dist: msgpack>=1.1.2
31
+ Requires-Dist: typing-extensions>=4.14.1
32
+ Provides-Extra: httpx
33
+ Requires-Dist: httpx>=0.28.1; extra == 'httpx'
27
34
  Provides-Extra: redis
28
- Requires-Dist: redis==5.0.4; extra == 'redis'
35
+ Requires-Dist: redis==6.2.0; extra == 'redis'
36
+ Provides-Extra: requests
37
+ Requires-Dist: requests>=2.32.5; extra == 'requests'
29
38
  Provides-Extra: s3
30
39
  Requires-Dist: boto3<=1.15.3,>=1.15.0; (python_version < '3.12') and extra == 's3'
31
40
  Requires-Dist: boto3>=1.15.3; (python_version >= '3.12') and extra == 's3'
32
41
  Provides-Extra: sqlite
33
42
  Requires-Dist: anysqlite>=0.0.5; extra == 'sqlite'
34
43
  Provides-Extra: yaml
35
- Requires-Dist: pyyaml==6.0.1; extra == 'yaml'
44
+ Requires-Dist: pyyaml==6.0.2; extra == 'yaml'
36
45
  Description-Content-Type: text/markdown
37
46
 
38
47
  <p align="center" class="logo">
@@ -59,8 +68,8 @@ Description-Content-Type: text/markdown
59
68
  <img src="https://img.shields.io/pypi/l/hishel" alt="license">
60
69
  </a>
61
70
 
62
- <a href="https://img.shields.io/codecov/c/github/karpetrosyan/hishel">
63
- <img src="https://img.shields.io/codecov/c/github/karpetrosyan/hishel" alt="license">
71
+ <a href="https://coveralls.io/github/karpetrosyan/hishel">
72
+ <img src="https://img.shields.io/coverallsCoverage/github/karpetrosyan/hishel" alt="license">
64
73
  </a>
65
74
 
66
75
  <a href="https://github.com/karpetrosyan/hishel">
@@ -68,6 +77,10 @@ Description-Content-Type: text/markdown
68
77
  </a>
69
78
  </p>
70
79
 
80
+ <p align="center">
81
+ <a href="https://buymeacoffee.com/karpetrosyan" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
82
+ </p>
83
+
71
84
  -----
72
85
 
73
86
  **Hishel (հիշել, remember)** is a library that implements HTTP Caching for [HTTPX](https://github.com/encode/httpx) and [HTTP Core](https://github.com/encode/httpcore) libraries in accordance with [**RFC 9111**](https://www.rfc-editor.org/rfc/rfc9111.html), the most recent caching specification.
@@ -179,7 +192,38 @@ You have complete control over them; you can change storage or even write your o
179
192
  You can support the project by simply leaving a GitHub star ⭐ or by [contributing](https://hishel.com/contributing/).
180
193
  Help us grow and continue developing good software for you ❤️
181
194
 
182
- # Changelog
195
+ ## [0.1.4] - 2025-10-14
196
+
197
+ ### 🚀 Features
198
+
199
+ - Add support for requests library
200
+ - Add support for Python 3.14
201
+ - Add support for a sans-IO API (#366)
202
+ - Allow already consumed streams with `CacheTransport` (#377)
203
+ - Add sqlite storage for beta storages
204
+ - Get rid of some locks from sqlite storage
205
+ - Better async implemetation for sqlite storage
206
+ - Added `Metadata` to public API. (#363)
207
+ - Fix race condition in FileStorage initialization. (#353)
208
+
209
+ ### 🐛 Bug Fixes
210
+
211
+ - Create an sqlite file in a cache folder
212
+ - Fix beta imports
213
+
214
+ ### ⚙️ Miscellaneous Tasks
215
+
216
+ - Improve CI (#369)
217
+ - *(internal)* Remove src folder (#373)
218
+ - *(internal)* Temporary remove python3.14 from CI
219
+ - *(tests)* Add sqlite tests for new storage
220
+ - *(tests)* Move some tests to beta
221
+ ## 0.1.3 (1st July, 2025)
222
+
223
+ - Remove `types-redis` from dev dependencies (#336)
224
+ - Bump redis to 6.0.0 and address async `.close()` deprecation warning (#336)
225
+ - Avoid race condition when unlinking files in `FileStorage`. (#334)
226
+ - Allow prodiving a `path_prefix` in `S3Storage` and `AsyncS3Storage`. (#342)
183
227
 
184
228
  ## 0.1.2 (5th April, 2025)
185
229
 
@@ -22,8 +22,8 @@
22
22
  <img src="https://img.shields.io/pypi/l/hishel" alt="license">
23
23
  </a>
24
24
 
25
- <a href="https://img.shields.io/codecov/c/github/karpetrosyan/hishel">
26
- <img src="https://img.shields.io/codecov/c/github/karpetrosyan/hishel" alt="license">
25
+ <a href="https://coveralls.io/github/karpetrosyan/hishel">
26
+ <img src="https://img.shields.io/coverallsCoverage/github/karpetrosyan/hishel" alt="license">
27
27
  </a>
28
28
 
29
29
  <a href="https://github.com/karpetrosyan/hishel">
@@ -31,6 +31,10 @@
31
31
  </a>
32
32
  </p>
33
33
 
34
+ <p align="center">
35
+ <a href="https://buymeacoffee.com/karpetrosyan" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
36
+ </p>
37
+
34
38
  -----
35
39
 
36
40
  **Hishel (հիշել, remember)** is a library that implements HTTP Caching for [HTTPX](https://github.com/encode/httpx) and [HTTP Core](https://github.com/encode/httpcore) libraries in accordance with [**RFC 9111**](https://www.rfc-editor.org/rfc/rfc9111.html), the most recent caching specification.
@@ -0,0 +1,57 @@
1
+ import httpx
2
+
3
+ from ._async import *
4
+ from ._controller import *
5
+ from ._exceptions import *
6
+ from ._headers import *
7
+ from ._serializers import *
8
+ from ._sync import *
9
+ from ._lfu_cache import *
10
+
11
+ __all__ = (
12
+ # Old API
13
+ "AsyncCacheClient",
14
+ "MockAsyncConnectionPool",
15
+ "MockAsyncTransport",
16
+ "AsyncCacheConnectionPool",
17
+ "AsyncBaseStorage",
18
+ "AsyncFileStorage",
19
+ "AsyncInMemoryStorage",
20
+ "AsyncRedisStorage",
21
+ "AsyncS3Storage",
22
+ "AsyncSQLiteStorage",
23
+ "AsyncCacheTransport",
24
+ "HEURISTICALLY_CACHEABLE_STATUS_CODES",
25
+ "Controller",
26
+ "CacheControlError",
27
+ "ParseError",
28
+ "ValidationError",
29
+ "CacheControl",
30
+ "Vary",
31
+ "BaseSerializer",
32
+ "JSONSerializer",
33
+ "Metadata",
34
+ "PickleSerializer",
35
+ "YAMLSerializer",
36
+ "clone_model",
37
+ "CacheClient",
38
+ "MockConnectionPool",
39
+ "MockTransport",
40
+ "CacheConnectionPool",
41
+ "BaseStorage",
42
+ "FileStorage",
43
+ "InMemoryStorage",
44
+ "RedisStorage",
45
+ "S3Storage",
46
+ "SQLiteStorage",
47
+ "CacheTransport",
48
+ "LFUCache",
49
+ )
50
+
51
+ def install_cache() -> None: # pragma: no cover
52
+ httpx.AsyncClient = AsyncCacheClient # type: ignore
53
+ httpx.Client = CacheClient # type: ignore
54
+
55
+
56
+ __version__ = "0.1.4"
57
+
@@ -2,7 +2,7 @@ import typing as tp
2
2
 
3
3
  import httpx
4
4
 
5
- from hishel._async._transports import AsyncCacheTransport
5
+ from ._transports import AsyncCacheTransport
6
6
 
7
7
  __all__ = ("AsyncCacheClient",)
8
8
 
@@ -4,7 +4,6 @@ import datetime
4
4
  import logging
5
5
  import os
6
6
  import time
7
- import typing as t
8
7
  import typing as tp
9
8
  import warnings
10
9
  from copy import deepcopy
@@ -24,13 +23,11 @@ except ImportError: # pragma: no cover
24
23
 
25
24
  from httpcore import Request, Response
26
25
 
27
- if t.TYPE_CHECKING: # pragma: no cover
26
+ if tp.TYPE_CHECKING: # pragma: no cover
28
27
  from typing_extensions import TypeAlias
29
28
 
30
- from hishel._serializers import BaseSerializer, clone_model
31
-
32
29
  from .._files import AsyncFileManager
33
- from .._serializers import JSONSerializer, Metadata
30
+ from .._serializers import BaseSerializer, JSONSerializer, Metadata, clone_model
34
31
  from .._synchronization import AsyncLock
35
32
  from .._utils import float_seconds_to_int_milliseconds
36
33
 
@@ -39,10 +36,10 @@ logger = logging.getLogger("hishel.storages")
39
36
  __all__ = (
40
37
  "AsyncBaseStorage",
41
38
  "AsyncFileStorage",
42
- "AsyncRedisStorage",
43
- "AsyncSQLiteStorage",
44
39
  "AsyncInMemoryStorage",
40
+ "AsyncRedisStorage",
45
41
  "AsyncS3Storage",
42
+ "AsyncSQLiteStorage",
46
43
  )
47
44
 
48
45
  StoredResponse: TypeAlias = tp.Tuple[Response, Request, Metadata]
@@ -106,8 +103,7 @@ class AsyncFileStorage(AsyncBaseStorage):
106
103
  self._base_path = Path(base_path) if base_path is not None else Path(".cache/hishel")
107
104
  self._gitignore_file = self._base_path / ".gitignore"
108
105
 
109
- if not self._base_path.is_dir():
110
- self._base_path.mkdir(parents=True)
106
+ self._base_path.mkdir(parents=True, exist_ok=True)
111
107
 
112
108
  if not self._gitignore_file.is_file():
113
109
  with open(self._gitignore_file, "w", encoding="utf-8") as f:
@@ -153,13 +149,13 @@ class AsyncFileStorage(AsyncBaseStorage):
153
149
  """
154
150
 
155
151
  if isinstance(key, Response): # pragma: no cover
156
- key = t.cast(str, key.extensions["cache_metadata"]["cache_key"])
152
+ key = tp.cast(str, key.extensions["cache_metadata"]["cache_key"])
157
153
 
158
154
  response_path = self._base_path / key
159
155
 
160
156
  async with self._lock:
161
157
  if response_path.exists():
162
- response_path.unlink()
158
+ response_path.unlink(missing_ok=True)
163
159
 
164
160
  async def update_metadata(self, key: str, response: Response, request: Request, metadata: Metadata) -> None:
165
161
  """
@@ -222,7 +218,7 @@ class AsyncFileStorage(AsyncBaseStorage):
222
218
  if response_path.is_file():
223
219
  age = time.time() - response_path.stat().st_mtime
224
220
  if age > self._ttl:
225
- response_path.unlink()
221
+ response_path.unlink(missing_ok=True)
226
222
  return
227
223
 
228
224
  self._last_cleaned = time.monotonic()
@@ -322,7 +318,7 @@ class AsyncSQLiteStorage(AsyncBaseStorage):
322
318
  assert self._connection
323
319
 
324
320
  if isinstance(key, Response): # pragma: no cover
325
- key = t.cast(str, key.extensions["cache_metadata"]["cache_key"])
321
+ key = tp.cast(str, key.extensions["cache_metadata"]["cache_key"])
326
322
 
327
323
  async with self._lock:
328
324
  await self._connection.execute("DELETE FROM cache WHERE key = ?", [key])
@@ -459,7 +455,7 @@ class AsyncRedisStorage(AsyncBaseStorage):
459
455
  """
460
456
 
461
457
  if isinstance(key, Response): # pragma: no cover
462
- key = t.cast(str, key.extensions["cache_metadata"]["cache_key"])
458
+ key = tp.cast(str, key.extensions["cache_metadata"]["cache_key"])
463
459
 
464
460
  await self._client.delete(key)
465
461
 
@@ -507,7 +503,7 @@ class AsyncRedisStorage(AsyncBaseStorage):
507
503
  return self._serializer.loads(cached_response)
508
504
 
509
505
  async def aclose(self) -> None: # pragma: no cover
510
- await self._client.close()
506
+ await self._client.aclose()
511
507
 
512
508
 
513
509
  class AsyncInMemoryStorage(AsyncBaseStorage):
@@ -572,7 +568,7 @@ class AsyncInMemoryStorage(AsyncBaseStorage):
572
568
  """
573
569
 
574
570
  if isinstance(key, Response): # pragma: no cover
575
- key = t.cast(str, key.extensions["cache_metadata"]["cache_key"])
571
+ key = tp.cast(str, key.extensions["cache_metadata"]["cache_key"])
576
572
 
577
573
  async with self._lock:
578
574
  self._cache.remove_key(key)
@@ -654,6 +650,8 @@ class AsyncS3Storage(AsyncBaseStorage): # pragma: no cover
654
650
  :type check_ttl_every: tp.Union[int, float]
655
651
  :param client: A client for S3, defaults to None
656
652
  :type client: tp.Optional[tp.Any], optional
653
+ :param path_prefix: A path prefix to use for S3 object keys, defaults to "hishel-"
654
+ :type path_prefix: str, optional
657
655
  """
658
656
 
659
657
  def __init__(
@@ -663,6 +661,7 @@ class AsyncS3Storage(AsyncBaseStorage): # pragma: no cover
663
661
  ttl: tp.Optional[tp.Union[int, float]] = None,
664
662
  check_ttl_every: tp.Union[int, float] = 60,
665
663
  client: tp.Optional[tp.Any] = None,
664
+ path_prefix: str = "hishel-",
666
665
  ) -> None:
667
666
  super().__init__(serializer, ttl)
668
667
 
@@ -680,6 +679,7 @@ class AsyncS3Storage(AsyncBaseStorage): # pragma: no cover
680
679
  bucket_name=bucket_name,
681
680
  is_binary=self._serializer.is_binary,
682
681
  check_ttl_every=check_ttl_every,
682
+ path_prefix=path_prefix,
683
683
  )
684
684
  self._lock = AsyncLock()
685
685
 
@@ -716,7 +716,7 @@ class AsyncS3Storage(AsyncBaseStorage): # pragma: no cover
716
716
  """
717
717
 
718
718
  if isinstance(key, Response): # pragma: no cover
719
- key = t.cast(str, key.extensions["cache_metadata"]["cache_key"])
719
+ key = tp.cast(str, key.extensions["cache_metadata"]["cache_key"])
720
720
 
721
721
  async with self._lock:
722
722
  await self._s3_manager.remove_entry(key)
@@ -8,11 +8,10 @@ import httpx
8
8
  from httpx import AsyncByteStream, Request, Response
9
9
  from httpx._exceptions import ConnectError
10
10
 
11
- from hishel._utils import extract_header_values_decoded, normalized_url
12
-
13
11
  from .._controller import Controller, allowed_stale
14
12
  from .._headers import parse_cache_control
15
13
  from .._serializers import JSONSerializer, Metadata
14
+ from .._utils import extract_header_values_decoded, normalized_url
16
15
  from ._storages import AsyncBaseStorage, AsyncFileStorage
17
16
 
18
17
  if tp.TYPE_CHECKING: # pragma: no cover
@@ -211,11 +210,17 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
211
210
  )
212
211
 
213
212
  regular_response = await self._transport.handle_async_request(request)
214
- assert isinstance(regular_response.stream, tp.AsyncIterable)
213
+ try:
214
+ # Prefer already-read content, if available
215
+ stream = fake_stream(regular_response.content)
216
+ except httpx.ResponseNotRead:
217
+ # Fall back to stream if not yet read
218
+ assert isinstance(regular_response.stream, tp.AsyncIterable)
219
+ stream = regular_response.stream
215
220
  httpcore_regular_response = httpcore.Response(
216
221
  status=regular_response.status_code,
217
222
  headers=regular_response.headers.raw,
218
- content=AsyncCacheStream(regular_response.stream),
223
+ content=AsyncCacheStream(stream),
219
224
  extensions=regular_response.extensions,
220
225
  )
221
226
  await httpcore_regular_response.aread()
@@ -3,8 +3,7 @@ import typing as tp
3
3
 
4
4
  from httpcore import Request, Response
5
5
 
6
- from hishel._headers import Vary, parse_cache_control
7
-
6
+ from ._headers import Vary, parse_cache_control
8
7
  from ._utils import (
9
8
  BaseClock,
10
9
  Clock,
@@ -21,7 +20,7 @@ logger = logging.getLogger("hishel.controller")
21
20
  HEURISTICALLY_CACHEABLE_STATUS_CODES = (200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 501)
22
21
  HTTP_METHODS = ["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"]
23
22
 
24
- __all__ = ("Controller", "HEURISTICALLY_CACHEABLE_STATUS_CODES")
23
+ __all__ = ("HEURISTICALLY_CACHEABLE_STATUS_CODES", "Controller")
25
24
 
26
25
 
27
26
  def get_updated_headers(
@@ -0,0 +1,53 @@
1
+ from contextlib import contextmanager
2
+ from typing import Any, Iterator
3
+
4
+ class Database: ...
5
+
6
+ class Transaction:
7
+ def get(self, key: bytes, *, db: Database | None = None) -> bytes | None:
8
+ """
9
+ Get the value associated with the given key.
10
+ """
11
+ pass
12
+
13
+ def put(self, key: bytes, value: bytes, *, db: Database | None = None, dupdata: bool = False) -> None:
14
+ """
15
+ Put a key-value pair into the database.
16
+ """
17
+ pass
18
+
19
+ def delete(self, key: bytes, *, db: Database | None = None) -> bool:
20
+ """
21
+ Delete the key from the database.
22
+ """
23
+ pass
24
+
25
+ def cursor(self, db: Database) -> Any:
26
+ """
27
+ Create a cursor for iterating over key-value pairs in the database.
28
+ """
29
+ pass
30
+
31
+ class Environment:
32
+ @contextmanager
33
+ def begin(self, *, db: Database | None = None, write: bool = False) -> Iterator[Transaction]:
34
+ """
35
+ Begin a transaction in the environment.
36
+ """
37
+ raise NotImplementedError("It's only for type hinting purposes")
38
+
39
+ def open_db(self, key: bytes, dupsort: bool = False) -> Database:
40
+ """
41
+ Open a database within the environment.
42
+ """
43
+ raise NotImplementedError("It's only for type hinting purposes")
44
+
45
+ def open(
46
+ path: str,
47
+ *,
48
+ max_dbs: int = 0,
49
+ ) -> Environment:
50
+ """
51
+ Open an LMDB environment at the specified path.
52
+ """
53
+ raise NotImplementedError("It's only for type hinting purposes")
@@ -11,16 +11,22 @@ def get_timestamp_in_ms() -> float:
11
11
 
12
12
  class S3Manager:
13
13
  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-",
15
20
  ):
16
21
  self._client = client
17
22
  self._bucket_name = bucket_name
18
23
  self._is_binary = is_binary
19
24
  self._last_cleaned = time.monotonic()
20
25
  self._check_ttl_every = check_ttl_every
26
+ self._path_prefix = path_prefix
21
27
 
22
28
  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
24
30
  if isinstance(data, str):
25
31
  data = data.encode("utf-8")
26
32
 
@@ -43,7 +49,7 @@ class S3Manager:
43
49
  )
44
50
 
45
51
  def read_from(self, path: str) -> tp.Union[bytes, str]:
46
- path = "hishel-" + path
52
+ path = self._path_prefix + path
47
53
  response = self._client.get_object(
48
54
  Bucket=self._bucket_name,
49
55
  Key=path,
@@ -57,7 +63,7 @@ class S3Manager:
57
63
  return tp.cast(str, content.decode("utf-8"))
58
64
 
59
65
  def remove_expired(self, ttl: int, key: str) -> None:
60
- path = "hishel-" + key
66
+ path = self._path_prefix + key
61
67
 
62
68
  if time.monotonic() - self._last_cleaned < self._check_ttl_every:
63
69
  try:
@@ -72,7 +78,7 @@ class S3Manager:
72
78
 
73
79
  self._last_cleaned = time.monotonic()
74
80
  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
76
82
  continue
77
83
 
78
84
  try:
@@ -88,15 +94,20 @@ class S3Manager:
88
94
  self._client.delete_object(Bucket=self._bucket_name, Key=obj["Key"])
89
95
 
90
96
  def remove_entry(self, key: str) -> None:
91
- path = "hishel-" + key
97
+ path = self._path_prefix + key
92
98
  self._client.delete_object(Bucket=self._bucket_name, Key=path)
93
99
 
94
100
 
95
101
  class AsyncS3Manager: # pragma: no cover
96
102
  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-",
98
109
  ):
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)
100
111
 
101
112
  async def write_to(self, path: str, data: tp.Union[bytes, str], only_metadata: bool = False) -> None:
102
113
  return await to_thread.run_sync(self._sync_manager.write_to, path, data, only_metadata)
@@ -6,7 +6,7 @@ from datetime import datetime
6
6
 
7
7
  from httpcore import Request, Response
8
8
 
9
- from hishel._utils import normalized_url
9
+ from ._utils import normalized_url
10
10
 
11
11
  try:
12
12
  import yaml
@@ -17,7 +17,7 @@ HEADERS_ENCODING = "iso-8859-1"
17
17
  KNOWN_RESPONSE_EXTENSIONS = ("http_version", "reason_phrase")
18
18
  KNOWN_REQUEST_EXTENSIONS = ("timeout", "sni_hostname")
19
19
 
20
- __all__ = ("PickleSerializer", "JSONSerializer", "YAMLSerializer", "BaseSerializer", "clone_model")
20
+ __all__ = ("BaseSerializer", "JSONSerializer", "Metadata", "PickleSerializer", "YAMLSerializer", "clone_model")
21
21
 
22
22
  T = tp.TypeVar("T", Request, Response)
23
23
 
@@ -2,7 +2,7 @@ import typing as tp
2
2
 
3
3
  import httpx
4
4
 
5
- from hishel._sync._transports import CacheTransport
5
+ from ._transports import CacheTransport
6
6
 
7
7
  __all__ = ("CacheClient",)
8
8