hishel 1.1.3__tar.gz → 1.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {hishel-1.1.3 → hishel-1.1.5}/CHANGELOG.md +22 -1
  2. {hishel-1.1.3 → hishel-1.1.5}/PKG-INFO +23 -2
  3. {hishel-1.1.3 → hishel-1.1.5}/hishel/_async_httpx.py +14 -2
  4. {hishel-1.1.3 → hishel-1.1.5}/hishel/_core/_storages/_async_sqlite.py +18 -10
  5. {hishel-1.1.3 → hishel-1.1.5}/hishel/_core/_storages/_sync_sqlite.py +18 -10
  6. {hishel-1.1.3 → hishel-1.1.5}/hishel/_sync_httpx.py +14 -2
  7. {hishel-1.1.3 → hishel-1.1.5}/hishel/requests.py +7 -2
  8. {hishel-1.1.3 → hishel-1.1.5}/pyproject.toml +1 -1
  9. {hishel-1.1.3 → hishel-1.1.5}/.gitignore +0 -0
  10. {hishel-1.1.3 → hishel-1.1.5}/LICENSE +0 -0
  11. {hishel-1.1.3 → hishel-1.1.5}/README.md +0 -0
  12. {hishel-1.1.3 → hishel-1.1.5}/hishel/__init__.py +0 -0
  13. {hishel-1.1.3 → hishel-1.1.5}/hishel/_async_cache.py +0 -0
  14. {hishel-1.1.3 → hishel-1.1.5}/hishel/_core/_headers.py +0 -0
  15. {hishel-1.1.3 → hishel-1.1.5}/hishel/_core/_spec.py +0 -0
  16. {hishel-1.1.3 → hishel-1.1.5}/hishel/_core/_storages/_async_base.py +0 -0
  17. {hishel-1.1.3 → hishel-1.1.5}/hishel/_core/_storages/_packing.py +0 -0
  18. {hishel-1.1.3 → hishel-1.1.5}/hishel/_core/_storages/_sync_base.py +0 -0
  19. {hishel-1.1.3 → hishel-1.1.5}/hishel/_core/models.py +0 -0
  20. {hishel-1.1.3 → hishel-1.1.5}/hishel/_policies.py +0 -0
  21. {hishel-1.1.3 → hishel-1.1.5}/hishel/_sync_cache.py +0 -0
  22. {hishel-1.1.3 → hishel-1.1.5}/hishel/_utils.py +0 -0
  23. {hishel-1.1.3 → hishel-1.1.5}/hishel/asgi.py +0 -0
  24. {hishel-1.1.3 → hishel-1.1.5}/hishel/fastapi.py +0 -0
  25. {hishel-1.1.3 → hishel-1.1.5}/hishel/httpx.py +0 -0
  26. {hishel-1.1.3 → hishel-1.1.5}/hishel/py.typed +0 -0
@@ -1,3 +1,24 @@
1
+ ## What's Changed in 1.1.5
2
+ ### 🐛 Bug Fixes
3
+
4
+ * filter out soft-deleted, expired and incomplete entries in `get_entries` by @karpetrosyan
5
+
6
+ ### Contributors
7
+ * @karpetrosyan
8
+
9
+ **Full Changelog**: https://github.com/karpetrosyan/hishel/compare/1.1.4...1.1.5
10
+
11
+ ## What's Changed in 1.1.4
12
+ ### 🐛 Bug Fixes
13
+
14
+ * don't raise an error on consumed streams that were read into memory by @karpetrosyan
15
+ * close sqlite connections properly by @karpetrosyan
16
+
17
+ ### Contributors
18
+ * @karpetrosyan
19
+
20
+ **Full Changelog**: https://github.com/karpetrosyan/hishel/compare/1.1.3...1.1.4
21
+
1
22
  ## What's Changed in 1.1.3
2
23
  ### ⚙️ Miscellaneous Tasks
3
24
 
@@ -8,9 +29,9 @@
8
29
  * fix: set `after_revalidation=True` for `NeedsToBeUpdated` -> `FromCache` transition by @jlopex in [#402](https://github.com/karpetrosyan/hishel/pull/402)
9
30
 
10
31
  ### Contributors
32
+ * @karpetrosyan
11
33
  * @martinblech
12
34
  * @jlopex
13
- * @karpetrosyan
14
35
 
15
36
  **Full Changelog**: https://github.com/karpetrosyan/hishel/compare/1.1.2...1.1.3
16
37
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hishel
3
- Version: 1.1.3
3
+ Version: 1.1.5
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
@@ -406,6 +406,27 @@ Hishel is inspired by and builds upon the excellent work in the Python HTTP ecos
406
406
  <strong>Made with ❤️ by <a href="https://github.com/karpetrosyan">Kar Petrosyan</a></strong>
407
407
  </p>
408
408
 
409
+ ## What's Changed in 1.1.5
410
+ ### 🐛 Bug Fixes
411
+
412
+ * filter out soft-deleted, expired and incomplete entries in `get_entries` by @karpetrosyan
413
+
414
+ ### Contributors
415
+ * @karpetrosyan
416
+
417
+ **Full Changelog**: https://github.com/karpetrosyan/hishel/compare/1.1.4...1.1.5
418
+
419
+ ## What's Changed in 1.1.4
420
+ ### 🐛 Bug Fixes
421
+
422
+ * don't raise an error on consumed streams that were read into memory by @karpetrosyan
423
+ * close sqlite connections properly by @karpetrosyan
424
+
425
+ ### Contributors
426
+ * @karpetrosyan
427
+
428
+ **Full Changelog**: https://github.com/karpetrosyan/hishel/compare/1.1.3...1.1.4
429
+
409
430
  ## What's Changed in 1.1.3
410
431
  ### ⚙️ Miscellaneous Tasks
411
432
 
@@ -416,9 +437,9 @@ Hishel is inspired by and builds upon the excellent work in the Python HTTP ecos
416
437
  * fix: set `after_revalidation=True` for `NeedsToBeUpdated` -> `FromCache` transition by @jlopex in [#402](https://github.com/karpetrosyan/hishel/pull/402)
417
438
 
418
439
  ### Contributors
440
+ * @karpetrosyan
419
441
  * @martinblech
420
442
  * @jlopex
421
- * @karpetrosyan
422
443
 
423
444
  **Full Changelog**: https://github.com/karpetrosyan/hishel/compare/1.1.2...1.1.3
424
445
 
@@ -118,12 +118,24 @@ def _httpx_to_internal(
118
118
  metadata=headers_metadata,
119
119
  )
120
120
  elif isinstance(value, httpx.Response):
121
- if value.is_stream_consumed and "content-encoding" in value.headers:
122
- raise RuntimeError("Can't get the raw stream of a response with `Content-Encoding` header.")
123
121
  stream = (
124
122
  make_async_iterator([value.content]) if value.is_stream_consumed else value.aiter_raw(chunk_size=CHUNK_SIZE)
125
123
  )
126
124
 
125
+ if value.is_stream_consumed and "content-encoding" in value.headers:
126
+ # If the stream was consumed and we don't know about
127
+ # the original data and its size, fix the Content-Length
128
+ # header and remove Content-Encoding so we can recreate it later properly.
129
+ headers = Headers(
130
+ {
131
+ **filter_mapping(
132
+ headers,
133
+ ["content-encoding"],
134
+ ),
135
+ "content-length": str(len(value.content)),
136
+ }
137
+ )
138
+
127
139
  return Response(
128
140
  status_code=value.status_code,
129
141
  headers=headers,
@@ -161,8 +161,19 @@ try:
161
161
  for row in await cursor.fetchall():
162
162
  pair_data = unpack(row[1], kind="pair")
163
163
 
164
+ if pair_data is None:
165
+ continue
166
+
164
167
  # Skip entries without a response (incomplete)
165
- if not isinstance(pair_data, Entry) or pair_data.response is None:
168
+ if not await self._is_stream_complete(pair_data.id, cursor=cursor):
169
+ continue
170
+
171
+ # Skip expired entries
172
+ if await self._is_pair_expired(pair_data, cursor=cursor):
173
+ continue
174
+
175
+ # Skip soft-deleted entries
176
+ if self.is_soft_deleted(pair_data):
166
177
  continue
167
178
 
168
179
  final_pairs.append(pair_data)
@@ -237,6 +248,11 @@ try:
237
248
  await self._soft_delete_pair(pair, cursor)
238
249
  await connection.commit()
239
250
 
251
+ async def close(self) -> None:
252
+ if self.connection is not None:
253
+ await self.connection.close()
254
+ self.connection = None
255
+
240
256
  async def _is_stream_complete(self, pair_id: uuid.UUID, cursor: anysqlite.Cursor) -> bool:
241
257
  # Check if there's a completion marker (chunk_number = -1) for response stream
242
258
  await cursor.execute(
@@ -325,15 +341,7 @@ try:
325
341
 
326
342
  async def _is_corrupted(self, pair: Entry, cursor: anysqlite.Cursor) -> bool:
327
343
  # if entry was created more than 1 hour ago and still has no response (incomplete)
328
- if pair.meta.created_at + 3600 < time.time() and pair.response is None:
329
- return True
330
-
331
- # Check if response stream is complete for Entry with response
332
- if (
333
- isinstance(pair, Entry)
334
- and pair.response is not None
335
- and not await self._is_stream_complete(pair.id, cursor)
336
- ):
344
+ if pair.meta.created_at + 3600 < time.time() and not self._is_stream_complete(pair.id, cursor):
337
345
  return True
338
346
  return False
339
347
 
@@ -161,8 +161,19 @@ try:
161
161
  for row in cursor.fetchall():
162
162
  pair_data = unpack(row[1], kind="pair")
163
163
 
164
+ if pair_data is None:
165
+ continue
166
+
164
167
  # Skip entries without a response (incomplete)
165
- if not isinstance(pair_data, Entry) or pair_data.response is None:
168
+ if not self._is_stream_complete(pair_data.id, cursor=cursor):
169
+ continue
170
+
171
+ # Skip expired entries
172
+ if self._is_pair_expired(pair_data, cursor=cursor):
173
+ continue
174
+
175
+ # Skip soft-deleted entries
176
+ if self.is_soft_deleted(pair_data):
166
177
  continue
167
178
 
168
179
  final_pairs.append(pair_data)
@@ -237,6 +248,11 @@ try:
237
248
  self._soft_delete_pair(pair, cursor)
238
249
  connection.commit()
239
250
 
251
+ def close(self) -> None:
252
+ if self.connection is not None:
253
+ self.connection.close()
254
+ self.connection = None
255
+
240
256
  def _is_stream_complete(self, pair_id: uuid.UUID, cursor: sqlite3.Cursor) -> bool:
241
257
  # Check if there's a completion marker (chunk_number = -1) for response stream
242
258
  cursor.execute(
@@ -325,15 +341,7 @@ try:
325
341
 
326
342
  def _is_corrupted(self, pair: Entry, cursor: sqlite3.Cursor) -> bool:
327
343
  # if entry was created more than 1 hour ago and still has no response (incomplete)
328
- if pair.meta.created_at + 3600 < time.time() and pair.response is None:
329
- return True
330
-
331
- # Check if response stream is complete for Entry with response
332
- if (
333
- isinstance(pair, Entry)
334
- and pair.response is not None
335
- and not self._is_stream_complete(pair.id, cursor)
336
- ):
344
+ if pair.meta.created_at + 3600 < time.time() and not self._is_stream_complete(pair.id, cursor):
337
345
  return True
338
346
  return False
339
347
 
@@ -118,12 +118,24 @@ def _httpx_to_internal(
118
118
  metadata=headers_metadata,
119
119
  )
120
120
  elif isinstance(value, httpx.Response):
121
- if value.is_stream_consumed and "content-encoding" in value.headers:
122
- raise RuntimeError("Can't get the raw stream of a response with `Content-Encoding` header.")
123
121
  stream = (
124
122
  make_sync_iterator([value.content]) if value.is_stream_consumed else value.iter_raw(chunk_size=CHUNK_SIZE)
125
123
  )
126
124
 
125
+ if value.is_stream_consumed and "content-encoding" in value.headers:
126
+ # If the stream was consumed and we don't know about
127
+ # the original data and its size, fix the Content-Length
128
+ # header and remove Content-Encoding so we can recreate it later properly.
129
+ headers = Headers(
130
+ {
131
+ **filter_mapping(
132
+ headers,
133
+ ["content-encoding"],
134
+ ),
135
+ "content-length": str(len(value.content)),
136
+ }
137
+ )
138
+
127
139
  return Response(
128
140
  status_code=value.status_code,
129
141
  headers=headers,
@@ -10,7 +10,7 @@ from hishel._core._storages._sync_base import SyncBaseStorage
10
10
  from hishel._core.models import extract_metadata_from_headers
11
11
  from hishel._policies import CachePolicy
12
12
  from hishel._sync_cache import SyncCacheProxy
13
- from hishel._utils import snake_to_header
13
+ from hishel._utils import filter_mapping, snake_to_header
14
14
 
15
15
  try:
16
16
  import requests
@@ -94,12 +94,17 @@ def _requests_to_internal(
94
94
  elif isinstance(model, requests.models.Response):
95
95
  try:
96
96
  stream = model.raw.stream(amt=CHUNK_SIZE, decode_content=None)
97
+ headers = Headers(filter_mapping(model.headers, ["transfer-encoding"]))
97
98
  except requests.exceptions.StreamConsumedError:
98
99
  stream = iter([model.content])
100
+ # If the stream was consumed and we don't know about the original
101
+ # data and its size, fix the Content-Length header and remove
102
+ # Content-Encoding so we can recreate it later properly.
103
+ headers = Headers(filter_mapping(model.headers, ["content-encoding", "transfer-encoding"]))
99
104
 
100
105
  return Response(
101
106
  status_code=model.status_code,
102
- headers=Headers(model.headers),
107
+ headers=headers,
103
108
  stream=stream,
104
109
  )
105
110
  else:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hishel"
7
- version = "1.1.3"
7
+ version = "1.1.5"
8
8
  dynamic = ["readme"]
9
9
  description = " Elegant HTTP Caching for Python"
10
10
  license = "BSD-3-Clause"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes