hishel 0.1.4__py3-none-any.whl → 1.0.0.dev0__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.
Files changed (42) hide show
  1. hishel/__init__.py +55 -53
  2. hishel/{beta/_async_cache.py → _async_cache.py} +3 -3
  3. hishel/{beta → _core}/__init__.py +6 -6
  4. hishel/{beta/_core → _core}/_async/_storages/_sqlite.py +3 -3
  5. hishel/{beta/_core → _core}/_base/_storages/_base.py +13 -1
  6. hishel/{beta/_core → _core}/_base/_storages/_packing.py +5 -5
  7. hishel/_core/_headers.py +636 -0
  8. hishel/{beta/_core → _core}/_spec.py +89 -2
  9. hishel/{beta/_core → _core}/_sync/_storages/_sqlite.py +3 -3
  10. hishel/{beta/_core → _core}/models.py +1 -1
  11. hishel/{beta/_sync_cache.py → _sync_cache.py} +3 -3
  12. hishel/{beta/httpx.py → httpx.py} +18 -7
  13. hishel/{beta/requests.py → requests.py} +15 -10
  14. hishel-1.0.0.dev0.dist-info/METADATA +321 -0
  15. hishel-1.0.0.dev0.dist-info/RECORD +19 -0
  16. hishel/_async/__init__.py +0 -5
  17. hishel/_async/_client.py +0 -30
  18. hishel/_async/_mock.py +0 -43
  19. hishel/_async/_pool.py +0 -201
  20. hishel/_async/_storages.py +0 -768
  21. hishel/_async/_transports.py +0 -282
  22. hishel/_controller.py +0 -581
  23. hishel/_exceptions.py +0 -10
  24. hishel/_files.py +0 -54
  25. hishel/_headers.py +0 -215
  26. hishel/_lfu_cache.py +0 -71
  27. hishel/_lmdb_types_.pyi +0 -53
  28. hishel/_s3.py +0 -122
  29. hishel/_serializers.py +0 -329
  30. hishel/_sync/__init__.py +0 -5
  31. hishel/_sync/_client.py +0 -30
  32. hishel/_sync/_mock.py +0 -43
  33. hishel/_sync/_pool.py +0 -201
  34. hishel/_sync/_storages.py +0 -768
  35. hishel/_sync/_transports.py +0 -282
  36. hishel/_synchronization.py +0 -37
  37. hishel/beta/_core/__init__.py +0 -0
  38. hishel/beta/_core/_headers.py +0 -301
  39. hishel-0.1.4.dist-info/METADATA +0 -404
  40. hishel-0.1.4.dist-info/RECORD +0 -41
  41. {hishel-0.1.4.dist-info → hishel-1.0.0.dev0.dist-info}/WHEEL +0 -0
  42. {hishel-0.1.4.dist-info → hishel-1.0.0.dev0.dist-info}/licenses/LICENSE +0 -0
@@ -15,11 +15,11 @@ from typing import (
15
15
  Union,
16
16
  )
17
17
 
18
+ from hishel._core._headers import Headers, Range, Vary, parse_cache_control
18
19
  from hishel._utils import parse_date, partition
19
- from hishel.beta._core._headers import Headers, Range, Vary, parse_cache_control
20
20
 
21
21
  if TYPE_CHECKING:
22
- from hishel.beta import CompletePair, Request, Response
22
+ from hishel import CompletePair, Request, Response
23
23
 
24
24
 
25
25
  TState = TypeVar("TState", bound="State")
@@ -41,9 +41,96 @@ logger = logging.getLogger("hishel.core.spec")
41
41
 
42
42
  @dataclass
43
43
  class CacheOptions:
44
+ """
45
+ Configuration options for HTTP cache behavior.
46
+
47
+ These options control how the cache interprets and applies RFC 9111 caching rules.
48
+ All options have sensible defaults that follow the specification.
49
+
50
+ Attributes:
51
+ ----------
52
+ shared : bool
53
+ Determines whether the cache operates as a shared cache or private cache.
54
+
55
+ RFC 9111 Section 3.5: Authenticated Responses
56
+ https://www.rfc-editor.org/rfc/rfc9111.html#section-3.5
57
+
58
+ - Shared cache (True): Acts as a proxy, CDN, or gateway cache serving multiple users.
59
+ Must respect private directives and Authorization header restrictions.
60
+ Can use s-maxage directive instead of max-age for shared-specific freshness.
61
+
62
+ - Private cache (False): Acts as a browser or user-agent cache for a single user.
63
+ Can cache private responses and ignore s-maxage directives.
64
+
65
+ Default: True (shared cache)
66
+
67
+ Examples:
68
+ --------
69
+ >>> # Shared cache (proxy/CDN)
70
+ >>> options = CacheOptions(shared=True)
71
+
72
+ >>> # Private cache (browser)
73
+ >>> options = CacheOptions(shared=False)
74
+
75
+ supported_methods : list[str]
76
+ HTTP methods that are allowed to be cached by this cache implementation.
77
+
78
+ RFC 9111 Section 3, paragraph 2.1:
79
+ https://www.rfc-editor.org/rfc/rfc9111.html#section-3-2.1.1
80
+
81
+ "A cache MUST NOT store a response to a request unless:
82
+ - the request method is understood by the cache"
83
+
84
+ Default: ["GET", "HEAD"] (most commonly cached methods)
85
+
86
+ Examples:
87
+ --------
88
+ >>> # Default: cache GET and HEAD only
89
+ >>> options = CacheOptions()
90
+ >>> options.supported_methods
91
+ ['GET', 'HEAD']
92
+
93
+ >>> # Cache POST responses (advanced use case)
94
+ >>> options = CacheOptions(supported_methods=["GET", "HEAD", "POST"])
95
+
96
+ allow_stale : bool
97
+ Controls whether stale responses can be served without revalidation.
98
+
99
+ RFC 9111 Section 4.2.4: Serving Stale Responses
100
+ https://www.rfc-editor.org/rfc/rfc9111.html#section-4.2.4
101
+
102
+ "A cache MUST NOT generate a stale response unless it is disconnected or
103
+ doing so is explicitly permitted by the client or origin server (e.g., by
104
+ the max-stale request directive in Section 5.2.1, extension directives
105
+ such as those defined in [RFC5861], or configuration in accordance with
106
+ an out-of-band contract)."
107
+
108
+ Default: False (no stale responses)
109
+
110
+ Examples:
111
+ --------
112
+ >>> # Conservative: never serve stale
113
+ >>> options = CacheOptions(allow_stale=False)
114
+
115
+ >>> # Permissive: serve stale when allowed
116
+ >>> options = CacheOptions(allow_stale=True)
117
+
118
+ >>> # Stale-while-revalidate pattern (RFC 5861)
119
+ >>> # Even with allow_stale=True, directives are respected
120
+ >>> options = CacheOptions(allow_stale=True)
121
+ """
122
+
44
123
  shared: bool = True
124
+ """
125
+ When True, the cache operates as a shared cache (proxy/CDN).
126
+ When False, as a private cache (browser).
127
+ """
128
+
45
129
  supported_methods: list[str] = field(default_factory=lambda: ["GET", "HEAD"])
130
+ """HTTP methods that are allowed to be cached."""
131
+
46
132
  allow_stale: bool = False
133
+ """When True, stale responses can be served without revalidation."""
47
134
 
48
135
 
49
136
  @dataclass
@@ -15,9 +15,9 @@ from typing import (
15
15
 
16
16
  import sqlite3
17
17
 
18
- from hishel.beta._core._base._storages._base import SyncBaseStorage, ensure_cache_dict
19
- from hishel.beta._core._base._storages._packing import pack, unpack
20
- from hishel.beta._core.models import (
18
+ from hishel._core._base._storages._base import SyncBaseStorage, ensure_cache_dict
19
+ from hishel._core._base._storages._packing import pack, unpack
20
+ from hishel._core.models import (
21
21
  CompletePair,
22
22
  IncompletePair,
23
23
  Pair,
@@ -12,7 +12,7 @@ from typing import (
12
12
  TypedDict,
13
13
  )
14
14
 
15
- from hishel.beta._core._headers import Headers
15
+ from hishel._core._headers import Headers
16
16
 
17
17
 
18
18
  class AnyIterable:
@@ -8,7 +8,7 @@ from typing import Iterator, Awaitable, Callable
8
8
 
9
9
  from typing_extensions import assert_never
10
10
 
11
- from hishel.beta import (
11
+ from hishel import (
12
12
  AnyState,
13
13
  SyncBaseStorage,
14
14
  SyncSqliteStorage,
@@ -24,8 +24,8 @@ from hishel.beta import (
24
24
  StoreAndUse,
25
25
  create_idle_state,
26
26
  )
27
- from hishel.beta._core._spec import InvalidatePairs, vary_headers_match
28
- from hishel.beta._core.models import CompletePair
27
+ from hishel._core._spec import InvalidatePairs, vary_headers_match
28
+ from hishel._core.models import CompletePair
29
29
 
30
30
  logger = logging.getLogger("hishel.integrations.clients")
31
31
 
@@ -6,14 +6,14 @@ from typing import AsyncIterator, Iterable, Iterator, Union, overload
6
6
 
7
7
  import httpx
8
8
 
9
- from hishel.beta import Headers, Request, Response
10
- from hishel.beta._async_cache import AsyncCacheProxy
11
- from hishel.beta._core._base._storages._base import AsyncBaseStorage, SyncBaseStorage
12
- from hishel.beta._core._spec import (
9
+ from hishel import Headers, Request, Response
10
+ from hishel._async_cache import AsyncCacheProxy
11
+ from hishel._core._base._storages._base import AsyncBaseStorage, SyncBaseStorage
12
+ from hishel._core._spec import (
13
13
  CacheOptions,
14
14
  )
15
- from hishel.beta._core.models import AnyIterable
16
- from hishel.beta._sync_cache import SyncCacheProxy
15
+ from hishel._core.models import AnyIterable
16
+ from hishel._sync_cache import SyncCacheProxy
17
17
 
18
18
  SOCKET_OPTION = t.Union[
19
19
  t.Tuple[int, int, int],
@@ -21,6 +21,9 @@ SOCKET_OPTION = t.Union[
21
21
  t.Tuple[int, int, None, int],
22
22
  ]
23
23
 
24
+ # 128 KB
25
+ CHUNK_SIZE = 131072
26
+
24
27
 
25
28
  class IteratorStream(httpx.SyncByteStream, httpx.AsyncByteStream):
26
29
  def __init__(self, iterator: Iterator[bytes] | AsyncIterator[bytes]) -> None:
@@ -86,7 +89,11 @@ def httpx_to_internal(
86
89
  stream = AnyIterable(value.content)
87
90
  except (httpx.RequestNotRead, httpx.ResponseNotRead):
88
91
  if isinstance(value, httpx.Response):
89
- stream = value.iter_raw() if isinstance(value.stream, Iterable) else value.aiter_raw()
92
+ stream = (
93
+ value.iter_raw(chunk_size=CHUNK_SIZE)
94
+ if isinstance(value.stream, Iterable)
95
+ else value.aiter_raw(chunk_size=CHUNK_SIZE)
96
+ )
90
97
  else:
91
98
  stream = value.stream # type: ignore
92
99
  if isinstance(value, httpx.Request):
@@ -125,6 +132,7 @@ class SyncCacheTransport(httpx.BaseTransport):
125
132
  cache_options=cache_options,
126
133
  ignore_specification=ignore_specification,
127
134
  )
135
+ self.storage = self._cache_proxy.storage
128
136
 
129
137
  def handle_request(
130
138
  self,
@@ -137,6 +145,7 @@ class SyncCacheTransport(httpx.BaseTransport):
137
145
 
138
146
  def close(self) -> None:
139
147
  self.next_transport.close()
148
+ self.storage.close()
140
149
  super().close()
141
150
 
142
151
  def sync_send_request(self, request: Request) -> Response:
@@ -235,6 +244,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
235
244
  cache_options=cache_options,
236
245
  ignore_specification=ignore_specification,
237
246
  )
247
+ self.storage = self._cache_proxy.storage
238
248
 
239
249
  async def handle_async_request(
240
250
  self,
@@ -247,6 +257,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
247
257
 
248
258
  async def aclose(self) -> None:
249
259
  await self.next_transport.aclose()
260
+ await self.storage.close()
250
261
  await super().aclose()
251
262
 
252
263
  async def async_send_request(self, request: Request) -> Response:
@@ -1,16 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from io import RawIOBase
4
- from typing import Iterator, Mapping, Optional, overload
4
+ from typing import Any, Iterator, Mapping, Optional, overload
5
5
 
6
6
  from typing_extensions import assert_never
7
7
 
8
+ from hishel import Headers, Request, Response as Response
9
+ from hishel._core._base._storages._base import SyncBaseStorage
10
+ from hishel._core._spec import CacheOptions
11
+ from hishel._core.models import extract_metadata_from_headers
12
+ from hishel._sync_cache import SyncCacheProxy
8
13
  from hishel._utils import snake_to_header
9
- from hishel.beta import Headers, Request, Response as Response
10
- from hishel.beta._core._base._storages._base import SyncBaseStorage
11
- from hishel.beta._core._spec import CacheOptions
12
- from hishel.beta._core.models import extract_metadata_from_headers
13
- from hishel.beta._sync_cache import SyncCacheProxy
14
14
 
15
15
  try:
16
16
  import requests
@@ -23,6 +23,9 @@ except ImportError: # pragma: no cover
23
23
  "Install hishel with 'pip install hishel[requests]'."
24
24
  )
25
25
 
26
+ # 128 KB
27
+ CHUNK_SIZE = 131072
28
+
26
29
 
27
30
  class IteratorStream(RawIOBase):
28
31
  def __init__(self, iterator: Iterator[bytes]):
@@ -90,7 +93,7 @@ def requests_to_internal(
90
93
  )
91
94
  elif isinstance(model, requests.models.Response):
92
95
  try:
93
- stream = model.raw.stream(amt=8192)
96
+ stream = model.raw.stream(amt=CHUNK_SIZE, decode_content=None)
94
97
  except requests.exceptions.StreamConsumedError:
95
98
  stream = iter([model.content])
96
99
 
@@ -113,7 +116,6 @@ def internal_to_requests(model: Request | Response) -> requests.models.Response
113
116
  response = requests.models.Response()
114
117
 
115
118
  assert isinstance(model.stream, Iterator)
116
- # Collect all chunks from the internal stream
117
119
  stream = IteratorStream(model.stream)
118
120
 
119
121
  urllib_response = HTTPResponse(
@@ -121,7 +123,7 @@ def internal_to_requests(model: Request | Response) -> requests.models.Response
121
123
  headers={**model.headers, **{snake_to_header(k): str(v) for k, v in model.metadata.items()}},
122
124
  status=model.status_code,
123
125
  preload_content=False,
124
- decode_content=True,
126
+ decode_content=False,
125
127
  )
126
128
 
127
129
  # Set up the response object
@@ -130,7 +132,6 @@ def internal_to_requests(model: Request | Response) -> requests.models.Response
130
132
  response.headers.update(model.headers)
131
133
  response.headers.update({snake_to_header(k): str(v) for k, v in model.metadata.items()})
132
134
  response.url = "" # Will be set by requests
133
- response.encoding = response.apparent_encoding
134
135
 
135
136
  return response
136
137
  else:
@@ -167,6 +168,7 @@ class CacheAdapter(HTTPAdapter):
167
168
  cache_options=cache_options,
168
169
  ignore_specification=ignore_specification,
169
170
  )
171
+ self.storage = self._cache_proxy.storage
170
172
 
171
173
  def send(
172
174
  self,
@@ -191,3 +193,6 @@ class CacheAdapter(HTTPAdapter):
191
193
  requests_request = internal_to_requests(request)
192
194
  response = super().send(requests_request, stream=True)
193
195
  return requests_to_internal(response)
196
+
197
+ def close(self) -> Any:
198
+ self.storage.close()
@@ -0,0 +1,321 @@
1
+ Metadata-Version: 2.4
2
+ Name: hishel
3
+ Version: 1.0.0.dev0
4
+ Summary: Elegant HTTP Caching for Python
5
+ Project-URL: Homepage, https://hishel.com
6
+ Project-URL: Source, https://github.com/karpetrosyan/hishel
7
+ Author-email: Kar Petrosyan <kar.petrosyanpy@gmail.com>
8
+ License-Expression: BSD-3-Clause
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Framework :: Trio
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: BSD License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Internet :: WWW/HTTP
26
+ Requires-Python: >=3.9
27
+ Requires-Dist: anyio>=4.9.0
28
+ Requires-Dist: anysqlite>=0.0.5
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'
34
+ Provides-Extra: requests
35
+ Requires-Dist: requests>=2.32.5; extra == 'requests'
36
+ Description-Content-Type: text/markdown
37
+
38
+ <p align="center">
39
+ <img alt="Hishel Logo" width="350" src="https://raw.githubusercontent.com/karpetrosyan/hishel/master/docs/static/Shelkopryad_350x250_yellow.png#gh-dark-mode-only">
40
+ <img alt="Hishel Logo" width="350" src="https://raw.githubusercontent.com/karpetrosyan/hishel/master/docs/static/Shelkopryad_350x250_black.png#gh-light-mode-only">
41
+ </p>
42
+
43
+ <h1 align="center">Hishel</h1>
44
+
45
+ <p align="center">
46
+ <strong>Elegant HTTP Caching for Python</strong>
47
+ </p>
48
+
49
+ <p align="center">
50
+ <a href="https://pypi.org/project/hishel">
51
+ <img src="https://img.shields.io/pypi/v/hishel.svg" alt="PyPI version">
52
+ </a>
53
+ <a href="https://pypi.org/project/hishel">
54
+ <img src="https://img.shields.io/pypi/pyversions/hishel.svg" alt="Python versions">
55
+ </a>
56
+ <a href="https://github.com/karpetrosyan/hishel/blob/master/LICENSE">
57
+ <img src="https://img.shields.io/pypi/l/hishel" alt="License">
58
+ </a>
59
+ <a href="https://coveralls.io/github/karpetrosyan/hishel">
60
+ <img src="https://img.shields.io/coverallsCoverage/github/karpetrosyan/hishel" alt="Coverage">
61
+ </a>
62
+ <a href="https://static.pepy.tech/badge/hishel/month">
63
+ <img src="https://static.pepy.tech/badge/hishel/month" alt="Downloads">
64
+ </a>
65
+ </p>
66
+
67
+ ---
68
+
69
+ **Hishel** (հիշել, *to remember* in Armenian) is a modern HTTP caching library for Python that implements [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111.html) specifications. It provides seamless caching integration for popular HTTP clients with minimal code changes.
70
+
71
+ ## ✨ Features
72
+
73
+ - 🎯 **RFC 9111 Compliant** - Fully compliant with the latest HTTP caching specification
74
+ - 🔌 **Easy Integration** - Drop-in support for HTTPX and Requests
75
+ - 💾 **Flexible Storage** - SQLite backend with more coming soon
76
+ - ⚡ **High Performance** - Efficient caching with minimal overhead
77
+ - 🔄 **Async & Sync** - Full support for both synchronous and asynchronous workflows
78
+ - 🎨 **Type Safe** - Fully typed with comprehensive type hints
79
+ - 🧪 **Well Tested** - Extensive test coverage and battle-tested
80
+ - 🎛️ **Configurable** - Fine-grained control over caching behavior
81
+ - 🌐 **Future Ready** - Designed for easy integration with any HTTP client/server
82
+
83
+ ## 📦 Installation
84
+
85
+ ```bash
86
+ pip install hishel
87
+ ```
88
+
89
+ ### Optional Dependencies
90
+
91
+ Install with specific HTTP client support:
92
+
93
+ ```bash
94
+ pip install hishel[httpx] # For HTTPX support
95
+ pip install hishel[requests] # For Requests support
96
+ ```
97
+
98
+ Or install both:
99
+
100
+ ```bash
101
+ pip install hishel[httpx,requests]
102
+ ```
103
+
104
+ ## 🚀 Quick Start
105
+
106
+ ### With HTTPX
107
+
108
+ **Synchronous:**
109
+
110
+ ```python
111
+ from hishel.httpx import SyncCacheClient
112
+
113
+ client = SyncCacheClient()
114
+
115
+ # First request - fetches from origin
116
+ response = client.get("https://api.example.com/data")
117
+ print(response.extensions["hishel_from_cache"]) # False
118
+
119
+ # Second request - served from cache
120
+ response = client.get("https://api.example.com/data")
121
+ print(response.extensions["hishel_from_cache"]) # True
122
+ ```
123
+
124
+ **Asynchronous:**
125
+
126
+ ```python
127
+ from hishel.httpx import AsyncCacheClient
128
+
129
+ async with AsyncCacheClient() as client:
130
+ # First request - fetches from origin
131
+ response = await client.get("https://api.example.com/data")
132
+ print(response.extensions["hishel_from_cache"]) # False
133
+
134
+ # Second request - served from cache
135
+ response = await client.get("https://api.example.com/data")
136
+ print(response.extensions["hishel_from_cache"]) # True
137
+ ```
138
+
139
+ ### With Requests
140
+
141
+ ```python
142
+ import requests
143
+ from hishel.requests import CacheAdapter
144
+
145
+ session = requests.Session()
146
+ session.mount("https://", CacheAdapter())
147
+ session.mount("http://", CacheAdapter())
148
+
149
+ # First request - fetches from origin
150
+ response = session.get("https://api.example.com/data")
151
+
152
+ # Second request - served from cache
153
+ response = session.get("https://api.example.com/data")
154
+ print(response.headers.get("X-Hishel-From-Cache")) # "True"
155
+ ```
156
+
157
+ ## 🎛️ Advanced Configuration
158
+
159
+ ### Custom Cache Options
160
+
161
+ ```python
162
+ from hishel import CacheOptions
163
+ from hishel.httpx import SyncCacheClient
164
+
165
+ client = SyncCacheClient(
166
+ cache_options=CacheOptions(
167
+ shared=False, # Use as private cache (browser-like)
168
+ supported_methods=["GET", "HEAD", "POST"], # Cache GET, HEAD, and POST
169
+ allow_stale=True # Allow serving stale responses
170
+ )
171
+ )
172
+ ```
173
+
174
+ ### Custom Storage Backend
175
+
176
+ ```python
177
+ from hishel import SyncSqliteStorage
178
+ from hishel.httpx import SyncCacheClient
179
+
180
+ storage = SyncSqliteStorage(
181
+ database_path="my_cache.db",
182
+ default_ttl=7200.0, # Cache entries expire after 2 hours
183
+ refresh_ttl_on_access=True # Reset TTL when accessing cached entries
184
+ )
185
+
186
+ client = SyncCacheClient(storage=storage)
187
+ ```
188
+
189
+ ## 🏗️ Architecture
190
+
191
+ Hishel uses a **sans-I/O state machine** architecture that separates HTTP caching logic from I/O operations:
192
+
193
+ - ✅ **Correct** - Fully RFC 9111 compliant
194
+ - ✅ **Testable** - Easy to test without network dependencies
195
+ - ✅ **Flexible** - Works with any HTTP client or server
196
+ - ✅ **Type Safe** - Clear state transitions with full type hints
197
+
198
+ ## 🔮 Roadmap
199
+
200
+ While Hishel currently supports HTTPX and Requests, we're actively working on:
201
+
202
+ - 🎯 Additional HTTP client integrations
203
+ - 🎯 Server-side caching support
204
+ - 🎯 More storage backends
205
+ - 🎯 Advanced caching strategies
206
+ - 🎯 Performance optimizations
207
+
208
+ ## 📚 Documentation
209
+
210
+ Comprehensive documentation is available at [https://hishel.com/dev](https://hishel.com/dev)
211
+
212
+ - [Getting Started](https://hishel.com)
213
+ - [HTTPX Integration](https://hishel.com/dev/integrations/httpx)
214
+ - [Requests Integration](https://hishel.com/dev/integrations/requests)
215
+ - [Storage Backends](https://hishel.com/dev/storages)
216
+ - [RFC 9111 Specification](https://hishel.com/dev/specification)
217
+
218
+ ## 🤝 Contributing
219
+
220
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
221
+
222
+ See our [Contributing Guide](https://hishel.com/dev/contributing) for more details.
223
+
224
+ ## 📄 License
225
+
226
+ This project is licensed under the BSD-3-Clause License - see the [LICENSE](LICENSE) file for details.
227
+
228
+ ## 💖 Support
229
+
230
+ If you find Hishel useful, please consider:
231
+
232
+ - ⭐ Starring the repository
233
+ - 🐛 Reporting bugs and issues
234
+ - 💡 Suggesting new features
235
+ - 📖 Improving documentation
236
+ - ☕ [Buying me a coffee](https://buymeacoffee.com/karpetrosyan)
237
+
238
+ ## 🙏 Acknowledgments
239
+
240
+ Hishel is inspired by and builds upon the excellent work in the Python HTTP ecosystem, particularly:
241
+
242
+ - [HTTPX](https://github.com/encode/httpx) - A next-generation HTTP client for Python
243
+ - [Requests](https://github.com/psf/requests) - The classic HTTP library for Python
244
+ - [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111.html) - HTTP Caching specification
245
+
246
+ ---
247
+
248
+ <p align="center">
249
+ <strong>Made with ❤️ by <a href="https://github.com/karpetrosyan">Kar Petrosyan</a></strong>
250
+ </p>
251
+
252
+ ## [1.0.0dev0] - 2025-10-19
253
+
254
+ ### ⚙️ Miscellaneous Tasks
255
+
256
+ - *(docs)* Use mike powered versioning
257
+ - *(docs)* Improve docs versioning, deploy dev doc on ci
258
+ ## [0.1.5] - 2025-10-18
259
+
260
+ ### 🚀 Features
261
+
262
+ - *(perf)* Set chunk size to 128KB for httpx to reduce SQLite read/writes
263
+ - Better cache-control parsing
264
+ - Add close method to storages API (#384)
265
+ - *(perf)* Increase requests buffer size to 128KB, disable charset detection
266
+
267
+ ### 🐛 Bug Fixes
268
+
269
+ - *(docs)* Fix some line breaks
270
+
271
+ ### ⚙️ Miscellaneous Tasks
272
+
273
+ - Remove some redundant files from repo
274
+ ## [0.1.4] - 2025-10-14
275
+
276
+ ### 🚀 Features
277
+
278
+ - Add support for a sans-IO API (#366)
279
+ - Allow already consumed streams with `CacheTransport` (#377)
280
+ - Add sqlite storage for beta storages
281
+ - Get rid of some locks from sqlite storage
282
+ - Better async implemetation for sqlite storage
283
+
284
+ ### 🐛 Bug Fixes
285
+
286
+ - Create an sqlite file in a cache folder
287
+ - Fix beta imports
288
+
289
+ ### ⚙️ Miscellaneous Tasks
290
+
291
+ - Improve CI (#369)
292
+ - *(internal)* Remove src folder (#373)
293
+ - *(internal)* Temporary remove python3.14 from CI
294
+ - *(tests)* Add sqlite tests for new storage
295
+ - *(tests)* Move some tests to beta
296
+ ## [0.1.3] - 2025-07-06
297
+
298
+ ### 🚀 Features
299
+
300
+ - Support providing a path prefix to S3 storage (#342)
301
+
302
+ ### 📚 Documentation
303
+
304
+ - Update link to httpx transports page (#337)
305
+ ## [0.1.2] - 2025-04-04
306
+
307
+ ### 🐛 Bug Fixes
308
+
309
+ - Requirements.txt to reduce vulnerabilities (#263)
310
+ ## [0.0.30] - 2024-07-12
311
+
312
+ ### 🐛 Bug Fixes
313
+
314
+ - Requirements.txt to reduce vulnerabilities (#245)
315
+ - Requirements.txt to reduce vulnerabilities (#255)
316
+ ## [0.0.27] - 2024-05-31
317
+
318
+ ### 🐛 Bug Fixes
319
+
320
+ - *(redis)* Do not update metadata with negative ttl (#231)
321
+ ## [0.0.1] - 2023-07-22
@@ -0,0 +1,19 @@
1
+ hishel/__init__.py,sha256=byj_IhCjFMaBcp6R8iyRlQV-3R4uTfH44PQzB4lVe1g,1447
2
+ hishel/_async_cache.py,sha256=gE5CygC7FG9htBMfxul7carRRNph8zcMlSoOcB_LNTY,6792
3
+ hishel/_sync_cache.py,sha256=lfkWHJFK527peESMaufjKSbXBriidc09tOwBwub2t34,6538
4
+ hishel/_utils.py,sha256=uO8PcY_E1sHSgBGzZ2GNB4kpKqAlzmnzPCc3s-yDd44,13826
5
+ hishel/httpx.py,sha256=HcJ5iO9PgkEOp92ti8013N6m1IotLajwd9M_DLsmrX0,10997
6
+ hishel/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ hishel/requests.py,sha256=eiWcwCId04DucnquCsU12tj9cDZcn-cjZ9MYniVuNeo,6429
8
+ hishel/_core/__init__.py,sha256=byj_IhCjFMaBcp6R8iyRlQV-3R4uTfH44PQzB4lVe1g,1447
9
+ hishel/_core/_headers.py,sha256=ii4x2L6GoQFpqpgg28OtFh7p2DoM9mhE4q6CjW6xUWc,17473
10
+ hishel/_core/_spec.py,sha256=d2ZnTXttyT4zuVq9xHAO86VGJxAEBxD2a8WMyEgOuAo,102702
11
+ hishel/_core/models.py,sha256=5qwo1WifrDeZdXag7M5rh0hJuVsm1N-sF3UagQ5LcLc,5519
12
+ hishel/_core/_async/_storages/_sqlite.py,sha256=wIO0UaFzal9qoVqDVczzcsW0kGUjBQD-ikauc_MN414,14704
13
+ hishel/_core/_base/_storages/_base.py,sha256=xLJGTBlFK8DVrQMgRMtGXJnYRUmNB-iYkk7S-BtMx8s,8516
14
+ hishel/_core/_base/_storages/_packing.py,sha256=NFMpSvYYTDBNkzwpjj5l4w-JOPLc19oAEDqDEQJ7VZI,4873
15
+ hishel/_core/_sync/_storages/_sqlite.py,sha256=TDm9jXIWtd54m4_8AiVApxZVmbBoeFVi3E6s-vGzDjs,14138
16
+ hishel-1.0.0.dev0.dist-info/METADATA,sha256=EpqEHRIGfzVXqMiRefCa_NZ9AlbjzVToXfnK-GBrs9o,9993
17
+ hishel-1.0.0.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ hishel-1.0.0.dev0.dist-info/licenses/LICENSE,sha256=1qQj7pE0V2O9OIedvyOgLGLvZLaPd3nFEup3IBEOZjQ,1493
19
+ hishel-1.0.0.dev0.dist-info/RECORD,,
hishel/_async/__init__.py DELETED
@@ -1,5 +0,0 @@
1
- from ._client import * # noqa: F403
2
- from ._mock import * # noqa: F403
3
- from ._pool import * # noqa: F403
4
- from ._storages import * # noqa: F403
5
- from ._transports import * # noqa: F403
hishel/_async/_client.py DELETED
@@ -1,30 +0,0 @@
1
- import typing as tp
2
-
3
- import httpx
4
-
5
- from ._transports import AsyncCacheTransport
6
-
7
- __all__ = ("AsyncCacheClient",)
8
-
9
-
10
- class AsyncCacheClient(httpx.AsyncClient):
11
- def __init__(self, *args: tp.Any, **kwargs: tp.Any):
12
- self._storage = kwargs.pop("storage") if "storage" in kwargs else None
13
- self._controller = kwargs.pop("controller") if "controller" in kwargs else None
14
- super().__init__(*args, **kwargs)
15
-
16
- def _init_transport(self, *args, **kwargs) -> AsyncCacheTransport: # type: ignore
17
- _transport = super()._init_transport(*args, **kwargs)
18
- return AsyncCacheTransport(
19
- transport=_transport,
20
- storage=self._storage,
21
- controller=self._controller,
22
- )
23
-
24
- def _init_proxy_transport(self, *args, **kwargs) -> AsyncCacheTransport: # type: ignore
25
- _transport = super()._init_proxy_transport(*args, **kwargs) # pragma: no cover
26
- return AsyncCacheTransport( # pragma: no cover
27
- transport=_transport,
28
- storage=self._storage,
29
- controller=self._controller,
30
- )