hishel 1.0.0.dev2__tar.gz → 1.1.0__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.
- {hishel-1.0.0.dev2 → hishel-1.1.0}/CHANGELOG.md +52 -0
- {hishel-1.0.0.dev2 → hishel-1.1.0}/PKG-INFO +225 -18
- {hishel-1.0.0.dev2 → hishel-1.1.0}/README.md +170 -17
- {hishel-1.0.0.dev2 → hishel-1.1.0}/hishel/__init__.py +26 -17
- hishel-1.1.0/hishel/_async_cache.py +213 -0
- hishel-1.1.0/hishel/_async_httpx.py +236 -0
- {hishel-1.0.0.dev2 → hishel-1.1.0}/hishel/_core/_headers.py +11 -1
- {hishel-1.0.0.dev2 → hishel-1.1.0}/hishel/_core/_spec.py +101 -120
- hishel-1.1.0/hishel/_core/_storages/_async_base.py +71 -0
- hishel-1.0.0.dev2/hishel/_core/_async/_storages/_sqlite.py → hishel-1.1.0/hishel/_core/_storages/_async_sqlite.py +100 -134
- hishel-1.1.0/hishel/_core/_storages/_packing.py +144 -0
- hishel-1.1.0/hishel/_core/_storages/_sync_base.py +71 -0
- hishel-1.0.0.dev2/hishel/_core/_sync/_storages/_sqlite.py → hishel-1.1.0/hishel/_core/_storages/_sync_sqlite.py +100 -134
- {hishel-1.0.0.dev2 → hishel-1.1.0}/hishel/_core/models.py +93 -33
- hishel-1.1.0/hishel/_policies.py +49 -0
- hishel-1.1.0/hishel/_sync_cache.py +213 -0
- hishel-1.1.0/hishel/_sync_httpx.py +236 -0
- {hishel-1.0.0.dev2 → hishel-1.1.0}/hishel/_utils.py +49 -2
- hishel-1.1.0/hishel/asgi.py +400 -0
- hishel-1.1.0/hishel/fastapi.py +263 -0
- hishel-1.1.0/hishel/httpx.py +12 -0
- {hishel-1.0.0.dev2 → hishel-1.1.0}/hishel/requests.py +28 -22
- {hishel-1.0.0.dev2 → hishel-1.1.0}/pyproject.toml +13 -2
- hishel-1.0.0.dev2/hishel/_async_cache.py +0 -174
- hishel-1.0.0.dev2/hishel/_core/__init__.py +0 -59
- hishel-1.0.0.dev2/hishel/_core/_base/_storages/_base.py +0 -272
- hishel-1.0.0.dev2/hishel/_core/_base/_storages/_packing.py +0 -165
- hishel-1.0.0.dev2/hishel/_sync_cache.py +0 -174
- hishel-1.0.0.dev2/hishel/httpx.py +0 -335
- {hishel-1.0.0.dev2 → hishel-1.1.0}/.gitignore +0 -0
- {hishel-1.0.0.dev2 → hishel-1.1.0}/LICENSE +0 -0
- {hishel-1.0.0.dev2 → hishel-1.1.0}/hishel/py.typed +0 -0
|
@@ -2,10 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 1.1.0 - 2025-10-31
|
|
6
|
+
### ⚙️ Miscellaneous Tasks
|
|
7
|
+
- Add in memory example
|
|
8
|
+
|
|
9
|
+
### 🐛 Bug Fixes
|
|
10
|
+
- Pass any response with non-expected status code on revalidation to client
|
|
11
|
+
|
|
12
|
+
### 🚀 Features
|
|
13
|
+
- Allow setting storage base with via `database_path` for sqlite storage
|
|
14
|
+
|
|
15
|
+
## 1.0.0 - 2025-10-28
|
|
16
|
+
### ⚙️ Miscellaneous Tasks
|
|
17
|
+
- Add examples, improve docs
|
|
18
|
+
|
|
19
|
+
## 1.0.0b1 - 2025-10-28
|
|
20
|
+
### ♻️ Refactoring
|
|
21
|
+
- Add policies
|
|
22
|
+
|
|
23
|
+
### ⚙️ Miscellaneous Tasks
|
|
24
|
+
- Improve sans-io diagram colors
|
|
25
|
+
- Add graphql docs
|
|
26
|
+
|
|
27
|
+
### 🐛 Bug Fixes
|
|
28
|
+
- Body-sensitive responses caching
|
|
29
|
+
- Filter out `Transfer-Encoding` header for asgi responses
|
|
30
|
+
|
|
31
|
+
### 🚀 Features
|
|
32
|
+
- Add global `use_body_key` setting
|
|
33
|
+
|
|
34
|
+
## 1.0.0.dev3 - 2025-10-26
|
|
35
|
+
### ♻️ Refactoring
|
|
36
|
+
- Replace pairs with entries, simplify storage API
|
|
37
|
+
- Automatically generate httpx sync integration from async
|
|
38
|
+
|
|
39
|
+
### ⚙️ Miscellaneous Tasks
|
|
40
|
+
- Simplify metadata docs
|
|
41
|
+
- Add custom integrations docs
|
|
42
|
+
- More robust compressed response caching
|
|
43
|
+
|
|
44
|
+
### 🐛 Bug Fixes
|
|
45
|
+
- Add missing permissions into `publish.yml`
|
|
46
|
+
- Raise on consumed httpx streams, which we can't store as is (it's already decoded)
|
|
47
|
+
- Fix compressed data caching for requests
|
|
48
|
+
- Handle httpx iterable usage instead of iterator correctly
|
|
49
|
+
- Add date header for proper age calculation
|
|
50
|
+
|
|
51
|
+
### 🚀 Features
|
|
52
|
+
- Add integrations with fastapi and asgi
|
|
53
|
+
- Add blacksheep integration examples
|
|
54
|
+
- Add logging for asgi
|
|
55
|
+
|
|
5
56
|
## 1.0.0.dev2 - 2025-10-21
|
|
6
57
|
### ⚙️ Miscellaneous Tasks
|
|
7
58
|
- Remove redundant utils and tests
|
|
8
59
|
- Add import without extras check in ci
|
|
60
|
+
- Fix time travel date, explicitly specify the timezone
|
|
9
61
|
|
|
10
62
|
### 🐛 Bug Fixes
|
|
11
63
|
- Fix check for storing auth requests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hishel
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Elegant HTTP Caching for Python
|
|
5
5
|
Project-URL: Homepage, https://hishel.com
|
|
6
6
|
Project-URL: Source, https://github.com/karpetrosyan/hishel
|
|
@@ -29,6 +29,8 @@ Requires-Dist: typing-extensions>=4.14.1
|
|
|
29
29
|
Provides-Extra: async
|
|
30
30
|
Requires-Dist: anyio>=4.9.0; extra == 'async'
|
|
31
31
|
Requires-Dist: anysqlite>=0.0.5; extra == 'async'
|
|
32
|
+
Provides-Extra: fastapi
|
|
33
|
+
Requires-Dist: fastapi>=0.119.1; extra == 'fastapi'
|
|
32
34
|
Provides-Extra: httpx
|
|
33
35
|
Requires-Dist: anyio>=4.9.0; extra == 'httpx'
|
|
34
36
|
Requires-Dist: anysqlite>=0.0.5; extra == 'httpx'
|
|
@@ -37,6 +39,7 @@ Provides-Extra: requests
|
|
|
37
39
|
Requires-Dist: requests>=2.32.5; extra == 'requests'
|
|
38
40
|
Description-Content-Type: text/markdown
|
|
39
41
|
|
|
42
|
+
|
|
40
43
|
<p align="center">
|
|
41
44
|
<img alt="Hishel Logo" width="350" src="https://raw.githubusercontent.com/karpetrosyan/hishel/master/docs/static/Shelkopryad_350x250_yellow.png#gh-dark-mode-only">
|
|
42
45
|
<img alt="Hishel Logo" width="350" src="https://raw.githubusercontent.com/karpetrosyan/hishel/master/docs/static/Shelkopryad_350x250_black.png#gh-light-mode-only">
|
|
@@ -73,14 +76,16 @@ Description-Content-Type: text/markdown
|
|
|
73
76
|
## ✨ Features
|
|
74
77
|
|
|
75
78
|
- 🎯 **RFC 9111 Compliant** - Fully compliant with the latest HTTP caching specification
|
|
76
|
-
- 🔌 **Easy Integration** - Drop-in support for HTTPX and
|
|
79
|
+
- 🔌 **Easy Integration** - Drop-in support for HTTPX, Requests, ASGI, FastAPI, and BlackSheep
|
|
77
80
|
- 💾 **Flexible Storage** - SQLite backend with more coming soon
|
|
78
81
|
- ⚡ **High Performance** - Efficient caching with minimal overhead
|
|
79
82
|
- 🔄 **Async & Sync** - Full support for both synchronous and asynchronous workflows
|
|
80
83
|
- 🎨 **Type Safe** - Fully typed with comprehensive type hints
|
|
81
84
|
- 🧪 **Well Tested** - Extensive test coverage and battle-tested
|
|
82
|
-
- 🎛️ **Configurable** - Fine-grained control over caching behavior
|
|
83
|
-
-
|
|
85
|
+
- 🎛️ **Configurable** - Fine-grained control over caching behavior with flexible policies
|
|
86
|
+
- 💨 **Memory Efficient** - Streaming support prevents loading large payloads into memory
|
|
87
|
+
- 🌐 **Universal** - Works with any ASGI application (Starlette, Litestar, BlackSheep, etc.)
|
|
88
|
+
- 🎯 **GraphQL Support** - Cache GraphQL queries with body-sensitive content caching
|
|
84
89
|
|
|
85
90
|
## 📦 Installation
|
|
86
91
|
|
|
@@ -90,19 +95,23 @@ pip install hishel
|
|
|
90
95
|
|
|
91
96
|
### Optional Dependencies
|
|
92
97
|
|
|
93
|
-
Install with specific
|
|
98
|
+
Install with specific integration support:
|
|
94
99
|
|
|
95
100
|
```bash
|
|
96
101
|
pip install hishel[httpx] # For HTTPX support
|
|
97
102
|
pip install hishel[requests] # For Requests support
|
|
103
|
+
pip install hishel[fastapi] # For FastAPI support (includes ASGI)
|
|
98
104
|
```
|
|
99
105
|
|
|
100
|
-
Or install
|
|
106
|
+
Or install multiple:
|
|
101
107
|
|
|
102
108
|
```bash
|
|
103
|
-
pip install hishel[httpx,requests]
|
|
109
|
+
pip install hishel[httpx,requests,fastapi]
|
|
104
110
|
```
|
|
105
111
|
|
|
112
|
+
> [!NOTE]
|
|
113
|
+
> ASGI middleware has no extra dependencies - it's included in the base installation.
|
|
114
|
+
|
|
106
115
|
## 🚀 Quick Start
|
|
107
116
|
|
|
108
117
|
### With HTTPX
|
|
@@ -156,23 +165,112 @@ response = session.get("https://api.example.com/data")
|
|
|
156
165
|
print(response.headers.get("X-Hishel-From-Cache")) # "True"
|
|
157
166
|
```
|
|
158
167
|
|
|
168
|
+
### With ASGI Applications
|
|
169
|
+
|
|
170
|
+
Add caching middleware to any ASGI application:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from hishel.asgi import ASGICacheMiddleware
|
|
174
|
+
|
|
175
|
+
# Wrap your ASGI app
|
|
176
|
+
app = ASGICacheMiddleware(app)
|
|
177
|
+
|
|
178
|
+
# Or configure with options
|
|
179
|
+
from hishel import AsyncSqliteStorage, CacheOptions, SpecificationPolicy
|
|
180
|
+
|
|
181
|
+
app = ASGICacheMiddleware(
|
|
182
|
+
app,
|
|
183
|
+
storage=AsyncSqliteStorage(),
|
|
184
|
+
policy=SpecificationPolicy(
|
|
185
|
+
cache_options=CacheOptions(shared=True)
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### With FastAPI
|
|
191
|
+
|
|
192
|
+
Add Cache-Control headers using the `cache()` dependency:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from fastapi import FastAPI
|
|
196
|
+
from hishel.fastapi import cache
|
|
197
|
+
|
|
198
|
+
app = FastAPI()
|
|
199
|
+
|
|
200
|
+
@app.get("/api/data", dependencies=[cache(max_age=300, public=True)])
|
|
201
|
+
async def get_data():
|
|
202
|
+
# Cache-Control: public, max-age=300
|
|
203
|
+
return {"data": "cached for 5 minutes"}
|
|
204
|
+
|
|
205
|
+
# Optionally wrap with ASGI middleware for local caching according to specified rules
|
|
206
|
+
from hishel.asgi import ASGICacheMiddleware
|
|
207
|
+
from hishel import AsyncSqliteStorage
|
|
208
|
+
|
|
209
|
+
app = ASGICacheMiddleware(app, storage=AsyncSqliteStorage())
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### With BlackSheep
|
|
213
|
+
|
|
214
|
+
Use BlackSheep's native `cache_control` decorator with Hishel's ASGI middleware:
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
from blacksheep import Application, get
|
|
218
|
+
from blacksheep.server.headers.cache import cache_control
|
|
219
|
+
|
|
220
|
+
app = Application()
|
|
221
|
+
|
|
222
|
+
@get("/api/data")
|
|
223
|
+
@cache_control(max_age=300, public=True)
|
|
224
|
+
async def get_data():
|
|
225
|
+
# Cache-Control: public, max-age=300
|
|
226
|
+
return {"data": "cached for 5 minutes"}
|
|
227
|
+
```
|
|
228
|
+
|
|
159
229
|
## 🎛️ Advanced Configuration
|
|
160
230
|
|
|
161
|
-
###
|
|
231
|
+
### Caching Policies
|
|
232
|
+
|
|
233
|
+
Hishel supports two types of caching policies:
|
|
234
|
+
|
|
235
|
+
**SpecificationPolicy** - RFC 9111 compliant HTTP caching (default):
|
|
162
236
|
|
|
163
237
|
```python
|
|
164
|
-
from hishel import CacheOptions
|
|
238
|
+
from hishel import CacheOptions, SpecificationPolicy
|
|
165
239
|
from hishel.httpx import SyncCacheClient
|
|
166
240
|
|
|
167
241
|
client = SyncCacheClient(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
242
|
+
policy=SpecificationPolicy(
|
|
243
|
+
cache_options=CacheOptions(
|
|
244
|
+
shared=False, # Use as private cache (browser-like)
|
|
245
|
+
supported_methods=["GET", "HEAD", "POST"], # Cache GET, HEAD, and POST
|
|
246
|
+
allow_stale=True # Allow serving stale responses
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**FilterPolicy** - Custom filtering logic for fine-grained control:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from hishel import FilterPolicy, BaseFilter, Request
|
|
256
|
+
from hishel.httpx import AsyncCacheClient
|
|
257
|
+
|
|
258
|
+
class CacheOnlyAPIRequests(BaseFilter[Request]):
|
|
259
|
+
def needs_body(self) -> bool:
|
|
260
|
+
return False
|
|
261
|
+
|
|
262
|
+
def apply(self, item: Request, body: bytes | None) -> bool:
|
|
263
|
+
return "/api/" in str(item.url)
|
|
264
|
+
|
|
265
|
+
client = AsyncCacheClient(
|
|
266
|
+
policy=FilterPolicy(
|
|
267
|
+
request_filters=[CacheOnlyAPIRequests()]
|
|
172
268
|
)
|
|
173
269
|
)
|
|
174
270
|
```
|
|
175
271
|
|
|
272
|
+
[Learn more about policies →](https://hishel.com/dev/policies/)
|
|
273
|
+
|
|
176
274
|
### Custom Storage Backend
|
|
177
275
|
|
|
178
276
|
```python
|
|
@@ -188,6 +286,60 @@ storage = SyncSqliteStorage(
|
|
|
188
286
|
client = SyncCacheClient(storage=storage)
|
|
189
287
|
```
|
|
190
288
|
|
|
289
|
+
### GraphQL and Body-Sensitive Caching
|
|
290
|
+
|
|
291
|
+
Cache GraphQL queries and other POST requests by including the request body in the cache key.
|
|
292
|
+
|
|
293
|
+
**Using per-request header:**
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from hishel import FilterPolicy
|
|
297
|
+
from hishel.httpx import SyncCacheClient
|
|
298
|
+
|
|
299
|
+
client = SyncCacheClient(
|
|
300
|
+
policy=FilterPolicy()
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Cache GraphQL queries - different queries get different cache entries
|
|
304
|
+
graphql_query = """
|
|
305
|
+
query GetUser($id: ID!) {
|
|
306
|
+
user(id: $id) {
|
|
307
|
+
name
|
|
308
|
+
email
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
response = client.post(
|
|
314
|
+
"https://api.example.com/graphql",
|
|
315
|
+
json={"query": graphql_query, "variables": {"id": "123"}},
|
|
316
|
+
headers={"X-Hishel-Body-Key": "true"} # Enable body-based caching
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Different query will be cached separately
|
|
320
|
+
response = client.post(
|
|
321
|
+
"https://api.example.com/graphql",
|
|
322
|
+
json={"query": graphql_query, "variables": {"id": "456"}},
|
|
323
|
+
headers={"X-Hishel-Body-Key": "true"}
|
|
324
|
+
)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Using global configuration:**
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
from hishel.httpx import SyncCacheClient
|
|
331
|
+
from hishel import FilterPolicy
|
|
332
|
+
|
|
333
|
+
# Enable body-based caching for all requests
|
|
334
|
+
client = SyncCacheClient(policy=FilterPolicy(use_body_key=True))
|
|
335
|
+
|
|
336
|
+
# All POST requests automatically include body in cache key
|
|
337
|
+
response = client.post(
|
|
338
|
+
"https://api.example.com/graphql",
|
|
339
|
+
json={"query": graphql_query, "variables": {"id": "123"}}
|
|
340
|
+
)
|
|
341
|
+
```
|
|
342
|
+
|
|
191
343
|
## 🏗️ Architecture
|
|
192
344
|
|
|
193
345
|
Hishel uses a **sans-I/O state machine** architecture that separates HTTP caching logic from I/O operations:
|
|
@@ -199,13 +351,11 @@ Hishel uses a **sans-I/O state machine** architecture that separates HTTP cachin
|
|
|
199
351
|
|
|
200
352
|
## 🔮 Roadmap
|
|
201
353
|
|
|
202
|
-
|
|
354
|
+
We're actively working on:
|
|
203
355
|
|
|
204
|
-
- 🎯 Additional HTTP client integrations
|
|
205
|
-
- 🎯 Server-side caching support
|
|
206
|
-
- 🎯 More storage backends
|
|
207
|
-
- 🎯 Advanced caching strategies
|
|
208
356
|
- 🎯 Performance optimizations
|
|
357
|
+
- 🎯 More integrations
|
|
358
|
+
- 🎯 Partial responses support
|
|
209
359
|
|
|
210
360
|
## 📚 Documentation
|
|
211
361
|
|
|
@@ -214,7 +364,12 @@ Comprehensive documentation is available at [https://hishel.com/dev](https://his
|
|
|
214
364
|
- [Getting Started](https://hishel.com)
|
|
215
365
|
- [HTTPX Integration](https://hishel.com/dev/integrations/httpx)
|
|
216
366
|
- [Requests Integration](https://hishel.com/dev/integrations/requests)
|
|
367
|
+
- [ASGI Integration](https://hishel.com/dev/asgi)
|
|
368
|
+
- [FastAPI Integration](https://hishel.com/dev/fastapi)
|
|
369
|
+
- [BlackSheep Integration](https://hishel.com/dev/integrations/blacksheep)
|
|
370
|
+
- [GraphQL Integration](https://hishel.com/dev/integrations/graphql)
|
|
217
371
|
- [Storage Backends](https://hishel.com/dev/storages)
|
|
372
|
+
- [Request/Response Metadata](https://hishel.com/dev/metadata)
|
|
218
373
|
- [RFC 9111 Specification](https://hishel.com/dev/specification)
|
|
219
374
|
|
|
220
375
|
## 🤝 Contributing
|
|
@@ -255,10 +410,62 @@ Hishel is inspired by and builds upon the excellent work in the Python HTTP ecos
|
|
|
255
410
|
|
|
256
411
|
All notable changes to this project will be documented in this file.
|
|
257
412
|
|
|
413
|
+
## 1.1.0 - 2025-10-31
|
|
414
|
+
### ⚙️ Miscellaneous Tasks
|
|
415
|
+
- Add in memory example
|
|
416
|
+
|
|
417
|
+
### 🐛 Bug Fixes
|
|
418
|
+
- Pass any response with non-expected status code on revalidation to client
|
|
419
|
+
|
|
420
|
+
### 🚀 Features
|
|
421
|
+
- Allow setting storage base with via `database_path` for sqlite storage
|
|
422
|
+
|
|
423
|
+
## 1.0.0 - 2025-10-28
|
|
424
|
+
### ⚙️ Miscellaneous Tasks
|
|
425
|
+
- Add examples, improve docs
|
|
426
|
+
|
|
427
|
+
## 1.0.0b1 - 2025-10-28
|
|
428
|
+
### ♻️ Refactoring
|
|
429
|
+
- Add policies
|
|
430
|
+
|
|
431
|
+
### ⚙️ Miscellaneous Tasks
|
|
432
|
+
- Improve sans-io diagram colors
|
|
433
|
+
- Add graphql docs
|
|
434
|
+
|
|
435
|
+
### 🐛 Bug Fixes
|
|
436
|
+
- Body-sensitive responses caching
|
|
437
|
+
- Filter out `Transfer-Encoding` header for asgi responses
|
|
438
|
+
|
|
439
|
+
### 🚀 Features
|
|
440
|
+
- Add global `use_body_key` setting
|
|
441
|
+
|
|
442
|
+
## 1.0.0.dev3 - 2025-10-26
|
|
443
|
+
### ♻️ Refactoring
|
|
444
|
+
- Replace pairs with entries, simplify storage API
|
|
445
|
+
- Automatically generate httpx sync integration from async
|
|
446
|
+
|
|
447
|
+
### ⚙️ Miscellaneous Tasks
|
|
448
|
+
- Simplify metadata docs
|
|
449
|
+
- Add custom integrations docs
|
|
450
|
+
- More robust compressed response caching
|
|
451
|
+
|
|
452
|
+
### 🐛 Bug Fixes
|
|
453
|
+
- Add missing permissions into `publish.yml`
|
|
454
|
+
- Raise on consumed httpx streams, which we can't store as is (it's already decoded)
|
|
455
|
+
- Fix compressed data caching for requests
|
|
456
|
+
- Handle httpx iterable usage instead of iterator correctly
|
|
457
|
+
- Add date header for proper age calculation
|
|
458
|
+
|
|
459
|
+
### 🚀 Features
|
|
460
|
+
- Add integrations with fastapi and asgi
|
|
461
|
+
- Add blacksheep integration examples
|
|
462
|
+
- Add logging for asgi
|
|
463
|
+
|
|
258
464
|
## 1.0.0.dev2 - 2025-10-21
|
|
259
465
|
### ⚙️ Miscellaneous Tasks
|
|
260
466
|
- Remove redundant utils and tests
|
|
261
467
|
- Add import without extras check in ci
|
|
468
|
+
- Fix time travel date, explicitly specify the timezone
|
|
262
469
|
|
|
263
470
|
### 🐛 Bug Fixes
|
|
264
471
|
- Fix check for storing auth requests
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
<p align="center">
|
|
2
3
|
<img alt="Hishel Logo" width="350" src="https://raw.githubusercontent.com/karpetrosyan/hishel/master/docs/static/Shelkopryad_350x250_yellow.png#gh-dark-mode-only">
|
|
3
4
|
<img alt="Hishel Logo" width="350" src="https://raw.githubusercontent.com/karpetrosyan/hishel/master/docs/static/Shelkopryad_350x250_black.png#gh-light-mode-only">
|
|
@@ -34,14 +35,16 @@
|
|
|
34
35
|
## ✨ Features
|
|
35
36
|
|
|
36
37
|
- 🎯 **RFC 9111 Compliant** - Fully compliant with the latest HTTP caching specification
|
|
37
|
-
- 🔌 **Easy Integration** - Drop-in support for HTTPX and
|
|
38
|
+
- 🔌 **Easy Integration** - Drop-in support for HTTPX, Requests, ASGI, FastAPI, and BlackSheep
|
|
38
39
|
- 💾 **Flexible Storage** - SQLite backend with more coming soon
|
|
39
40
|
- ⚡ **High Performance** - Efficient caching with minimal overhead
|
|
40
41
|
- 🔄 **Async & Sync** - Full support for both synchronous and asynchronous workflows
|
|
41
42
|
- 🎨 **Type Safe** - Fully typed with comprehensive type hints
|
|
42
43
|
- 🧪 **Well Tested** - Extensive test coverage and battle-tested
|
|
43
|
-
- 🎛️ **Configurable** - Fine-grained control over caching behavior
|
|
44
|
-
-
|
|
44
|
+
- 🎛️ **Configurable** - Fine-grained control over caching behavior with flexible policies
|
|
45
|
+
- 💨 **Memory Efficient** - Streaming support prevents loading large payloads into memory
|
|
46
|
+
- 🌐 **Universal** - Works with any ASGI application (Starlette, Litestar, BlackSheep, etc.)
|
|
47
|
+
- 🎯 **GraphQL Support** - Cache GraphQL queries with body-sensitive content caching
|
|
45
48
|
|
|
46
49
|
## 📦 Installation
|
|
47
50
|
|
|
@@ -51,19 +54,23 @@ pip install hishel
|
|
|
51
54
|
|
|
52
55
|
### Optional Dependencies
|
|
53
56
|
|
|
54
|
-
Install with specific
|
|
57
|
+
Install with specific integration support:
|
|
55
58
|
|
|
56
59
|
```bash
|
|
57
60
|
pip install hishel[httpx] # For HTTPX support
|
|
58
61
|
pip install hishel[requests] # For Requests support
|
|
62
|
+
pip install hishel[fastapi] # For FastAPI support (includes ASGI)
|
|
59
63
|
```
|
|
60
64
|
|
|
61
|
-
Or install
|
|
65
|
+
Or install multiple:
|
|
62
66
|
|
|
63
67
|
```bash
|
|
64
|
-
pip install hishel[httpx,requests]
|
|
68
|
+
pip install hishel[httpx,requests,fastapi]
|
|
65
69
|
```
|
|
66
70
|
|
|
71
|
+
> [!NOTE]
|
|
72
|
+
> ASGI middleware has no extra dependencies - it's included in the base installation.
|
|
73
|
+
|
|
67
74
|
## 🚀 Quick Start
|
|
68
75
|
|
|
69
76
|
### With HTTPX
|
|
@@ -117,23 +124,112 @@ response = session.get("https://api.example.com/data")
|
|
|
117
124
|
print(response.headers.get("X-Hishel-From-Cache")) # "True"
|
|
118
125
|
```
|
|
119
126
|
|
|
127
|
+
### With ASGI Applications
|
|
128
|
+
|
|
129
|
+
Add caching middleware to any ASGI application:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from hishel.asgi import ASGICacheMiddleware
|
|
133
|
+
|
|
134
|
+
# Wrap your ASGI app
|
|
135
|
+
app = ASGICacheMiddleware(app)
|
|
136
|
+
|
|
137
|
+
# Or configure with options
|
|
138
|
+
from hishel import AsyncSqliteStorage, CacheOptions, SpecificationPolicy
|
|
139
|
+
|
|
140
|
+
app = ASGICacheMiddleware(
|
|
141
|
+
app,
|
|
142
|
+
storage=AsyncSqliteStorage(),
|
|
143
|
+
policy=SpecificationPolicy(
|
|
144
|
+
cache_options=CacheOptions(shared=True)
|
|
145
|
+
),
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### With FastAPI
|
|
150
|
+
|
|
151
|
+
Add Cache-Control headers using the `cache()` dependency:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from fastapi import FastAPI
|
|
155
|
+
from hishel.fastapi import cache
|
|
156
|
+
|
|
157
|
+
app = FastAPI()
|
|
158
|
+
|
|
159
|
+
@app.get("/api/data", dependencies=[cache(max_age=300, public=True)])
|
|
160
|
+
async def get_data():
|
|
161
|
+
# Cache-Control: public, max-age=300
|
|
162
|
+
return {"data": "cached for 5 minutes"}
|
|
163
|
+
|
|
164
|
+
# Optionally wrap with ASGI middleware for local caching according to specified rules
|
|
165
|
+
from hishel.asgi import ASGICacheMiddleware
|
|
166
|
+
from hishel import AsyncSqliteStorage
|
|
167
|
+
|
|
168
|
+
app = ASGICacheMiddleware(app, storage=AsyncSqliteStorage())
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### With BlackSheep
|
|
172
|
+
|
|
173
|
+
Use BlackSheep's native `cache_control` decorator with Hishel's ASGI middleware:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from blacksheep import Application, get
|
|
177
|
+
from blacksheep.server.headers.cache import cache_control
|
|
178
|
+
|
|
179
|
+
app = Application()
|
|
180
|
+
|
|
181
|
+
@get("/api/data")
|
|
182
|
+
@cache_control(max_age=300, public=True)
|
|
183
|
+
async def get_data():
|
|
184
|
+
# Cache-Control: public, max-age=300
|
|
185
|
+
return {"data": "cached for 5 minutes"}
|
|
186
|
+
```
|
|
187
|
+
|
|
120
188
|
## 🎛️ Advanced Configuration
|
|
121
189
|
|
|
122
|
-
###
|
|
190
|
+
### Caching Policies
|
|
191
|
+
|
|
192
|
+
Hishel supports two types of caching policies:
|
|
193
|
+
|
|
194
|
+
**SpecificationPolicy** - RFC 9111 compliant HTTP caching (default):
|
|
123
195
|
|
|
124
196
|
```python
|
|
125
|
-
from hishel import CacheOptions
|
|
197
|
+
from hishel import CacheOptions, SpecificationPolicy
|
|
126
198
|
from hishel.httpx import SyncCacheClient
|
|
127
199
|
|
|
128
200
|
client = SyncCacheClient(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
201
|
+
policy=SpecificationPolicy(
|
|
202
|
+
cache_options=CacheOptions(
|
|
203
|
+
shared=False, # Use as private cache (browser-like)
|
|
204
|
+
supported_methods=["GET", "HEAD", "POST"], # Cache GET, HEAD, and POST
|
|
205
|
+
allow_stale=True # Allow serving stale responses
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**FilterPolicy** - Custom filtering logic for fine-grained control:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from hishel import FilterPolicy, BaseFilter, Request
|
|
215
|
+
from hishel.httpx import AsyncCacheClient
|
|
216
|
+
|
|
217
|
+
class CacheOnlyAPIRequests(BaseFilter[Request]):
|
|
218
|
+
def needs_body(self) -> bool:
|
|
219
|
+
return False
|
|
220
|
+
|
|
221
|
+
def apply(self, item: Request, body: bytes | None) -> bool:
|
|
222
|
+
return "/api/" in str(item.url)
|
|
223
|
+
|
|
224
|
+
client = AsyncCacheClient(
|
|
225
|
+
policy=FilterPolicy(
|
|
226
|
+
request_filters=[CacheOnlyAPIRequests()]
|
|
133
227
|
)
|
|
134
228
|
)
|
|
135
229
|
```
|
|
136
230
|
|
|
231
|
+
[Learn more about policies →](https://hishel.com/dev/policies/)
|
|
232
|
+
|
|
137
233
|
### Custom Storage Backend
|
|
138
234
|
|
|
139
235
|
```python
|
|
@@ -149,6 +245,60 @@ storage = SyncSqliteStorage(
|
|
|
149
245
|
client = SyncCacheClient(storage=storage)
|
|
150
246
|
```
|
|
151
247
|
|
|
248
|
+
### GraphQL and Body-Sensitive Caching
|
|
249
|
+
|
|
250
|
+
Cache GraphQL queries and other POST requests by including the request body in the cache key.
|
|
251
|
+
|
|
252
|
+
**Using per-request header:**
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from hishel import FilterPolicy
|
|
256
|
+
from hishel.httpx import SyncCacheClient
|
|
257
|
+
|
|
258
|
+
client = SyncCacheClient(
|
|
259
|
+
policy=FilterPolicy()
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Cache GraphQL queries - different queries get different cache entries
|
|
263
|
+
graphql_query = """
|
|
264
|
+
query GetUser($id: ID!) {
|
|
265
|
+
user(id: $id) {
|
|
266
|
+
name
|
|
267
|
+
email
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
response = client.post(
|
|
273
|
+
"https://api.example.com/graphql",
|
|
274
|
+
json={"query": graphql_query, "variables": {"id": "123"}},
|
|
275
|
+
headers={"X-Hishel-Body-Key": "true"} # Enable body-based caching
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Different query will be cached separately
|
|
279
|
+
response = client.post(
|
|
280
|
+
"https://api.example.com/graphql",
|
|
281
|
+
json={"query": graphql_query, "variables": {"id": "456"}},
|
|
282
|
+
headers={"X-Hishel-Body-Key": "true"}
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**Using global configuration:**
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from hishel.httpx import SyncCacheClient
|
|
290
|
+
from hishel import FilterPolicy
|
|
291
|
+
|
|
292
|
+
# Enable body-based caching for all requests
|
|
293
|
+
client = SyncCacheClient(policy=FilterPolicy(use_body_key=True))
|
|
294
|
+
|
|
295
|
+
# All POST requests automatically include body in cache key
|
|
296
|
+
response = client.post(
|
|
297
|
+
"https://api.example.com/graphql",
|
|
298
|
+
json={"query": graphql_query, "variables": {"id": "123"}}
|
|
299
|
+
)
|
|
300
|
+
```
|
|
301
|
+
|
|
152
302
|
## 🏗️ Architecture
|
|
153
303
|
|
|
154
304
|
Hishel uses a **sans-I/O state machine** architecture that separates HTTP caching logic from I/O operations:
|
|
@@ -160,13 +310,11 @@ Hishel uses a **sans-I/O state machine** architecture that separates HTTP cachin
|
|
|
160
310
|
|
|
161
311
|
## 🔮 Roadmap
|
|
162
312
|
|
|
163
|
-
|
|
313
|
+
We're actively working on:
|
|
164
314
|
|
|
165
|
-
- 🎯 Additional HTTP client integrations
|
|
166
|
-
- 🎯 Server-side caching support
|
|
167
|
-
- 🎯 More storage backends
|
|
168
|
-
- 🎯 Advanced caching strategies
|
|
169
315
|
- 🎯 Performance optimizations
|
|
316
|
+
- 🎯 More integrations
|
|
317
|
+
- 🎯 Partial responses support
|
|
170
318
|
|
|
171
319
|
## 📚 Documentation
|
|
172
320
|
|
|
@@ -175,7 +323,12 @@ Comprehensive documentation is available at [https://hishel.com/dev](https://his
|
|
|
175
323
|
- [Getting Started](https://hishel.com)
|
|
176
324
|
- [HTTPX Integration](https://hishel.com/dev/integrations/httpx)
|
|
177
325
|
- [Requests Integration](https://hishel.com/dev/integrations/requests)
|
|
326
|
+
- [ASGI Integration](https://hishel.com/dev/asgi)
|
|
327
|
+
- [FastAPI Integration](https://hishel.com/dev/fastapi)
|
|
328
|
+
- [BlackSheep Integration](https://hishel.com/dev/integrations/blacksheep)
|
|
329
|
+
- [GraphQL Integration](https://hishel.com/dev/integrations/graphql)
|
|
178
330
|
- [Storage Backends](https://hishel.com/dev/storages)
|
|
331
|
+
- [Request/Response Metadata](https://hishel.com/dev/metadata)
|
|
179
332
|
- [RFC 9111 Specification](https://hishel.com/dev/specification)
|
|
180
333
|
|
|
181
334
|
## 🤝 Contributing
|