hishel 0.0.26__tar.gz → 0.0.28__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 (29) hide show
  1. {hishel-0.0.26 → hishel-0.0.28}/CHANGELOG.md +8 -0
  2. {hishel-0.0.26 → hishel-0.0.28}/PKG-INFO +11 -3
  3. {hishel-0.0.26 → hishel-0.0.28}/README.md +2 -2
  4. {hishel-0.0.26 → hishel-0.0.28}/hishel/__init__.py +1 -1
  5. {hishel-0.0.26 → hishel-0.0.28}/hishel/_async/_pool.py +18 -3
  6. {hishel-0.0.26 → hishel-0.0.28}/hishel/_async/_storages.py +15 -19
  7. {hishel-0.0.26 → hishel-0.0.28}/hishel/_async/_transports.py +7 -1
  8. {hishel-0.0.26 → hishel-0.0.28}/hishel/_lfu_cache.py +1 -2
  9. {hishel-0.0.26 → hishel-0.0.28}/hishel/_serializers.py +6 -10
  10. {hishel-0.0.26 → hishel-0.0.28}/hishel/_sync/_pool.py +18 -3
  11. {hishel-0.0.26 → hishel-0.0.28}/hishel/_sync/_storages.py +15 -19
  12. {hishel-0.0.26 → hishel-0.0.28}/hishel/_sync/_transports.py +7 -1
  13. {hishel-0.0.26 → hishel-0.0.28}/.gitignore +0 -0
  14. {hishel-0.0.26 → hishel-0.0.28}/LICENSE +0 -0
  15. {hishel-0.0.26 → hishel-0.0.28}/hishel/_async/__init__.py +0 -0
  16. {hishel-0.0.26 → hishel-0.0.28}/hishel/_async/_client.py +0 -0
  17. {hishel-0.0.26 → hishel-0.0.28}/hishel/_async/_mock.py +0 -0
  18. {hishel-0.0.26 → hishel-0.0.28}/hishel/_controller.py +0 -0
  19. {hishel-0.0.26 → hishel-0.0.28}/hishel/_exceptions.py +0 -0
  20. {hishel-0.0.26 → hishel-0.0.28}/hishel/_files.py +0 -0
  21. {hishel-0.0.26 → hishel-0.0.28}/hishel/_headers.py +0 -0
  22. {hishel-0.0.26 → hishel-0.0.28}/hishel/_s3.py +0 -0
  23. {hishel-0.0.26 → hishel-0.0.28}/hishel/_sync/__init__.py +0 -0
  24. {hishel-0.0.26 → hishel-0.0.28}/hishel/_sync/_client.py +0 -0
  25. {hishel-0.0.26 → hishel-0.0.28}/hishel/_sync/_mock.py +0 -0
  26. {hishel-0.0.26 → hishel-0.0.28}/hishel/_synchronization.py +0 -0
  27. {hishel-0.0.26 → hishel-0.0.28}/hishel/_utils.py +0 -0
  28. {hishel-0.0.26 → hishel-0.0.28}/hishel/py.typed +0 -0
  29. {hishel-0.0.26 → hishel-0.0.28}/pyproject.toml +0 -0
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.28 (23th June, 2024)
4
+
5
+ - Add `revalidated` response extension. (#242)
6
+
7
+ ## 0.0.27 (31th May, 2024)
8
+
9
+ - Fix `RedisStorage` when using without ttl. (#231)
10
+
3
11
  ## 0.0.26 (12th April, 2024)
4
12
 
5
13
  - Expose `AsyncBaseStorage` and `BaseStorage`. (#220)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: hishel
3
- Version: 0.0.26
3
+ Version: 0.0.28
4
4
  Summary: Persistent cache implementation for httpx and httpcore
5
5
  Project-URL: Homepage, https://hishel.com
6
6
  Project-URL: Source, https://github.com/karpetrosyan/hishel
@@ -57,8 +57,8 @@ Description-Content-Type: text/markdown
57
57
  <img src="https://img.shields.io/codecov/c/github/karpetrosyan/hishel" alt="license">
58
58
  </a>
59
59
 
60
- <a href="https://static.pepy.tech/badge/hishel/month">
61
- <img src="https://static.pepy.tech/badge/hishel/month" alt="Downloads">
60
+ <a href="https://github.com/karpetrosyan/hishel">
61
+ <img src="https://img.shields.io/pypi/dm/hishel.svg" alt="Downloads">
62
62
  </a>
63
63
  </p>
64
64
 
@@ -175,6 +175,14 @@ Help us grow and continue developing good software for you ❤️
175
175
 
176
176
  # Changelog
177
177
 
178
+ ## 0.0.28 (23th June, 2024)
179
+
180
+ - Add `revalidated` response extension. (#242)
181
+
182
+ ## 0.0.27 (31th May, 2024)
183
+
184
+ - Fix `RedisStorage` when using without ttl. (#231)
185
+
178
186
  ## 0.0.26 (12th April, 2024)
179
187
 
180
188
  - Expose `AsyncBaseStorage` and `BaseStorage`. (#220)
@@ -19,8 +19,8 @@
19
19
  <img src="https://img.shields.io/codecov/c/github/karpetrosyan/hishel" alt="license">
20
20
  </a>
21
21
 
22
- <a href="https://static.pepy.tech/badge/hishel/month">
23
- <img src="https://static.pepy.tech/badge/hishel/month" alt="Downloads">
22
+ <a href="https://github.com/karpetrosyan/hishel">
23
+ <img src="https://img.shields.io/pypi/dm/hishel.svg" alt="Downloads">
24
24
  </a>
25
25
  </p>
26
26
 
@@ -14,4 +14,4 @@ def install_cache() -> None: # pragma: no cover
14
14
  httpx.Client = CacheClient # type: ignore
15
15
 
16
16
 
17
- __version__ = "0.0.26"
17
+ __version__ = "0.0.28"
@@ -100,7 +100,12 @@ class AsyncCacheConnectionPool(AsyncRequestInterface):
100
100
  if isinstance(res, Response):
101
101
  # Simply use the response if the controller determines it is ready for use.
102
102
  return await self._create_hishel_response(
103
- key=key, response=stored_response, request=request, metadata=metadata, cached=True
103
+ key=key,
104
+ response=stored_response,
105
+ request=request,
106
+ metadata=metadata,
107
+ cached=True,
108
+ revalidated=False,
104
109
  )
105
110
 
106
111
  if request_cache_control.only_if_cached:
@@ -115,7 +120,12 @@ class AsyncCacheConnectionPool(AsyncRequestInterface):
115
120
  # If there is a connection error, we can use the stale response if allowed.
116
121
  if self._controller._allow_stale and allowed_stale(response=stored_response):
117
122
  return await self._create_hishel_response(
118
- key=key, response=stored_response, request=request, metadata=metadata, cached=True
123
+ key=key,
124
+ response=stored_response,
125
+ request=request,
126
+ metadata=metadata,
127
+ cached=True,
128
+ revalidated=False,
119
129
  )
120
130
  raise # pragma: no cover
121
131
  # Merge headers with the stale response.
@@ -130,6 +140,7 @@ class AsyncCacheConnectionPool(AsyncRequestInterface):
130
140
  request=request,
131
141
  metadata=metadata,
132
142
  cached=revalidation_response.status == 304,
143
+ revalidated=True,
133
144
  )
134
145
 
135
146
  regular_response = await self._pool.handle_async_request(request)
@@ -141,7 +152,9 @@ class AsyncCacheConnectionPool(AsyncRequestInterface):
141
152
  )
142
153
  await self._storage.store(key, response=regular_response, request=request, metadata=metadata)
143
154
 
144
- return await self._create_hishel_response(key=key, response=regular_response, request=request, cached=False)
155
+ return await self._create_hishel_response(
156
+ key=key, response=regular_response, request=request, cached=False, revalidated=False
157
+ )
145
158
 
146
159
  async def _create_hishel_response(
147
160
  self,
@@ -149,6 +162,7 @@ class AsyncCacheConnectionPool(AsyncRequestInterface):
149
162
  response: Response,
150
163
  request: Request,
151
164
  cached: bool,
165
+ revalidated: bool,
152
166
  metadata: Metadata | None = None,
153
167
  ) -> Response:
154
168
  if cached:
@@ -159,6 +173,7 @@ class AsyncCacheConnectionPool(AsyncRequestInterface):
159
173
  response.extensions["cache_metadata"] = metadata # type: ignore[index]
160
174
  else:
161
175
  response.extensions["from_cache"] = False # type: ignore[index]
176
+ response.extensions["revalidated"] = revalidated # type: ignore[index]
162
177
  return response
163
178
 
164
179
  async def aclose(self) -> None:
@@ -225,16 +225,14 @@ class AsyncSQLiteStorage(AsyncBaseStorage):
225
225
  def __init__(
226
226
  self,
227
227
  serializer: tp.Optional[BaseSerializer] = None,
228
- connection: tp.Optional["anysqlite.Connection"] = None,
228
+ connection: tp.Optional[anysqlite.Connection] = None,
229
229
  ttl: tp.Optional[tp.Union[int, float]] = None,
230
230
  ) -> None:
231
231
  if anysqlite is None: # pragma: no cover
232
232
  raise RuntimeError(
233
- (
234
- f"The `{type(self).__name__}` was used, but the required packages were not found. "
235
- "Check that you have `Hishel` installed with the `sqlite` extension as shown.\n"
236
- "```pip install hishel[sqlite]```"
237
- )
233
+ f"The `{type(self).__name__}` was used, but the required packages were not found. "
234
+ "Check that you have `Hishel` installed with the `sqlite` extension as shown.\n"
235
+ "```pip install hishel[sqlite]```"
238
236
  )
239
237
  super().__init__(serializer, ttl)
240
238
 
@@ -249,7 +247,7 @@ class AsyncSQLiteStorage(AsyncBaseStorage):
249
247
  if not self._connection: # pragma: no cover
250
248
  self._connection = await anysqlite.connect(".hishel.sqlite", check_same_thread=False)
251
249
  await self._connection.execute(
252
- ("CREATE TABLE IF NOT EXISTS cache(key TEXT, data BLOB, date_created REAL)")
250
+ "CREATE TABLE IF NOT EXISTS cache(key TEXT, data BLOB, date_created REAL)"
253
251
  )
254
252
  await self._connection.commit()
255
253
  self._setup_completed = True
@@ -363,16 +361,14 @@ class AsyncRedisStorage(AsyncBaseStorage):
363
361
  def __init__(
364
362
  self,
365
363
  serializer: tp.Optional[BaseSerializer] = None,
366
- client: tp.Optional["redis.Redis"] = None, # type: ignore
364
+ client: tp.Optional[redis.Redis] = None, # type: ignore
367
365
  ttl: tp.Optional[tp.Union[int, float]] = None,
368
366
  ) -> None:
369
367
  if redis is None: # pragma: no cover
370
368
  raise RuntimeError(
371
- (
372
- f"The `{type(self).__name__}` was used, but the required packages were not found. "
373
- "Check that you have `Hishel` installed with the `redis` extension as shown.\n"
374
- "```pip install hishel[redis]```"
375
- )
369
+ f"The `{type(self).__name__}` was used, but the required packages were not found. "
370
+ "Check that you have `Hishel` installed with the `redis` extension as shown.\n"
371
+ "```pip install hishel[redis]```"
376
372
  )
377
373
  super().__init__(serializer, ttl)
378
374
 
@@ -424,7 +420,9 @@ class AsyncRedisStorage(AsyncBaseStorage):
424
420
 
425
421
  ttl_in_milliseconds = await self._client.pttl(key)
426
422
 
427
- if ttl_in_milliseconds == -2: # pragma: no cover
423
+ # -2: if the key does not exist in Redis
424
+ # -1: if the key exists in Redis but has no expiration
425
+ if ttl_in_milliseconds == -2 or ttl_in_milliseconds == -1: # pragma: no cover
428
426
  await self.store(key, response, request, metadata)
429
427
  else:
430
428
  await self._client.set(
@@ -597,11 +595,9 @@ class AsyncS3Storage(AsyncBaseStorage): # pragma: no cover
597
595
 
598
596
  if boto3 is None: # pragma: no cover
599
597
  raise RuntimeError(
600
- (
601
- f"The `{type(self).__name__}` was used, but the required packages were not found. "
602
- "Check that you have `Hishel` installed with the `s3` extension as shown.\n"
603
- "```pip install hishel[s3]```"
604
- )
598
+ f"The `{type(self).__name__}` was used, but the required packages were not found. "
599
+ "Check that you have `Hishel` installed with the `s3` extension as shown.\n"
600
+ "```pip install hishel[s3]```"
605
601
  )
606
602
 
607
603
  self._bucket_name = bucket_name
@@ -141,6 +141,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
141
141
  response=res,
142
142
  request=httpcore_request,
143
143
  cached=True,
144
+ revalidated=False,
144
145
  metadata=metadata,
145
146
  )
146
147
 
@@ -166,6 +167,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
166
167
  response=stored_response,
167
168
  request=httpcore_request,
168
169
  cached=True,
170
+ revalidated=False,
169
171
  metadata=metadata,
170
172
  )
171
173
  raise # pragma: no cover
@@ -191,6 +193,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
191
193
  response=final_httpcore_response,
192
194
  request=httpcore_request,
193
195
  cached=revalidation_response.status_code == 304,
196
+ revalidated=True,
194
197
  metadata=metadata,
195
198
  )
196
199
 
@@ -217,6 +220,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
217
220
  response=httpcore_regular_response,
218
221
  request=httpcore_request,
219
222
  cached=False,
223
+ revalidated=False,
220
224
  )
221
225
 
222
226
  async def _create_hishel_response(
@@ -225,6 +229,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
225
229
  response: httpcore.Response,
226
230
  request: httpcore.Request,
227
231
  cached: bool,
232
+ revalidated: bool,
228
233
  metadata: Metadata | None = None,
229
234
  ) -> Response:
230
235
  if cached:
@@ -235,6 +240,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
235
240
  response.extensions["cache_metadata"] = metadata # type: ignore[index]
236
241
  else:
237
242
  response.extensions["from_cache"] = False # type: ignore[index]
243
+ response.extensions["revalidated"] = revalidated # type: ignore[index]
238
244
  return Response(
239
245
  status_code=response.status,
240
246
  headers=response.headers,
@@ -246,7 +252,7 @@ class AsyncCacheTransport(httpx.AsyncBaseTransport):
246
252
  await self._storage.aclose()
247
253
  await self._transport.aclose()
248
254
 
249
- async def __aenter__(self) -> "Self":
255
+ async def __aenter__(self) -> Self:
250
256
  return self
251
257
 
252
258
  async def __aexit__(
@@ -68,5 +68,4 @@ class LFUCache(Generic[K, V]):
68
68
  del self.cache[key]
69
69
 
70
70
  def __iter__(self) -> Iterator[K]:
71
- for key in self.cache:
72
- yield key
71
+ yield from self.cache
@@ -224,11 +224,9 @@ class YAMLSerializer(BaseSerializer):
224
224
  """
225
225
  if yaml is None: # pragma: no cover
226
226
  raise RuntimeError(
227
- (
228
- f"The `{type(self).__name__}` was used, but the required packages were not found. "
229
- "Check that you have `Hishel` installed with the `yaml` extension as shown.\n"
230
- "```pip install hishel[yaml]```"
231
- )
227
+ f"The `{type(self).__name__}` was used, but the required packages were not found. "
228
+ "Check that you have `Hishel` installed with the `yaml` extension as shown.\n"
229
+ "```pip install hishel[yaml]```"
232
230
  )
233
231
  response_dict = {
234
232
  "status": response.status,
@@ -278,11 +276,9 @@ class YAMLSerializer(BaseSerializer):
278
276
  """
279
277
  if yaml is None: # pragma: no cover
280
278
  raise RuntimeError(
281
- (
282
- f"The `{type(self).__name__}` was used, but the required packages were not found. "
283
- "Check that you have `Hishel` installed with the `yaml` extension as shown.\n"
284
- "```pip install hishel[yaml]```"
285
- )
279
+ f"The `{type(self).__name__}` was used, but the required packages were not found. "
280
+ "Check that you have `Hishel` installed with the `yaml` extension as shown.\n"
281
+ "```pip install hishel[yaml]```"
286
282
  )
287
283
 
288
284
  full_json = yaml.safe_load(data)
@@ -100,7 +100,12 @@ class CacheConnectionPool(RequestInterface):
100
100
  if isinstance(res, Response):
101
101
  # Simply use the response if the controller determines it is ready for use.
102
102
  return self._create_hishel_response(
103
- key=key, response=stored_response, request=request, metadata=metadata, cached=True
103
+ key=key,
104
+ response=stored_response,
105
+ request=request,
106
+ metadata=metadata,
107
+ cached=True,
108
+ revalidated=False,
104
109
  )
105
110
 
106
111
  if request_cache_control.only_if_cached:
@@ -115,7 +120,12 @@ class CacheConnectionPool(RequestInterface):
115
120
  # If there is a connection error, we can use the stale response if allowed.
116
121
  if self._controller._allow_stale and allowed_stale(response=stored_response):
117
122
  return self._create_hishel_response(
118
- key=key, response=stored_response, request=request, metadata=metadata, cached=True
123
+ key=key,
124
+ response=stored_response,
125
+ request=request,
126
+ metadata=metadata,
127
+ cached=True,
128
+ revalidated=False,
119
129
  )
120
130
  raise # pragma: no cover
121
131
  # Merge headers with the stale response.
@@ -130,6 +140,7 @@ class CacheConnectionPool(RequestInterface):
130
140
  request=request,
131
141
  metadata=metadata,
132
142
  cached=revalidation_response.status == 304,
143
+ revalidated=True,
133
144
  )
134
145
 
135
146
  regular_response = self._pool.handle_request(request)
@@ -141,7 +152,9 @@ class CacheConnectionPool(RequestInterface):
141
152
  )
142
153
  self._storage.store(key, response=regular_response, request=request, metadata=metadata)
143
154
 
144
- return self._create_hishel_response(key=key, response=regular_response, request=request, cached=False)
155
+ return self._create_hishel_response(
156
+ key=key, response=regular_response, request=request, cached=False, revalidated=False
157
+ )
145
158
 
146
159
  def _create_hishel_response(
147
160
  self,
@@ -149,6 +162,7 @@ class CacheConnectionPool(RequestInterface):
149
162
  response: Response,
150
163
  request: Request,
151
164
  cached: bool,
165
+ revalidated: bool,
152
166
  metadata: Metadata | None = None,
153
167
  ) -> Response:
154
168
  if cached:
@@ -159,6 +173,7 @@ class CacheConnectionPool(RequestInterface):
159
173
  response.extensions["cache_metadata"] = metadata # type: ignore[index]
160
174
  else:
161
175
  response.extensions["from_cache"] = False # type: ignore[index]
176
+ response.extensions["revalidated"] = revalidated # type: ignore[index]
162
177
  return response
163
178
 
164
179
  def close(self) -> None:
@@ -225,16 +225,14 @@ class SQLiteStorage(BaseStorage):
225
225
  def __init__(
226
226
  self,
227
227
  serializer: tp.Optional[BaseSerializer] = None,
228
- connection: tp.Optional["sqlite3.Connection"] = None,
228
+ connection: tp.Optional[sqlite3.Connection] = None,
229
229
  ttl: tp.Optional[tp.Union[int, float]] = None,
230
230
  ) -> None:
231
231
  if sqlite3 is None: # pragma: no cover
232
232
  raise RuntimeError(
233
- (
234
- f"The `{type(self).__name__}` was used, but the required packages were not found. "
235
- "Check that you have `Hishel` installed with the `sqlite` extension as shown.\n"
236
- "```pip install hishel[sqlite]```"
237
- )
233
+ f"The `{type(self).__name__}` was used, but the required packages were not found. "
234
+ "Check that you have `Hishel` installed with the `sqlite` extension as shown.\n"
235
+ "```pip install hishel[sqlite]```"
238
236
  )
239
237
  super().__init__(serializer, ttl)
240
238
 
@@ -249,7 +247,7 @@ class SQLiteStorage(BaseStorage):
249
247
  if not self._connection: # pragma: no cover
250
248
  self._connection = sqlite3.connect(".hishel.sqlite", check_same_thread=False)
251
249
  self._connection.execute(
252
- ("CREATE TABLE IF NOT EXISTS cache(key TEXT, data BLOB, date_created REAL)")
250
+ "CREATE TABLE IF NOT EXISTS cache(key TEXT, data BLOB, date_created REAL)"
253
251
  )
254
252
  self._connection.commit()
255
253
  self._setup_completed = True
@@ -363,16 +361,14 @@ class RedisStorage(BaseStorage):
363
361
  def __init__(
364
362
  self,
365
363
  serializer: tp.Optional[BaseSerializer] = None,
366
- client: tp.Optional["redis.Redis"] = None, # type: ignore
364
+ client: tp.Optional[redis.Redis] = None, # type: ignore
367
365
  ttl: tp.Optional[tp.Union[int, float]] = None,
368
366
  ) -> None:
369
367
  if redis is None: # pragma: no cover
370
368
  raise RuntimeError(
371
- (
372
- f"The `{type(self).__name__}` was used, but the required packages were not found. "
373
- "Check that you have `Hishel` installed with the `redis` extension as shown.\n"
374
- "```pip install hishel[redis]```"
375
- )
369
+ f"The `{type(self).__name__}` was used, but the required packages were not found. "
370
+ "Check that you have `Hishel` installed with the `redis` extension as shown.\n"
371
+ "```pip install hishel[redis]```"
376
372
  )
377
373
  super().__init__(serializer, ttl)
378
374
 
@@ -424,7 +420,9 @@ class RedisStorage(BaseStorage):
424
420
 
425
421
  ttl_in_milliseconds = self._client.pttl(key)
426
422
 
427
- if ttl_in_milliseconds == -2: # pragma: no cover
423
+ # -2: if the key does not exist in Redis
424
+ # -1: if the key exists in Redis but has no expiration
425
+ if ttl_in_milliseconds == -2 or ttl_in_milliseconds == -1: # pragma: no cover
428
426
  self.store(key, response, request, metadata)
429
427
  else:
430
428
  self._client.set(
@@ -597,11 +595,9 @@ class S3Storage(BaseStorage): # pragma: no cover
597
595
 
598
596
  if boto3 is None: # pragma: no cover
599
597
  raise RuntimeError(
600
- (
601
- f"The `{type(self).__name__}` was used, but the required packages were not found. "
602
- "Check that you have `Hishel` installed with the `s3` extension as shown.\n"
603
- "```pip install hishel[s3]```"
604
- )
598
+ f"The `{type(self).__name__}` was used, but the required packages were not found. "
599
+ "Check that you have `Hishel` installed with the `s3` extension as shown.\n"
600
+ "```pip install hishel[s3]```"
605
601
  )
606
602
 
607
603
  self._bucket_name = bucket_name
@@ -141,6 +141,7 @@ class CacheTransport(httpx.BaseTransport):
141
141
  response=res,
142
142
  request=httpcore_request,
143
143
  cached=True,
144
+ revalidated=False,
144
145
  metadata=metadata,
145
146
  )
146
147
 
@@ -166,6 +167,7 @@ class CacheTransport(httpx.BaseTransport):
166
167
  response=stored_response,
167
168
  request=httpcore_request,
168
169
  cached=True,
170
+ revalidated=False,
169
171
  metadata=metadata,
170
172
  )
171
173
  raise # pragma: no cover
@@ -191,6 +193,7 @@ class CacheTransport(httpx.BaseTransport):
191
193
  response=final_httpcore_response,
192
194
  request=httpcore_request,
193
195
  cached=revalidation_response.status_code == 304,
196
+ revalidated=True,
194
197
  metadata=metadata,
195
198
  )
196
199
 
@@ -217,6 +220,7 @@ class CacheTransport(httpx.BaseTransport):
217
220
  response=httpcore_regular_response,
218
221
  request=httpcore_request,
219
222
  cached=False,
223
+ revalidated=False,
220
224
  )
221
225
 
222
226
  def _create_hishel_response(
@@ -225,6 +229,7 @@ class CacheTransport(httpx.BaseTransport):
225
229
  response: httpcore.Response,
226
230
  request: httpcore.Request,
227
231
  cached: bool,
232
+ revalidated: bool,
228
233
  metadata: Metadata | None = None,
229
234
  ) -> Response:
230
235
  if cached:
@@ -235,6 +240,7 @@ class CacheTransport(httpx.BaseTransport):
235
240
  response.extensions["cache_metadata"] = metadata # type: ignore[index]
236
241
  else:
237
242
  response.extensions["from_cache"] = False # type: ignore[index]
243
+ response.extensions["revalidated"] = revalidated # type: ignore[index]
238
244
  return Response(
239
245
  status_code=response.status,
240
246
  headers=response.headers,
@@ -246,7 +252,7 @@ class CacheTransport(httpx.BaseTransport):
246
252
  self._storage.close()
247
253
  self._transport.close()
248
254
 
249
- def __enter__(self) -> "Self":
255
+ def __enter__(self) -> Self:
250
256
  return self
251
257
 
252
258
  def __exit__(
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