hishel 0.1.5__py3-none-any.whl → 1.0.0.dev1__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 (44) 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/_core/_async/_storages/_sqlite.py +457 -0
  5. hishel/{beta/_core → _core}/_base/_storages/_base.py +1 -1
  6. hishel/{beta/_core → _core}/_base/_storages/_packing.py +5 -5
  7. hishel/{beta/_core → _core}/_spec.py +89 -2
  8. hishel/_core/_sync/_storages/_sqlite.py +457 -0
  9. hishel/{beta/_core → _core}/models.py +1 -1
  10. hishel/{beta/_sync_cache.py → _sync_cache.py} +3 -3
  11. hishel/_utils.py +1 -241
  12. hishel/{beta/httpx.py → httpx.py} +15 -8
  13. hishel/{beta/requests.py → requests.py} +5 -5
  14. hishel-1.0.0.dev1.dist-info/METADATA +298 -0
  15. hishel-1.0.0.dev1.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/_async/_storages/_sqlite.py +0 -411
  39. hishel/beta/_core/_sync/_storages/_sqlite.py +0 -411
  40. hishel-0.1.5.dist-info/METADATA +0 -258
  41. hishel-0.1.5.dist-info/RECORD +0 -41
  42. /hishel/{beta/_core → _core}/_headers.py +0 -0
  43. {hishel-0.1.5.dist-info → hishel-1.0.0.dev1.dist-info}/WHEEL +0 -0
  44. {hishel-0.1.5.dist-info → hishel-1.0.0.dev1.dist-info}/licenses/LICENSE +0 -0
hishel/_utils.py CHANGED
@@ -2,19 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  import calendar
4
4
  import hashlib
5
- import json
6
- import sqlite3
7
5
  import time
8
6
  import typing as tp
9
- from datetime import date
10
7
  from email.utils import parsedate_tz
11
- from typing import Any, AsyncIterator, Generator, Iterable, Iterator, TypeVar
8
+ from typing import AsyncIterator, Generator, Iterable, Iterator, TypeVar
12
9
 
13
- import anyio
14
- import anysqlite
15
10
  import httpcore
16
11
  import httpx
17
- from anyio import from_thread, to_thread
18
12
 
19
13
  HEADERS_ENCODING = "iso-8859-1"
20
14
 
@@ -116,10 +110,6 @@ def parse_date(date: str) -> tp.Optional[int]:
116
110
  return timestamp
117
111
 
118
112
 
119
- async def asleep(seconds: tp.Union[int, float]) -> None:
120
- await anyio.sleep(seconds)
121
-
122
-
123
113
  def sleep(seconds: tp.Union[int, float]) -> None:
124
114
  time.sleep(seconds)
125
115
 
@@ -155,66 +145,6 @@ def partition(iterable: tp.Iterable[T], predicate: tp.Callable[[T], bool]) -> tp
155
145
  return matching, non_matching
156
146
 
157
147
 
158
- def async_iterator_to_sync(iterator: AsyncIterator[bytes]) -> Iterator[bytes]:
159
- """
160
- Convert an asynchronous byte iterator to a synchronous one.
161
- This function takes an asynchronous iterator that yields bytes and converts it into
162
- a synchronous iterator.
163
-
164
- Args:
165
- iterator (AsyncIterator[bytes]): The asynchronous byte iterator to be converted.
166
- Returns:
167
- Iterator[bytes]: A synchronous iterator that yields the same byte chunks as the input iterator.
168
- Example:
169
- ```python
170
- async_iter = some_async_byte_stream()
171
- sync_iter = async_iterator_to_sync(async_iter)
172
- for chunk in sync_iter:
173
- process_bytes(chunk)
174
- ```
175
- """
176
-
177
- while True:
178
- try:
179
- chunk = from_thread.run(iterator.__anext__)
180
- except StopAsyncIteration:
181
- break
182
- yield chunk
183
-
184
-
185
- def _call_next(iterator: Iterator[bytes]) -> bytes:
186
- try:
187
- return iterator.__next__()
188
- except StopIteration:
189
- raise StopAsyncIteration
190
-
191
-
192
- async def sync_iterator_to_async(iterator: Iterator[bytes]) -> AsyncIterator[bytes]:
193
- """
194
- Converts a synchronous bytes iterator to an asynchronous one.
195
- This function takes a synchronous iterator that yields bytes and converts it into an
196
- asynchronous iterator, allowing it to be used in async contexts without blocking.
197
- Args:
198
- iterator (Iterator[bytes]): A synchronous iterator yielding bytes objects.
199
- Returns:
200
- AsyncIterator[bytes]: An asynchronous iterator yielding the same bytes objects.
201
- Example:
202
- ```
203
- sync_iter = iter([b'data1', b'data2'])
204
- async for chunk in sync_iterator_to_async(sync_iter):
205
- await process_chunk(chunk)
206
- ```
207
- """
208
-
209
- while True:
210
- try:
211
- chunk = await to_thread.run_sync(_call_next, iterator)
212
- except StopAsyncIteration:
213
- break
214
-
215
- yield chunk
216
-
217
-
218
148
  async def make_async_iterator(iterable: Iterable[bytes]) -> AsyncIterator[bytes]:
219
149
  for item in iterable:
220
150
  yield item
@@ -286,173 +216,3 @@ class GeneratorWithReturnValue:
286
216
  self.value = exc.value
287
217
  raise
288
218
  return chunk
289
-
290
-
291
- def print_sqlite_state(conn: sqlite3.Connection) -> str:
292
- """
293
- Print all tables and their rows in a pretty format suitable for inline snapshots.
294
-
295
- Args:
296
- conn: SQLite database connection
297
-
298
- Returns:
299
- Formatted string representation of the database state
300
- """
301
- cursor = conn.cursor()
302
-
303
- # Get all table names
304
- cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
305
- tables = [row[0] for row in cursor.fetchall()]
306
-
307
- output_lines = []
308
- output_lines.append("=" * 80)
309
- output_lines.append("DATABASE SNAPSHOT")
310
- output_lines.append("=" * 80)
311
-
312
- for table_name in tables:
313
- # Get column information
314
- cursor.execute(f"PRAGMA table_info({table_name})")
315
- columns = cursor.fetchall()
316
- column_names = [col[1] for col in columns]
317
- column_types = {col[1]: col[2] for col in columns}
318
-
319
- # Get all rows
320
- cursor.execute(f"SELECT * FROM {table_name}")
321
- rows = cursor.fetchall()
322
-
323
- output_lines.append("")
324
- output_lines.append(f"TABLE: {table_name}")
325
- output_lines.append("-" * 80)
326
- output_lines.append(f"Rows: {len(rows)}")
327
- output_lines.append("")
328
-
329
- if not rows:
330
- output_lines.append(" (empty)")
331
- continue
332
-
333
- # Format each row
334
- for idx, row in enumerate(rows, 1):
335
- output_lines.append(f" Row {idx}:")
336
-
337
- for col_name, value in zip(column_names, row):
338
- col_type = column_types[col_name]
339
- formatted_value = format_value(value, col_name, col_type)
340
- output_lines.append(f" {col_name:15} = {formatted_value}")
341
-
342
- if idx < len(rows):
343
- output_lines.append("")
344
-
345
- output_lines.append("")
346
- output_lines.append("=" * 80)
347
-
348
- result = "\n".join(output_lines)
349
- return result
350
-
351
-
352
- async def aprint_sqlite_state(conn: anysqlite.Connection) -> str:
353
- """
354
- Print all tables and their rows in a pretty format suitable for inline snapshots.
355
-
356
- Args:
357
- conn: SQLite database connection
358
-
359
- Returns:
360
- Formatted string representation of the database state
361
- """
362
- cursor = await conn.cursor()
363
-
364
- # Get all table names
365
- await cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
366
- tables = [row[0] for row in await cursor.fetchall()]
367
-
368
- output_lines = []
369
- output_lines.append("=" * 80)
370
- output_lines.append("DATABASE SNAPSHOT")
371
- output_lines.append("=" * 80)
372
-
373
- for table_name in tables:
374
- # Get column information
375
- await cursor.execute(f"PRAGMA table_info({table_name})")
376
- columns = await cursor.fetchall()
377
- column_names = [col[1] for col in columns]
378
- column_types = {col[1]: col[2] for col in columns}
379
-
380
- # Get all rows
381
- await cursor.execute(f"SELECT * FROM {table_name}")
382
- rows = await cursor.fetchall()
383
-
384
- output_lines.append("")
385
- output_lines.append(f"TABLE: {table_name}")
386
- output_lines.append("-" * 80)
387
- output_lines.append(f"Rows: {len(rows)}")
388
- output_lines.append("")
389
-
390
- if not rows:
391
- output_lines.append(" (empty)")
392
- continue
393
-
394
- # Format each row
395
- for idx, row in enumerate(rows, 1):
396
- output_lines.append(f" Row {idx}:")
397
-
398
- for col_name, value in zip(column_names, row):
399
- col_type = column_types[col_name]
400
- formatted_value = format_value(value, col_name, col_type)
401
- output_lines.append(f" {col_name:15} = {formatted_value}")
402
-
403
- if idx < len(rows):
404
- output_lines.append("")
405
-
406
- output_lines.append("")
407
- output_lines.append("=" * 80)
408
-
409
- result = "\n".join(output_lines)
410
- return result
411
-
412
-
413
- def format_value(value: Any, col_name: str, col_type: str) -> str:
414
- """Format a value for display based on its type and column name."""
415
-
416
- if value is None:
417
- return "NULL"
418
-
419
- # Handle BLOB columns
420
- if col_type.upper() == "BLOB":
421
- if isinstance(value, bytes):
422
- # Try to decode as UTF-8 string first
423
- try:
424
- decoded = value.decode("utf-8")
425
- # Check if it looks like JSON
426
- if decoded.strip().startswith("{") or decoded.strip().startswith("["):
427
- try:
428
- parsed = json.loads(decoded)
429
- return f"(JSON) {json.dumps(parsed, indent=2)}"
430
- except json.JSONDecodeError:
431
- pass
432
- # Show string if it's printable
433
- if all(32 <= ord(c) <= 126 or c in "\n\r\t" for c in decoded):
434
- return f"(str) '{decoded}'"
435
- except UnicodeDecodeError:
436
- pass
437
-
438
- # Show hex representation for binary data
439
- hex_str = value.hex()
440
- if len(hex_str) > 64:
441
- return f"(bytes) 0x{hex_str[:60]}... ({len(value)} bytes)"
442
- return f"(bytes) 0x{hex_str} ({len(value)} bytes)"
443
- return repr(value)
444
-
445
- # Handle timestamps - ONLY show date, not the raw timestamp
446
- if col_name.endswith("_at") and isinstance(value, (int, float)):
447
- try:
448
- dt = date.fromtimestamp(value)
449
- return dt.isoformat() # Changed: removed the timestamp prefix
450
- except (ValueError, OSError):
451
- return str(value)
452
-
453
- # Handle TEXT columns
454
- if col_type.upper() == "TEXT":
455
- return f"'{value}'"
456
-
457
- # Handle other types
458
- return str(value)
@@ -4,16 +4,23 @@ import ssl
4
4
  import typing as t
5
5
  from typing import AsyncIterator, Iterable, Iterator, Union, overload
6
6
 
7
- import httpx
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 (
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 (
13
11
  CacheOptions,
14
12
  )
15
- from hishel.beta._core.models import AnyIterable
16
- from hishel.beta._sync_cache import SyncCacheProxy
13
+ from hishel._core.models import AnyIterable
14
+ from hishel._sync_cache import SyncCacheProxy
15
+
16
+ try:
17
+ import httpx
18
+ except ImportError as e:
19
+ raise ImportError(
20
+ "httpx is required to use hishel.httpx module. "
21
+ "Please install hishel with the 'httpx' extra, "
22
+ "e.g., 'pip install hishel[httpx]'."
23
+ ) from e
17
24
 
18
25
  SOCKET_OPTION = t.Union[
19
26
  t.Tuple[int, int, int],
@@ -5,12 +5,12 @@ 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
@@ -0,0 +1,298 @@
1
+ Metadata-Version: 2.4
2
+ Name: hishel
3
+ Version: 1.0.0.dev1
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: msgpack>=1.1.2
28
+ Requires-Dist: typing-extensions>=4.14.1
29
+ Provides-Extra: async
30
+ Requires-Dist: anyio>=4.9.0; extra == 'async'
31
+ Requires-Dist: anysqlite>=0.0.5; extra == 'async'
32
+ Provides-Extra: httpx
33
+ Requires-Dist: anyio>=4.9.0; extra == 'httpx'
34
+ Requires-Dist: anysqlite>=0.0.5; extra == 'httpx'
35
+ Requires-Dist: httpx>=0.28.1; extra == 'httpx'
36
+ Provides-Extra: requests
37
+ Requires-Dist: requests>=2.32.5; extra == 'requests'
38
+ Description-Content-Type: text/markdown
39
+
40
+ <p align="center">
41
+ <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
+ <img alt="Hishel Logo" width="350" src="https://raw.githubusercontent.com/karpetrosyan/hishel/master/docs/static/Shelkopryad_350x250_black.png#gh-light-mode-only">
43
+ </p>
44
+
45
+ <h1 align="center">Hishel</h1>
46
+
47
+ <p align="center">
48
+ <strong>Elegant HTTP Caching for Python</strong>
49
+ </p>
50
+
51
+ <p align="center">
52
+ <a href="https://pypi.org/project/hishel">
53
+ <img src="https://img.shields.io/pypi/v/hishel.svg" alt="PyPI version">
54
+ </a>
55
+ <a href="https://pypi.org/project/hishel">
56
+ <img src="https://img.shields.io/pypi/pyversions/hishel.svg" alt="Python versions">
57
+ </a>
58
+ <a href="https://github.com/karpetrosyan/hishel/blob/master/LICENSE">
59
+ <img src="https://img.shields.io/pypi/l/hishel" alt="License">
60
+ </a>
61
+ <a href="https://coveralls.io/github/karpetrosyan/hishel">
62
+ <img src="https://img.shields.io/coverallsCoverage/github/karpetrosyan/hishel" alt="Coverage">
63
+ </a>
64
+ <a href="https://static.pepy.tech/badge/hishel/month">
65
+ <img src="https://static.pepy.tech/badge/hishel/month" alt="Downloads">
66
+ </a>
67
+ </p>
68
+
69
+ ---
70
+
71
+ **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.
72
+
73
+ ## ✨ Features
74
+
75
+ - 🎯 **RFC 9111 Compliant** - Fully compliant with the latest HTTP caching specification
76
+ - 🔌 **Easy Integration** - Drop-in support for HTTPX and Requests
77
+ - 💾 **Flexible Storage** - SQLite backend with more coming soon
78
+ - ⚡ **High Performance** - Efficient caching with minimal overhead
79
+ - 🔄 **Async & Sync** - Full support for both synchronous and asynchronous workflows
80
+ - 🎨 **Type Safe** - Fully typed with comprehensive type hints
81
+ - 🧪 **Well Tested** - Extensive test coverage and battle-tested
82
+ - 🎛️ **Configurable** - Fine-grained control over caching behavior
83
+ - 🌐 **Future Ready** - Designed for easy integration with any HTTP client/server
84
+
85
+ ## 📦 Installation
86
+
87
+ ```bash
88
+ pip install hishel
89
+ ```
90
+
91
+ ### Optional Dependencies
92
+
93
+ Install with specific HTTP client support:
94
+
95
+ ```bash
96
+ pip install hishel[httpx] # For HTTPX support
97
+ pip install hishel[requests] # For Requests support
98
+ ```
99
+
100
+ Or install both:
101
+
102
+ ```bash
103
+ pip install hishel[httpx,requests]
104
+ ```
105
+
106
+ ## 🚀 Quick Start
107
+
108
+ ### With HTTPX
109
+
110
+ **Synchronous:**
111
+
112
+ ```python
113
+ from hishel.httpx import SyncCacheClient
114
+
115
+ client = SyncCacheClient()
116
+
117
+ # First request - fetches from origin
118
+ response = client.get("https://api.example.com/data")
119
+ print(response.extensions["hishel_from_cache"]) # False
120
+
121
+ # Second request - served from cache
122
+ response = client.get("https://api.example.com/data")
123
+ print(response.extensions["hishel_from_cache"]) # True
124
+ ```
125
+
126
+ **Asynchronous:**
127
+
128
+ ```python
129
+ from hishel.httpx import AsyncCacheClient
130
+
131
+ async with AsyncCacheClient() as client:
132
+ # First request - fetches from origin
133
+ response = await client.get("https://api.example.com/data")
134
+ print(response.extensions["hishel_from_cache"]) # False
135
+
136
+ # Second request - served from cache
137
+ response = await client.get("https://api.example.com/data")
138
+ print(response.extensions["hishel_from_cache"]) # True
139
+ ```
140
+
141
+ ### With Requests
142
+
143
+ ```python
144
+ import requests
145
+ from hishel.requests import CacheAdapter
146
+
147
+ session = requests.Session()
148
+ session.mount("https://", CacheAdapter())
149
+ session.mount("http://", CacheAdapter())
150
+
151
+ # First request - fetches from origin
152
+ response = session.get("https://api.example.com/data")
153
+
154
+ # Second request - served from cache
155
+ response = session.get("https://api.example.com/data")
156
+ print(response.headers.get("X-Hishel-From-Cache")) # "True"
157
+ ```
158
+
159
+ ## 🎛️ Advanced Configuration
160
+
161
+ ### Custom Cache Options
162
+
163
+ ```python
164
+ from hishel import CacheOptions
165
+ from hishel.httpx import SyncCacheClient
166
+
167
+ client = SyncCacheClient(
168
+ cache_options=CacheOptions(
169
+ shared=False, # Use as private cache (browser-like)
170
+ supported_methods=["GET", "HEAD", "POST"], # Cache GET, HEAD, and POST
171
+ allow_stale=True # Allow serving stale responses
172
+ )
173
+ )
174
+ ```
175
+
176
+ ### Custom Storage Backend
177
+
178
+ ```python
179
+ from hishel import SyncSqliteStorage
180
+ from hishel.httpx import SyncCacheClient
181
+
182
+ storage = SyncSqliteStorage(
183
+ database_path="my_cache.db",
184
+ default_ttl=7200.0, # Cache entries expire after 2 hours
185
+ refresh_ttl_on_access=True # Reset TTL when accessing cached entries
186
+ )
187
+
188
+ client = SyncCacheClient(storage=storage)
189
+ ```
190
+
191
+ ## 🏗️ Architecture
192
+
193
+ Hishel uses a **sans-I/O state machine** architecture that separates HTTP caching logic from I/O operations:
194
+
195
+ - ✅ **Correct** - Fully RFC 9111 compliant
196
+ - ✅ **Testable** - Easy to test without network dependencies
197
+ - ✅ **Flexible** - Works with any HTTP client or server
198
+ - ✅ **Type Safe** - Clear state transitions with full type hints
199
+
200
+ ## 🔮 Roadmap
201
+
202
+ While Hishel currently supports HTTPX and Requests, we're actively working on:
203
+
204
+ - 🎯 Additional HTTP client integrations
205
+ - 🎯 Server-side caching support
206
+ - 🎯 More storage backends
207
+ - 🎯 Advanced caching strategies
208
+ - 🎯 Performance optimizations
209
+
210
+ ## 📚 Documentation
211
+
212
+ Comprehensive documentation is available at [https://hishel.com/dev](https://hishel.com/dev)
213
+
214
+ - [Getting Started](https://hishel.com)
215
+ - [HTTPX Integration](https://hishel.com/dev/integrations/httpx)
216
+ - [Requests Integration](https://hishel.com/dev/integrations/requests)
217
+ - [Storage Backends](https://hishel.com/dev/storages)
218
+ - [RFC 9111 Specification](https://hishel.com/dev/specification)
219
+
220
+ ## 🤝 Contributing
221
+
222
+ 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.
223
+
224
+ See our [Contributing Guide](https://hishel.com/dev/contributing) for more details.
225
+
226
+ ## 📄 License
227
+
228
+ This project is licensed under the BSD-3-Clause License - see the [LICENSE](LICENSE) file for details.
229
+
230
+ ## 💖 Support
231
+
232
+ If you find Hishel useful, please consider:
233
+
234
+ - ⭐ Starring the repository
235
+ - 🐛 Reporting bugs and issues
236
+ - 💡 Suggesting new features
237
+ - 📖 Improving documentation
238
+ - ☕ [Buying me a coffee](https://buymeacoffee.com/karpetrosyan)
239
+
240
+ ## 🙏 Acknowledgments
241
+
242
+ Hishel is inspired by and builds upon the excellent work in the Python HTTP ecosystem, particularly:
243
+
244
+ - [HTTPX](https://github.com/encode/httpx) - A next-generation HTTP client for Python
245
+ - [Requests](https://github.com/psf/requests) - The classic HTTP library for Python
246
+ - [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111.html) - HTTP Caching specification
247
+
248
+ ---
249
+
250
+ <p align="center">
251
+ <strong>Made with ❤️ by <a href="https://github.com/karpetrosyan">Kar Petrosyan</a></strong>
252
+ </p>
253
+
254
+ # Changelog
255
+
256
+ All notable changes to this project will be documented in this file.
257
+
258
+ ## 1.0.0dev1 - 2025-10-21
259
+ ### <!-- 7 -->⚙️ Miscellaneous Tasks
260
+ - Remove some redundant utils methods
261
+
262
+ ## 1.0.0.dev0 - 2025-10-19
263
+ ### <!-- 7 -->⚙️ Miscellaneous Tasks
264
+ - Use mike powered versioning
265
+ - Improve docs versioning, deploy dev doc on ci
266
+
267
+ ## 0.1.5 - 2025-10-18
268
+ ### <!-- 0 -->🚀 Features
269
+ - Set chunk size to 128KB for httpx to reduce SQLite read/writes
270
+ - Better cache-control parsing
271
+ - Add close method to storages API (#384)
272
+ - Increase requests buffer size to 128KB, disable charset detection
273
+
274
+ ### <!-- 1 -->🐛 Bug Fixes
275
+ - Fix some line breaks
276
+
277
+ ### <!-- 7 -->⚙️ Miscellaneous Tasks
278
+ - Remove some redundant files from repo
279
+
280
+ ## 0.1.4 - 2025-10-14
281
+ ### <!-- 0 -->🚀 Features
282
+ - Add support for a sans-IO API (#366)
283
+ - Allow already consumed streams with `CacheTransport` (#377)
284
+ - Add sqlite storage for beta storages
285
+ - Get rid of some locks from sqlite storage
286
+ - Better async implemetation for sqlite storage
287
+
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
+
@@ -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=hQURFuNdS7sjHeVGu12MQzJewjFdnVrdOki3CjBfZvs,6162
5
+ hishel/httpx.py,sha256=vscNB426VIhh0f5qQVkGA_WpwVvaKxbI6gnsHrBA_D0,11226
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=QPbNtNMA7vYjpt8bSPFIZ4u4c3UCH6eDfUWnH6WprTU,17787
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=kvAcV2FttZstWEfCczBxj30-PYdj1y9lzh0x3hNusKY,17210
16
+ hishel-1.0.0.dev1.dist-info/METADATA,sha256=IW6_UTqzUl-k8Pcyil_6brlEgd-gKomu1OyeZxrN3dU,9727
17
+ hishel-1.0.0.dev1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ hishel-1.0.0.dev1.dist-info/licenses/LICENSE,sha256=1qQj7pE0V2O9OIedvyOgLGLvZLaPd3nFEup3IBEOZjQ,1493
19
+ hishel-1.0.0.dev1.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
- )