hishel 0.1.3__tar.gz → 0.1.5__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 (46) hide show
  1. {hishel-0.1.3 → hishel-0.1.5}/.gitignore +3 -1
  2. hishel-0.1.5/CHANGELOG.md +64 -0
  3. {hishel-0.1.3 → hishel-0.1.5}/PKG-INFO +58 -167
  4. {hishel-0.1.3 → hishel-0.1.5}/README.md +6 -2
  5. hishel-0.1.5/hishel/__init__.py +57 -0
  6. {hishel-0.1.3 → hishel-0.1.5}/hishel/_async/_client.py +1 -1
  7. {hishel-0.1.3 → hishel-0.1.5}/hishel/_async/_storages.py +10 -14
  8. {hishel-0.1.3 → hishel-0.1.5}/hishel/_async/_transports.py +9 -4
  9. {hishel-0.1.3 → hishel-0.1.5}/hishel/_controller.py +2 -3
  10. hishel-0.1.5/hishel/_lmdb_types_.pyi +53 -0
  11. {hishel-0.1.3 → hishel-0.1.5}/hishel/_serializers.py +2 -2
  12. {hishel-0.1.3 → hishel-0.1.5}/hishel/_sync/_client.py +1 -1
  13. {hishel-0.1.3 → hishel-0.1.5}/hishel/_sync/_storages.py +10 -14
  14. {hishel-0.1.3 → hishel-0.1.5}/hishel/_sync/_transports.py +9 -4
  15. hishel-0.1.5/hishel/_utils.py +458 -0
  16. hishel-0.1.5/hishel/beta/__init__.py +59 -0
  17. hishel-0.1.5/hishel/beta/_async_cache.py +167 -0
  18. hishel-0.1.5/hishel/beta/_core/_async/_storages/_sqlite.py +411 -0
  19. hishel-0.1.5/hishel/beta/_core/_base/_storages/_base.py +272 -0
  20. hishel-0.1.5/hishel/beta/_core/_base/_storages/_packing.py +165 -0
  21. hishel-0.1.5/hishel/beta/_core/_headers.py +636 -0
  22. hishel-0.1.5/hishel/beta/_core/_spec.py +2291 -0
  23. hishel-0.1.5/hishel/beta/_core/_sync/_storages/_sqlite.py +411 -0
  24. hishel-0.1.5/hishel/beta/_core/models.py +176 -0
  25. hishel-0.1.5/hishel/beta/_sync_cache.py +167 -0
  26. hishel-0.1.5/hishel/beta/httpx.py +328 -0
  27. hishel-0.1.5/hishel/beta/requests.py +198 -0
  28. hishel-0.1.5/hishel/py.typed +0 -0
  29. {hishel-0.1.3 → hishel-0.1.5}/pyproject.toml +44 -44
  30. hishel-0.1.3/CHANGELOG.md +0 -186
  31. hishel-0.1.3/hishel/__init__.py +0 -17
  32. hishel-0.1.3/hishel/_utils.py +0 -118
  33. {hishel-0.1.3 → hishel-0.1.5}/LICENSE +0 -0
  34. {hishel-0.1.3 → hishel-0.1.5}/hishel/_async/__init__.py +0 -0
  35. {hishel-0.1.3 → hishel-0.1.5}/hishel/_async/_mock.py +0 -0
  36. {hishel-0.1.3 → hishel-0.1.5}/hishel/_async/_pool.py +0 -0
  37. {hishel-0.1.3 → hishel-0.1.5}/hishel/_exceptions.py +0 -0
  38. {hishel-0.1.3 → hishel-0.1.5}/hishel/_files.py +0 -0
  39. {hishel-0.1.3 → hishel-0.1.5}/hishel/_headers.py +0 -0
  40. {hishel-0.1.3 → hishel-0.1.5}/hishel/_lfu_cache.py +0 -0
  41. {hishel-0.1.3 → hishel-0.1.5}/hishel/_s3.py +0 -0
  42. {hishel-0.1.3 → hishel-0.1.5}/hishel/_sync/__init__.py +0 -0
  43. {hishel-0.1.3 → hishel-0.1.5}/hishel/_sync/_mock.py +0 -0
  44. {hishel-0.1.3 → hishel-0.1.5}/hishel/_sync/_pool.py +0 -0
  45. {hishel-0.1.3 → hishel-0.1.5}/hishel/_synchronization.py +0 -0
  46. /hishel-0.1.3/hishel/py.typed → /hishel-0.1.5/hishel/beta/_core/__init__.py +0 -0
@@ -1,5 +1,7 @@
1
+ .venv/
1
2
  venv/
2
3
  __pycache__/
3
4
  .coverage
4
5
  .cache/
5
- .idea/
6
+ .idea/
7
+ coverage.xml
@@ -0,0 +1,64 @@
1
+ ## [0.1.5] - 2025-10-18
2
+
3
+ ### 🚀 Features
4
+
5
+ - *(perf)* Set chunk size to 128KB for httpx to reduce SQLite read/writes
6
+ - Better cache-control parsing
7
+ - Add close method to storages API (#384)
8
+ - *(perf)* Increase requests buffer size to 128KB, disable charset detection
9
+
10
+ ### 🐛 Bug Fixes
11
+
12
+ - *(docs)* Fix some line breaks
13
+
14
+ ### ⚙️ Miscellaneous Tasks
15
+
16
+ - Remove some redundant files from repo
17
+ ## [0.1.4] - 2025-10-14
18
+
19
+ ### 🚀 Features
20
+
21
+ - Add support for a sans-IO API (#366)
22
+ - Allow already consumed streams with `CacheTransport` (#377)
23
+ - Add sqlite storage for beta storages
24
+ - Get rid of some locks from sqlite storage
25
+ - Better async implemetation for sqlite storage
26
+
27
+ ### 🐛 Bug Fixes
28
+
29
+ - Create an sqlite file in a cache folder
30
+ - Fix beta imports
31
+
32
+ ### ⚙️ Miscellaneous Tasks
33
+
34
+ - Improve CI (#369)
35
+ - *(internal)* Remove src folder (#373)
36
+ - *(internal)* Temporary remove python3.14 from CI
37
+ - *(tests)* Add sqlite tests for new storage
38
+ - *(tests)* Move some tests to beta
39
+ ## [0.1.3] - 2025-07-06
40
+
41
+ ### 🚀 Features
42
+
43
+ - Support providing a path prefix to S3 storage (#342)
44
+
45
+ ### 📚 Documentation
46
+
47
+ - Update link to httpx transports page (#337)
48
+ ## [0.1.2] - 2025-04-04
49
+
50
+ ### 🐛 Bug Fixes
51
+
52
+ - Requirements.txt to reduce vulnerabilities (#263)
53
+ ## [0.0.30] - 2024-07-12
54
+
55
+ ### 🐛 Bug Fixes
56
+
57
+ - Requirements.txt to reduce vulnerabilities (#245)
58
+ - Requirements.txt to reduce vulnerabilities (#255)
59
+ ## [0.0.27] - 2024-05-31
60
+
61
+ ### 🐛 Bug Fixes
62
+
63
+ - *(redis)* Do not update metadata with negative ttl (#231)
64
+ ## [0.0.1] - 2023-07-22
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hishel
3
- Version: 0.1.3
3
+ Version: 0.1.5
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,11 +21,20 @@ 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
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'
@@ -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,189 +192,67 @@ 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
183
-
184
- ## 0.1.3 (1st July, 2025)
185
-
186
- - Remove `types-redis` from dev dependencies (#336)
187
- - Bump redis to 6.0.0 and address async `.close()` deprecation warning (#336)
188
- - Avoid race condition when unlinking files in `FileStorage`. (#334)
189
- - Allow prodiving a `path_prefix` in `S3Storage` and `AsyncS3Storage`. (#342)
190
-
191
- ## 0.1.2 (5th April, 2025)
192
-
193
- - Add check for fips compliant python. (#325)
194
- - Fix compatibility with httpx. (#291)
195
- - Use `SyncByteStream` instead of `ByteStream`. (#298)
196
- - Don't raise exceptions if date-containing headers are invalid. (#318)
197
- - Fix for S3 Storage missing metadata in API request. (#320)
198
-
199
- ## 0.1.1 (2nd Nov, 2024)
200
-
201
- - Fix typing extensions not found. (#290)
202
-
203
- ## 0.1.0 (2nd Nov, 2024)
204
-
205
- - Add support for Python 3.12 / drop Python 3.8. (#286)
206
- - Specify usedforsecurity=False in blake2b. (#285)
207
-
208
- ## 0.0.33 (4th Oct, 2024)
209
-
210
- - Added a [Logging](https://hishel.com/advanced/logging/) section to the documentation.
211
-
212
- ## 0.0.32 (27th Sep, 2024)
213
-
214
- - Don't raise an exception if the `Date` header is not present. (#273)
215
-
216
- ## 0.0.31 (22nd Sep, 2024)
217
-
218
- - Ignore file not found error when cleaning up a file storage. (#264)
219
- - Fix `AssertionError` on `client.close()` when use SQLiteStorage. (#269)
220
- - Fix ignored flags when use `force_cache`. (#271)
221
-
222
- ## 0.0.30 (12th July, 2024)
223
-
224
- - Fix cache update on revalidation response with content (rfc9111 section 4.3.3) (#239)
225
- - Fix request extensions that were not passed into revalidation request for transport-based implementation (but were
226
- passed for the pool-based impl) (#247).
227
- - Add `cache_private` property to the controller to support acting as shared cache. (#224)
228
- - Improve efficiency of scanning cached responses in `FileStorage` by reducing number of syscalls. (#252)
229
- - Add `remove` support for storages (#241)
230
-
231
- ## 0.0.29 (23th June, 2024)
232
-
233
- - Documentation hotfix. (#244)
234
-
235
- ## 0.0.28 (23th June, 2024)
236
-
237
- - Add `revalidated` response extension. (#242)
238
-
239
- ## 0.0.27 (31th May, 2024)
240
-
241
- - Fix `RedisStorage` when using without ttl. (#231)
242
-
243
- ## 0.0.26 (12th April, 2024)
244
-
245
- - Expose `AsyncBaseStorage` and `BaseStorage`. (#220)
246
- - Prevent cache hits from resetting the ttl. (#215)
247
-
248
- ## 0.0.25 (26th March, 2024)
249
-
250
- - Add `force_cache` property to the controller, allowing RFC9111 rules to be completely disabled. (#204)
251
- - Add `.gitignore` to cache directory created by `FIleStorage`. (#197)
252
- - Remove `stale_*` headers from the `CacheControl` class. (#199)
253
-
254
- ## 0.0.24 (14th February, 2024)
255
-
256
- - Fix `botocore is not installed` exception when using any kind of storage. (#186)
257
-
258
- ## 0.0.23 (14th February, 2024)
259
-
260
- - Make `S3Storage` to check staleness of all cache files with set interval. (#182)
261
- - Fix an issue where an empty file in `FileCache` could cause a parsing error. (#181)
262
- - Support caching for `POST` and other HTTP methods. (#183)
263
-
264
- ## 0.0.22 (31th January, 2024)
265
-
266
- - Make `FileStorage` to check staleness of all cache files with set interval. (#169)
267
- - Support AWS S3 storages. (#164)
268
- - Move `typing_extensions` from requirements.txt to pyproject.toml. (#161)
269
-
270
- ## 0.0.21 (29th December, 2023)
271
-
272
- - Fix inner transport and connection pool instances closing. (#147)
273
- - Improved error message when the storage type is incorrect. (#138)
274
-
275
- ## 0.0.20 (12th December, 2023)
276
-
277
- - Add in-memory storage. (#133)
278
- - Allow customization of cache key generation. (#130)
279
-
280
- ## 0.0.19 (30th November, 2023)
281
-
282
- - Add `force_cache` extension to enforce the request to be cached, ignoring the HTTP headers. (#117)
283
- - Fix issue where sqlite storage cache get deleted immediately. (#119)
284
- - Support float numbers for storage ttl. (#107)
285
-
286
- ## 0.0.18 (23rd November, 2023)
287
-
288
- - Fix issue where freshness cannot be calculated to re-send request. (#104)
289
- - Add `cache_disabled` extension to temporarily disable the cache (#109)
290
- - Update `datetime.datetime.utcnow()` to `datetime.datetime.now(datetime.timezone.utc)` since `datetime.datetime.utcnow()` has been deprecated. (#111)
291
-
292
- ## 0.0.17 (6th November, 2023)
293
-
294
- - Fix `Last-Modified` validation.
295
-
296
- ## 0.0.16 (25th October, 2023)
297
-
298
- - Add `install_cache` function. (#95)
299
- - Add sqlite support. (#92)
300
- - Move `ttl` argument to `BaseStorage` class. (#94)
301
-
302
- ## 0.0.14 (23rd October, 2023)
303
-
304
- - Replace `AsyncResponseStream` with `AsyncCacheStream`. (#86)
305
- - Add `must-understand` response directive support. (#90)
306
-
307
- ## 0.0.13 (5th October, 2023)
308
-
309
- - Add support for Python 3.12. (#71)
310
- - Fix connections releasing from the connection pool. (#83)
195
+ ## [0.1.5] - 2025-10-18
311
196
 
312
- ## 0.0.12 (8th September, 2023)
197
+ ### 🚀 Features
313
198
 
314
- - Add metadata into the response extensions. (#56)
199
+ - *(perf)* Set chunk size to 128KB for httpx to reduce SQLite read/writes
200
+ - Better cache-control parsing
201
+ - Add close method to storages API (#384)
202
+ - *(perf)* Increase requests buffer size to 128KB, disable charset detection
315
203
 
316
- ## 0.0.11 (15th August, 2023)
204
+ ### 🐛 Bug Fixes
317
205
 
318
- - Add support for request `cache-control` directives. (#42)
319
- - Drop httpcore dependency. (#40)
320
- - Support HTTP methods only if they are defined as cacheable. (#37)
206
+ - *(docs)* Fix some line breaks
321
207
 
322
- ## 0.0.10 (7th August, 2023)
208
+ ### ⚙️ Miscellaneous Tasks
323
209
 
324
- - Add Response metadata. (#33)
325
- - Add API Reference documentation. (#30)
326
- - Use stale responses only if the client is disconnected. (#28)
210
+ - Remove some redundant files from repo
211
+ ## [0.1.4] - 2025-10-14
327
212
 
328
- ## 0.0.9 (1st August, 2023)
213
+ ### 🚀 Features
329
214
 
330
- - Expose Controller API. (#23)
215
+ - Add support for a sans-IO API (#366)
216
+ - Allow already consumed streams with `CacheTransport` (#377)
217
+ - Add sqlite storage for beta storages
218
+ - Get rid of some locks from sqlite storage
219
+ - Better async implemetation for sqlite storage
331
220
 
332
- ## 0.0.8 (31st July, 2023)
221
+ ### 🐛 Bug Fixes
333
222
 
334
- - Skip redis tests if the server was not found. (#16)
335
- - Decrease sleep time for the storage ttl tests. (#18)
336
- - Fail coverage under 100. (#19)
223
+ - Create an sqlite file in a cache folder
224
+ - Fix beta imports
337
225
 
338
- ## 0.0.7 (30th July, 2023)
226
+ ### ⚙️ Miscellaneous Tasks
339
227
 
340
- - Add support for `Heuristic Freshness`. (#11)
341
- - Change `Controller.cache_heuristically` to `Controller.allow_heuristics`. (#12)
342
- - Handle import errors. (#13)
228
+ - Improve CI (#369)
229
+ - *(internal)* Remove src folder (#373)
230
+ - *(internal)* Temporary remove python3.14 from CI
231
+ - *(tests)* Add sqlite tests for new storage
232
+ - *(tests)* Move some tests to beta
233
+ ## [0.1.3] - 2025-07-06
343
234
 
344
- ## 0.0.6 (29th July, 2023)
235
+ ### 🚀 Features
345
236
 
346
- - Fix `Vary` header validation. (#8)
347
- - Dump original requests with the responses. (#7)
237
+ - Support providing a path prefix to S3 storage (#342)
348
238
 
349
- ## 0.0.5 (29th July, 2023)
239
+ ### 📚 Documentation
350
240
 
351
- - Fix httpx response streaming.
241
+ - Update link to httpx transports page (#337)
242
+ ## [0.1.2] - 2025-04-04
352
243
 
353
- ## 0.0.4 (29th July, 2023)
244
+ ### 🐛 Bug Fixes
354
245
 
355
- - Change `YamlSerializer` name to `YAMLSerializer`.
246
+ - Requirements.txt to reduce vulnerabilities (#263)
247
+ ## [0.0.30] - 2024-07-12
356
248
 
357
- ## 0.0.3 (28th July, 2023)
249
+ ### 🐛 Bug Fixes
358
250
 
359
- - Add `from_cache` response extension.
360
- - Add `typing_extensions` into the requirements.
251
+ - Requirements.txt to reduce vulnerabilities (#245)
252
+ - Requirements.txt to reduce vulnerabilities (#255)
253
+ ## [0.0.27] - 2024-05-31
361
254
 
362
- ## 0.0.2 (25th July, 2023)
255
+ ### 🐛 Bug Fixes
363
256
 
364
- - Add [redis](https://redis.io/) support.
365
- - Make backends thread and task safe.
366
- - Add black as a new linter.
367
- - Add an expire time for cached responses.
257
+ - *(redis)* Do not update metadata with negative ttl (#231)
258
+ ## [0.0.1] - 2023-07-22
@@ -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.5"
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,7 +149,7 @@ 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
 
@@ -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
 
@@ -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)
@@ -720,7 +716,7 @@ class AsyncS3Storage(AsyncBaseStorage): # pragma: no cover
720
716
  """
721
717
 
722
718
  if isinstance(key, Response): # pragma: no cover
723
- key = t.cast(str, key.extensions["cache_metadata"]["cache_key"])
719
+ key = tp.cast(str, key.extensions["cache_metadata"]["cache_key"])
724
720
 
725
721
  async with self._lock:
726
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")
@@ -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