hishel 1.0.0.dev1__py3-none-any.whl → 1.0.0.dev3__py3-none-any.whl

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/httpx.py CHANGED
@@ -1,20 +1,5 @@
1
- from __future__ import annotations
2
-
3
- import ssl
4
- import typing as t
5
- from typing import AsyncIterator, Iterable, Iterator, Union, overload
6
-
7
- from hishel import Headers, Request, Response
8
- from hishel._async_cache import AsyncCacheProxy
9
- from hishel._core._base._storages._base import AsyncBaseStorage, SyncBaseStorage
10
- from hishel._core._spec import (
11
- CacheOptions,
12
- )
13
- from hishel._core.models import AnyIterable
14
- from hishel._sync_cache import SyncCacheProxy
15
-
16
1
  try:
17
- import httpx
2
+ import httpx # noqa: F401
18
3
  except ImportError as e:
19
4
  raise ImportError(
20
5
  "httpx is required to use hishel.httpx module. "
@@ -22,314 +7,6 @@ except ImportError as e:
22
7
  "e.g., 'pip install hishel[httpx]'."
23
8
  ) from e
24
9
 
25
- SOCKET_OPTION = t.Union[
26
- t.Tuple[int, int, int],
27
- t.Tuple[int, int, t.Union[bytes, bytearray]],
28
- t.Tuple[int, int, None, int],
29
- ]
30
-
31
- # 128 KB
32
- CHUNK_SIZE = 131072
33
-
34
-
35
- class IteratorStream(httpx.SyncByteStream, httpx.AsyncByteStream):
36
- def __init__(self, iterator: Iterator[bytes] | AsyncIterator[bytes]) -> None:
37
- self.iterator = iterator
38
-
39
- def __iter__(self) -> Iterator[bytes]:
40
- assert isinstance(self.iterator, (Iterator))
41
- yield from self.iterator
42
-
43
- async def __aiter__(self) -> AsyncIterator[bytes]:
44
- assert isinstance(self.iterator, (AsyncIterator))
45
- async for chunk in self.iterator:
46
- yield chunk
47
-
48
-
49
- @overload
50
- def internal_to_httpx(
51
- value: Request,
52
- ) -> httpx.Request: ...
53
- @overload
54
- def internal_to_httpx(
55
- value: Response,
56
- ) -> httpx.Response: ...
57
- def internal_to_httpx(
58
- value: Union[Request, Response],
59
- ) -> Union[httpx.Request, httpx.Response]:
60
- """
61
- Convert internal Request/Response to httpx.Request/httpx.Response.
62
- """
63
- if isinstance(value, Request):
64
- return httpx.Request(
65
- method=value.method,
66
- url=value.url,
67
- headers=value.headers,
68
- stream=IteratorStream(value.stream),
69
- extensions=value.metadata,
70
- )
71
- elif isinstance(value, Response):
72
- return httpx.Response(
73
- status_code=value.status_code,
74
- headers=value.headers,
75
- stream=IteratorStream(value.stream),
76
- extensions=value.metadata,
77
- )
78
-
79
-
80
- @overload
81
- def httpx_to_internal(
82
- value: httpx.Request,
83
- ) -> Request: ...
84
- @overload
85
- def httpx_to_internal(
86
- value: httpx.Response,
87
- ) -> Response: ...
88
- def httpx_to_internal(
89
- value: Union[httpx.Request, httpx.Response],
90
- ) -> Union[Request, Response]:
91
- """
92
- Convert httpx.Request/httpx.Response to internal Request/Response.
93
- """
94
- stream: Union[Iterator[bytes], AsyncIterator[bytes]]
95
- try:
96
- stream = AnyIterable(value.content)
97
- except (httpx.RequestNotRead, httpx.ResponseNotRead):
98
- if isinstance(value, httpx.Response):
99
- stream = (
100
- value.iter_raw(chunk_size=CHUNK_SIZE)
101
- if isinstance(value.stream, Iterable)
102
- else value.aiter_raw(chunk_size=CHUNK_SIZE)
103
- )
104
- else:
105
- stream = value.stream # type: ignore
106
- if isinstance(value, httpx.Request):
107
- return Request(
108
- method=value.method,
109
- url=str(value.url),
110
- headers=Headers({key: value for key, value in value.headers.items()}),
111
- stream=stream,
112
- metadata={
113
- "hishel_refresh_ttl_on_access": value.extensions.get("hishel_refresh_ttl_on_access"),
114
- "hishel_ttl": value.extensions.get("hishel_ttl"),
115
- "hishel_spec_ignore": value.extensions.get("hishel_spec_ignore"),
116
- },
117
- )
118
- elif isinstance(value, httpx.Response):
119
- return Response(
120
- status_code=value.status_code,
121
- headers=Headers({key: value for key, value in value.headers.items()}),
122
- stream=stream,
123
- metadata={},
124
- )
125
-
126
-
127
- class SyncCacheTransport(httpx.BaseTransport):
128
- def __init__(
129
- self,
130
- next_transport: httpx.BaseTransport,
131
- storage: SyncBaseStorage | None = None,
132
- cache_options: CacheOptions | None = None,
133
- ignore_specification: bool = False,
134
- ) -> None:
135
- self.next_transport = next_transport
136
- self._cache_proxy: SyncCacheProxy = SyncCacheProxy(
137
- send_request=self.sync_send_request,
138
- storage=storage,
139
- cache_options=cache_options,
140
- ignore_specification=ignore_specification,
141
- )
142
- self.storage = self._cache_proxy.storage
143
-
144
- def handle_request(
145
- self,
146
- request: httpx.Request,
147
- ) -> httpx.Response:
148
- internal_request = httpx_to_internal(request)
149
- internal_response = self._cache_proxy.handle_request(internal_request)
150
- response = internal_to_httpx(internal_response)
151
- return response
152
-
153
- def close(self) -> None:
154
- self.next_transport.close()
155
- self.storage.close()
156
- super().close()
157
-
158
- def sync_send_request(self, request: Request) -> Response:
159
- httpx_request = internal_to_httpx(request)
160
- httpx_response = self.next_transport.handle_request(httpx_request)
161
- return httpx_to_internal(httpx_response)
162
-
163
-
164
- class SyncCacheClient(httpx.Client):
165
- @overload
166
- def __init__(
167
- self,
168
- *,
169
- storage: SyncBaseStorage | None = None,
170
- cache_options: CacheOptions | None = None,
171
- **kwargs: t.Any,
172
- ) -> None: ...
173
- @overload
174
- def __init__(
175
- self,
176
- *args: t.Any,
177
- **kwargs: t.Any,
178
- ) -> None: ...
179
- def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
180
- self.storage: SyncBaseStorage | None = kwargs.pop("storage", None)
181
- self.cache_options: CacheOptions | None = kwargs.pop("cache_options", None)
182
- super().__init__(*args, **kwargs)
183
-
184
- def _init_transport(
185
- self,
186
- verify: ssl.SSLContext | str | bool = True,
187
- cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None,
188
- trust_env: bool = True,
189
- http1: bool = True,
190
- http2: bool = False,
191
- limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20),
192
- transport: httpx.BaseTransport | None = None,
193
- **kwargs: t.Any,
194
- ) -> httpx.BaseTransport:
195
- if transport is not None:
196
- return transport
197
-
198
- return SyncCacheTransport(
199
- next_transport=httpx.HTTPTransport(
200
- verify=verify,
201
- cert=cert,
202
- trust_env=trust_env,
203
- http1=http1,
204
- http2=http2,
205
- limits=limits,
206
- ),
207
- storage=self.storage,
208
- cache_options=self.cache_options,
209
- ignore_specification=False,
210
- )
211
-
212
- def _init_proxy_transport(
213
- self,
214
- proxy: httpx.Proxy,
215
- verify: ssl.SSLContext | str | bool = True,
216
- cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None,
217
- trust_env: bool = True,
218
- http1: bool = True,
219
- http2: bool = False,
220
- limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20),
221
- **kwargs: t.Any,
222
- ) -> httpx.BaseTransport:
223
- return SyncCacheTransport(
224
- next_transport=httpx.HTTPTransport(
225
- verify=verify,
226
- cert=cert,
227
- trust_env=trust_env,
228
- http1=http1,
229
- http2=http2,
230
- limits=limits,
231
- proxy=proxy,
232
- ),
233
- storage=self.storage,
234
- cache_options=self.cache_options,
235
- ignore_specification=False,
236
- )
237
-
238
-
239
- class AsyncCacheTransport(httpx.AsyncBaseTransport):
240
- def __init__(
241
- self,
242
- next_transport: httpx.AsyncBaseTransport,
243
- storage: AsyncBaseStorage | None = None,
244
- cache_options: CacheOptions | None = None,
245
- ignore_specification: bool = False,
246
- ) -> None:
247
- self.next_transport = next_transport
248
- self._cache_proxy: AsyncCacheProxy = AsyncCacheProxy(
249
- send_request=self.async_send_request,
250
- storage=storage,
251
- cache_options=cache_options,
252
- ignore_specification=ignore_specification,
253
- )
254
- self.storage = self._cache_proxy.storage
255
-
256
- async def handle_async_request(
257
- self,
258
- request: httpx.Request,
259
- ) -> httpx.Response:
260
- internal_request = httpx_to_internal(request)
261
- internal_response = await self._cache_proxy.handle_request(internal_request)
262
- response = internal_to_httpx(internal_response)
263
- return response
264
-
265
- async def aclose(self) -> None:
266
- await self.next_transport.aclose()
267
- await self.storage.close()
268
- await super().aclose()
269
-
270
- async def async_send_request(self, request: Request) -> Response:
271
- httpx_request = internal_to_httpx(request)
272
- httpx_response = await self.next_transport.handle_async_request(httpx_request)
273
- return httpx_to_internal(httpx_response)
274
-
275
-
276
- class AsyncCacheClient(httpx.AsyncClient):
277
- def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
278
- self.storage: AsyncBaseStorage | None = kwargs.pop("storage", None)
279
- self.cache_options: CacheOptions | None = kwargs.pop("cache_options", None)
280
- self.ignore_specification: bool = kwargs.pop("ignore_specification", False)
281
- super().__init__(*args, **kwargs)
282
-
283
- def _init_transport(
284
- self,
285
- verify: ssl.SSLContext | str | bool = True,
286
- cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None,
287
- trust_env: bool = True,
288
- http1: bool = True,
289
- http2: bool = False,
290
- limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20),
291
- transport: httpx.AsyncBaseTransport | None = None,
292
- **kwargs: t.Any,
293
- ) -> httpx.AsyncBaseTransport:
294
- if transport is not None:
295
- return transport
296
-
297
- return AsyncCacheTransport(
298
- next_transport=httpx.AsyncHTTPTransport(
299
- verify=verify,
300
- cert=cert,
301
- trust_env=trust_env,
302
- http1=http1,
303
- http2=http2,
304
- limits=limits,
305
- ),
306
- storage=self.storage,
307
- cache_options=self.cache_options,
308
- ignore_specification=False,
309
- )
310
10
 
311
- def _init_proxy_transport(
312
- self,
313
- proxy: httpx.Proxy,
314
- verify: ssl.SSLContext | str | bool = True,
315
- cert: t.Union[str, t.Tuple[str, str], t.Tuple[str, str, str], None] = None,
316
- trust_env: bool = True,
317
- http1: bool = True,
318
- http2: bool = False,
319
- limits: httpx.Limits = httpx.Limits(max_connections=100, max_keepalive_connections=20),
320
- **kwargs: t.Any,
321
- ) -> httpx.AsyncBaseTransport:
322
- return AsyncCacheTransport(
323
- next_transport=httpx.AsyncHTTPTransport(
324
- verify=verify,
325
- cert=cert,
326
- trust_env=trust_env,
327
- http1=http1,
328
- http2=http2,
329
- limits=limits,
330
- proxy=proxy,
331
- ),
332
- storage=self.storage,
333
- cache_options=self.cache_options,
334
- ignore_specification=self.ignore_specification,
335
- )
11
+ from ._async_httpx import AsyncCacheClient as AsyncCacheClient, AsyncCacheTransport as AsyncCacheTransport
12
+ from ._sync_httpx import SyncCacheClient as SyncCacheClient, SyncCacheTransport as SyncCacheTransport
hishel/requests.py CHANGED
@@ -6,8 +6,8 @@ from typing import Any, Iterator, Mapping, Optional, overload
6
6
  from typing_extensions import assert_never
7
7
 
8
8
  from hishel import Headers, Request, Response as Response
9
- from hishel._core._base._storages._base import SyncBaseStorage
10
9
  from hishel._core._spec import CacheOptions
10
+ from hishel._core._storages._sync_base import SyncBaseStorage
11
11
  from hishel._core.models import extract_metadata_from_headers
12
12
  from hishel._sync_cache import SyncCacheProxy
13
13
  from hishel._utils import snake_to_header
@@ -27,7 +27,7 @@ except ImportError: # pragma: no cover
27
27
  CHUNK_SIZE = 131072
28
28
 
29
29
 
30
- class IteratorStream(RawIOBase):
30
+ class _IteratorStream(RawIOBase):
31
31
  def __init__(self, iterator: Iterator[bytes]):
32
32
  self.iterator = iterator
33
33
  self.leftover = b""
@@ -61,18 +61,18 @@ class IteratorStream(RawIOBase):
61
61
 
62
62
 
63
63
  @overload
64
- def requests_to_internal(
64
+ def _requests_to_internal(
65
65
  model: requests.models.PreparedRequest,
66
66
  ) -> Request: ...
67
67
 
68
68
 
69
69
  @overload
70
- def requests_to_internal(
70
+ def _requests_to_internal(
71
71
  model: requests.models.Response,
72
72
  ) -> Response: ...
73
73
 
74
74
 
75
- def requests_to_internal(
75
+ def _requests_to_internal(
76
76
  model: requests.models.PreparedRequest | requests.models.Response,
77
77
  ) -> Request | Response:
78
78
  if isinstance(model, requests.models.PreparedRequest):
@@ -108,19 +108,24 @@ def requests_to_internal(
108
108
 
109
109
 
110
110
  @overload
111
- def internal_to_requests(model: Request) -> requests.models.PreparedRequest: ...
111
+ def _internal_to_requests(model: Request) -> requests.models.PreparedRequest: ...
112
112
  @overload
113
- def internal_to_requests(model: Response) -> requests.models.Response: ...
114
- def internal_to_requests(model: Request | Response) -> requests.models.Response | requests.models.PreparedRequest:
113
+ def _internal_to_requests(model: Response) -> requests.models.Response: ...
114
+ def _internal_to_requests(
115
+ model: Request | Response,
116
+ ) -> requests.models.Response | requests.models.PreparedRequest:
115
117
  if isinstance(model, Response):
116
118
  response = requests.models.Response()
117
119
 
118
120
  assert isinstance(model.stream, Iterator)
119
- stream = IteratorStream(model.stream)
121
+ stream = _IteratorStream(model.stream)
120
122
 
121
123
  urllib_response = HTTPResponse(
122
124
  body=stream,
123
- headers={**model.headers, **{snake_to_header(k): str(v) for k, v in model.metadata.items()}},
125
+ headers={
126
+ **model.headers,
127
+ **{snake_to_header(k): str(v) for k, v in model.metadata.items()},
128
+ },
124
129
  status=model.status_code,
125
130
  preload_content=False,
126
131
  decode_content=False,
@@ -163,7 +168,7 @@ class CacheAdapter(HTTPAdapter):
163
168
  ):
164
169
  super().__init__(pool_connections, pool_maxsize, max_retries, pool_block)
165
170
  self._cache_proxy = SyncCacheProxy(
166
- send_request=self.send_request,
171
+ request_sender=self._send_request,
167
172
  storage=storage,
168
173
  cache_options=cache_options,
169
174
  ignore_specification=ignore_specification,
@@ -179,9 +184,9 @@ class CacheAdapter(HTTPAdapter):
179
184
  cert: None | bytes | str | tuple[bytes | str, bytes | str] = None,
180
185
  proxies: Mapping[str, str] | None = None,
181
186
  ) -> requests.models.Response:
182
- internal_request = requests_to_internal(request)
187
+ internal_request = _requests_to_internal(request)
183
188
  internal_response = self._cache_proxy.handle_request(internal_request)
184
- response = internal_to_requests(internal_response)
189
+ response = _internal_to_requests(internal_response)
185
190
 
186
191
  # Set the original request on the response
187
192
  response.request = request
@@ -189,10 +194,13 @@ class CacheAdapter(HTTPAdapter):
189
194
 
190
195
  return response
191
196
 
192
- def send_request(self, request: Request) -> Response:
193
- requests_request = internal_to_requests(request)
194
- response = super().send(requests_request, stream=True)
195
- return requests_to_internal(response)
197
+ def _send_request(self, request: Request) -> Response:
198
+ requests_request = _internal_to_requests(request)
199
+ response = super().send(
200
+ requests_request,
201
+ stream=True,
202
+ )
203
+ return _requests_to_internal(response)
196
204
 
197
205
  def close(self) -> Any:
198
206
  self.storage.close()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hishel
3
- Version: 1.0.0.dev1
3
+ Version: 1.0.0.dev3
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,15 @@ 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 Requests
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
85
  - 🎛️ **Configurable** - Fine-grained control over caching behavior
83
- - 🌐 **Future Ready** - Designed for easy integration with any HTTP client/server
86
+ - **Memory Efficient** - Streaming support prevents loading large payloads into memory
87
+ - 🌐 **Universal** - Works with any ASGI application (Starlette, Litestar, BlackSheep, etc.)
84
88
 
85
89
  ## 📦 Installation
86
90
 
@@ -90,19 +94,23 @@ pip install hishel
90
94
 
91
95
  ### Optional Dependencies
92
96
 
93
- Install with specific HTTP client support:
97
+ Install with specific integration support:
94
98
 
95
99
  ```bash
96
100
  pip install hishel[httpx] # For HTTPX support
97
101
  pip install hishel[requests] # For Requests support
102
+ pip install hishel[fastapi] # For FastAPI support (includes ASGI)
98
103
  ```
99
104
 
100
- Or install both:
105
+ Or install multiple:
101
106
 
102
107
  ```bash
103
- pip install hishel[httpx,requests]
108
+ pip install hishel[httpx,requests,fastapi]
104
109
  ```
105
110
 
111
+ > [!NOTE]
112
+ > ASGI middleware has no extra dependencies - it's included in the base installation.
113
+
106
114
  ## 🚀 Quick Start
107
115
 
108
116
  ### With HTTPX
@@ -156,6 +164,65 @@ response = session.get("https://api.example.com/data")
156
164
  print(response.headers.get("X-Hishel-From-Cache")) # "True"
157
165
  ```
158
166
 
167
+ ### With ASGI Applications
168
+
169
+ Add caching middleware to any ASGI application:
170
+
171
+ ```python
172
+ from hishel.asgi import ASGICacheMiddleware
173
+
174
+ # Wrap your ASGI app
175
+ app = ASGICacheMiddleware(app)
176
+
177
+ # Or configure with options
178
+ from hishel import AsyncSqliteStorage, CacheOptions
179
+
180
+ app = ASGICacheMiddleware(
181
+ app,
182
+ storage=AsyncSqliteStorage(),
183
+ cache_options=CacheOptions(shared=True)
184
+ )
185
+ ```
186
+
187
+ ### With FastAPI
188
+
189
+ Add Cache-Control headers using the `cache()` dependency:
190
+
191
+ ```python
192
+ from fastapi import FastAPI
193
+ from hishel.fastapi import cache
194
+
195
+ app = FastAPI()
196
+
197
+ @app.get("/api/data", dependencies=[cache(max_age=300, public=True)])
198
+ async def get_data():
199
+ # Cache-Control: public, max-age=300
200
+ return {"data": "cached for 5 minutes"}
201
+
202
+ # Optionally wrap with ASGI middleware for local caching according to specified rules
203
+ from hishel.asgi import ASGICacheMiddleware
204
+ from hishel import AsyncSqliteStorage
205
+
206
+ app = ASGICacheMiddleware(app, storage=AsyncSqliteStorage())
207
+ ```
208
+
209
+ ### With BlackSheep
210
+
211
+ Use BlackSheep's native `cache_control` decorator with Hishel's ASGI middleware:
212
+
213
+ ```python
214
+ from blacksheep import Application, get
215
+ from blacksheep.server.headers.cache import cache_control
216
+
217
+ app = Application()
218
+
219
+ @get("/api/data")
220
+ @cache_control(max_age=300, public=True)
221
+ async def get_data():
222
+ # Cache-Control: public, max-age=300
223
+ return {"data": "cached for 5 minutes"}
224
+ ```
225
+
159
226
  ## 🎛️ Advanced Configuration
160
227
 
161
228
  ### Custom Cache Options
@@ -214,7 +281,11 @@ Comprehensive documentation is available at [https://hishel.com/dev](https://his
214
281
  - [Getting Started](https://hishel.com)
215
282
  - [HTTPX Integration](https://hishel.com/dev/integrations/httpx)
216
283
  - [Requests Integration](https://hishel.com/dev/integrations/requests)
284
+ - [ASGI Integration](https://hishel.com/dev/asgi)
285
+ - [FastAPI Integration](https://hishel.com/dev/fastapi)
286
+ - [BlackSheep Integration](https://hishel.com/dev/integrations/blacksheep)
217
287
  - [Storage Backends](https://hishel.com/dev/storages)
288
+ - [Request/Response Metadata](https://hishel.com/dev/metadata)
218
289
  - [RFC 9111 Specification](https://hishel.com/dev/specification)
219
290
 
220
291
  ## 🤝 Contributing
@@ -255,44 +326,85 @@ Hishel is inspired by and builds upon the excellent work in the Python HTTP ecos
255
326
 
256
327
  All notable changes to this project will be documented in this file.
257
328
 
258
- ## 1.0.0dev1 - 2025-10-21
259
- ### <!-- 7 -->⚙️ Miscellaneous Tasks
329
+ ## 1.0.0.dev3 - 2025-10-26
330
+ ### ♻️ Refactoring
331
+ - Replace pairs with entries, simplify storage API
332
+ - Automatically generate httpx sync integration from async
333
+
334
+ ### ⚙️ Miscellaneous Tasks
335
+ - Simplify metadata docs
336
+ - Add custom integrations docs
337
+ - More robust compressed response caching
338
+
339
+ ### 🐛 Bug Fixes
340
+ - Add missing permissions into `publish.yml`
341
+ - Raise on consumed httpx streams, which we can't store as is (it's already decoded)
342
+ - Fix compressed data caching for requests
343
+ - Handle httpx iterable usage instead of iterator correctly
344
+ - Add date header for proper age calculation
345
+
346
+ ### 🚀 Features
347
+ - Add integrations with fastapi and asgi
348
+ - Add blacksheep integration examples
349
+ - Add logging for asgi
350
+
351
+ ## 1.0.0.dev2 - 2025-10-21
352
+ ### ⚙️ Miscellaneous Tasks
353
+ - Remove redundant utils and tests
354
+ - Add import without extras check in ci
355
+ - Fix time travel date, explicitly specify the timezone
356
+
357
+ ### 🐛 Bug Fixes
358
+ - Fix check for storing auth requests
359
+ - Don't raise an error on 3xx during revalidation
360
+
361
+ ### 🚀 Features
362
+ - Add hishel_created_at response metadata
363
+
364
+ ## 1.0.0.dev1 - 2025-10-21
365
+ ### ⚙️ Miscellaneous Tasks
260
366
  - Remove some redundant utils methods
261
367
 
368
+ ### 📦 Dependencies
369
+ - Make httpx and async libs optional dependencies
370
+ - Make `anysqlite` optional dependency
371
+ - Install async extra with httpx
372
+ - Improve git-cliff
373
+
262
374
  ## 1.0.0.dev0 - 2025-10-19
263
- ### <!-- 7 -->⚙️ Miscellaneous Tasks
375
+ ### ⚙️ Miscellaneous Tasks
264
376
  - Use mike powered versioning
265
377
  - Improve docs versioning, deploy dev doc on ci
266
378
 
267
379
  ## 0.1.5 - 2025-10-18
268
- ### <!-- 0 -->🚀 Features
380
+ ### ⚙️ Miscellaneous Tasks
381
+ - Remove some redundant files from repo
382
+
383
+ ### 🐛 Bug Fixes
384
+ - Fix some line breaks
385
+
386
+ ### 🚀 Features
269
387
  - Set chunk size to 128KB for httpx to reduce SQLite read/writes
270
388
  - Better cache-control parsing
271
389
  - Add close method to storages API (#384)
272
390
  - Increase requests buffer size to 128KB, disable charset detection
273
391
 
274
- ### <!-- 1 -->🐛 Bug Fixes
275
- - Fix some line breaks
392
+ ## 0.1.4 - 2025-10-14
393
+ ### ⚙️ Miscellaneous Tasks
394
+ - Improve CI (#369)
395
+ - Remove src folder (#373)
396
+ - Temporary remove python3.14 from CI
397
+ - Add sqlite tests for new storage
398
+ - Move some tests to beta
276
399
 
277
- ### <!-- 7 -->⚙️ Miscellaneous Tasks
278
- - Remove some redundant files from repo
400
+ ### 🐛 Bug Fixes
401
+ - Create an sqlite file in a cache folder
402
+ - Fix beta imports
279
403
 
280
- ## 0.1.4 - 2025-10-14
281
- ### <!-- 0 -->🚀 Features
404
+ ### 🚀 Features
282
405
  - Add support for a sans-IO API (#366)
283
406
  - Allow already consumed streams with `CacheTransport` (#377)
284
407
  - Add sqlite storage for beta storages
285
408
  - Get rid of some locks from sqlite storage
286
409
  - Better async implemetation for sqlite storage
287
410
 
288
- ### <!-- 1 -->🐛 Bug Fixes
289
- - Create an sqlite file in a cache folder
290
- - Fix beta imports
291
-
292
- ### <!-- 7 -->⚙️ Miscellaneous Tasks
293
- - Improve CI (#369)
294
- - Remove src folder (#373)
295
- - Temporary remove python3.14 from CI
296
- - Add sqlite tests for new storage
297
- - Move some tests to beta
298
-