diff options
author | Jordan Cook <jordan.cook@pioneer.com> | 2022-05-03 14:52:37 -0500 |
---|---|---|
committer | Jordan Cook <jordan.cook@pioneer.com> | 2022-05-03 14:52:37 -0500 |
commit | 7f4c20525770d463c5526fa601e8e3b5472cf2e6 (patch) | |
tree | 98bf214cb4ad9395f136a6a93010f0ada54e3a20 | |
parent | a300510c4ba087fd41d31c379a9d03e3b80b55aa (diff) | |
download | requests-cache-7f4c20525770d463c5526fa601e8e3b5472cf2e6.tar.gz |
Add always_revalidate session option
-rw-r--r-- | docs/user_guide/expiration.md | 9 | ||||
-rwxr-xr-x | requests_cache/models/response.py | 4 | ||||
-rw-r--r-- | requests_cache/policy/actions.py | 3 | ||||
-rw-r--r-- | requests_cache/policy/settings.py | 1 | ||||
-rw-r--r-- | requests_cache/session.py | 4 | ||||
-rw-r--r-- | tests/unit/test_session.py | 26 |
6 files changed, 41 insertions, 6 deletions
diff --git a/docs/user_guide/expiration.md b/docs/user_guide/expiration.md index a53da87..3257571 100644 --- a/docs/user_guide/expiration.md +++ b/docs/user_guide/expiration.md @@ -181,6 +181,15 @@ Example: >>> assert response_2.from_cache is False ``` +### Validation-Only Requests +If you want to always send a conditional request before using a cached response, you can use the +session setting `always_revalidate`: +```python +>>> session = CachedSession(always_revalidate=True) +``` + +Unlike the `refresh` option, this only affects cached responses with a validator. + ### Cache-Only Requests If you want to only use cached responses without making any real requests, you can use the `only_if_cached` option. This essentially uses your cache in "offline mode". If a response isn't diff --git a/requests_cache/models/response.py b/requests_cache/models/response.py index 7d40998..09c7e8c 100755 --- a/requests_cache/models/response.py +++ b/requests_cache/models/response.py @@ -28,9 +28,10 @@ class BaseResponse(Response): provide type hints for extra cache-related attributes that are added to non-cached responses. """ - cache_key: Optional[str] = None created_at: datetime = field(factory=datetime.utcnow) expires: Optional[datetime] = field(default=None) + cache_key: Optional[str] = None # Not serialized; set by BaseCache.get_response() + revalidated: bool = False # Not serialized; set by CacheActions.update_revalidated_response() @property def from_cache(self) -> bool: @@ -63,7 +64,6 @@ class CachedResponse(BaseResponse, RichMixin): _content: bytes = field(default=None) _next: Optional[CachedRequest] = field(default=None) - cache_key: Optional[str] = None # Not serialized; set by BaseCache.get_response() cookies: RequestsCookieJar = field(factory=RequestsCookieJar) created_at: datetime = field(factory=datetime.utcnow) elapsed: timedelta = field(factory=timedelta) diff --git a/requests_cache/policy/actions.py b/requests_cache/policy/actions.py index fd61507..cdacefd 100644 --- a/requests_cache/policy/actions.py +++ b/requests_cache/policy/actions.py @@ -203,6 +203,7 @@ class CacheActions(RichMixin): logger.debug(f'Response for URL {response.request.url} has not been modified') cached_response.expires = self.expires cached_response.headers.update(response.headers) + cached_response.revalidated = True return cached_response def _update_from_response_headers(self, directives: CacheDirectives): @@ -230,7 +231,7 @@ class CacheActions(RichMixin): or self._refresh or directives.no_cache or directives.must_revalidate - and directives.max_age == 0 + or (self._settings.always_revalidate and directives.has_validator) ) # Add the appropriate validation headers, if needed diff --git a/requests_cache/policy/settings.py b/requests_cache/policy/settings.py index 7888e45..2f9b9fe 100644 --- a/requests_cache/policy/settings.py +++ b/requests_cache/policy/settings.py @@ -25,6 +25,7 @@ class CacheSettings(RichMixin): allowable_codes: Iterable[int] = field(default=DEFAULT_STATUS_CODES) allowable_methods: Iterable[str] = field(default=DEFAULT_METHODS) + always_revalidate: bool = field(default=None) cache_control: bool = field(default=False) disabled: bool = field(default=False) expire_after: ExpirationTime = field(default=None) diff --git a/requests_cache/session.py b/requests_cache/session.py index de0a0cd..58b35ea 100644 --- a/requests_cache/session.py +++ b/requests_cache/session.py @@ -51,6 +51,7 @@ class CacheMixin(MIXIN_BASE): cache_control: bool = False, allowable_codes: Iterable[int] = DEFAULT_STATUS_CODES, allowable_methods: Iterable[str] = DEFAULT_METHODS, + always_revalidate: bool = False, ignored_parameters: Iterable[str] = DEFAULT_IGNORED_PARAMS, match_headers: Union[Iterable[str], bool] = False, filter_fn: FilterCallback = None, @@ -65,6 +66,7 @@ class CacheMixin(MIXIN_BASE): cache_control=cache_control, allowable_codes=allowable_codes, allowable_methods=allowable_methods, + always_revalidate=always_revalidate, ignored_parameters=ignored_parameters, match_headers=match_headers, filter_fn=filter_fn, @@ -326,6 +328,8 @@ class CachedSession(CacheMixin, OriginalSession): cache_control: Use Cache-Control and other response headers to set expiration allowable_codes: Only cache responses with one of these status codes allowable_methods: Cache only responses for one of these HTTP methods + always_revalidate: Revalidate with the server for every request, even if the cached response + is not expired match_headers: Match request headers when reading from the cache; may be either ``True`` or a list of specific headers to match ignored_parameters: Request paramters, headers, and/or JSON body params to exclude from both diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py index ad2ecbd..6e37ded 100644 --- a/tests/unit/test_session.py +++ b/tests/unit/test_session.py @@ -801,8 +801,28 @@ def test_request_refresh(mock_session): assert response_1.from_cache is False assert response_2.from_cache is True - assert response_3.from_cache is True - assert response_4.from_cache is True + assert response_3.from_cache is True and response_3.revalidated is True + assert response_4.from_cache is True and response_4.revalidated is False + + # Expect expiration to get reset after revalidation + assert response_2.expires < response_4.expires + + +def test_request_always_revalidate(mock_session): + """The session always_revalidate option should send a conditional request, if possible""" + mock_session.settings.expire_after = 60 + response_1 = mock_session.get(MOCKED_URL_ETAG) + response_2 = mock_session.get(MOCKED_URL_ETAG) + mock_session.mock_adapter.register_uri('GET', MOCKED_URL_ETAG, status_code=304) + + mock_session.settings.always_revalidate = True + response_3 = mock_session.get(MOCKED_URL_ETAG) + response_4 = mock_session.get(MOCKED_URL_ETAG) + + assert response_1.from_cache is False + assert response_2.from_cache is True + assert response_3.from_cache is True and response_3.revalidated is True + assert response_4.from_cache is True and response_4.revalidated is True # Expect expiration to get reset after revalidation assert response_2.expires < response_4.expires @@ -821,7 +841,7 @@ def test_request_refresh__no_validator(mock_session): assert response_1.from_cache is False assert response_2.from_cache is True - assert response_3.from_cache is True + assert response_3.from_cache is True and response_3.revalidated is False assert response_2.expires == response_4.expires |