diff options
| author | Jordan Cook <jordan.cook@pioneer.com> | 2021-09-13 17:38:12 -0500 |
|---|---|---|
| committer | Jordan Cook <jordan.cook@pioneer.com> | 2021-09-18 17:43:12 -0500 |
| commit | dbee4da8293e8410deeb77ddb5c2a6f5cc4c31eb (patch) | |
| tree | 8b0cdebf6f6d68b96f9e158dfa43df1813086455 /requests_cache/cache_control.py | |
| parent | 1fa0fbb715e5964eb8aee467721f6b5a0f4cfdb6 (diff) | |
| download | requests-cache-dbee4da8293e8410deeb77ddb5c2a6f5cc4c31eb.tar.gz | |
Make per-request expiration thread-safe by passing via request headers instead of session attribute, and use Cache-Control request headers by default
Diffstat (limited to 'requests_cache/cache_control.py')
| -rw-r--r-- | requests_cache/cache_control.py | 71 |
1 files changed, 25 insertions, 46 deletions
diff --git a/requests_cache/cache_control.py b/requests_cache/cache_control.py index b3d1703..79add63 100644 --- a/requests_cache/cache_control.py +++ b/requests_cache/cache_control.py @@ -47,7 +47,7 @@ class CacheActions: If multiple sources provide an expiration time, they will be used in the following order of precedence: - 1. Cache-Control request headers (if enabled) + 1. Cache-Control request headers 2. Cache-Control response headers (if enabled) 3. Per-request expiration 4. Per-URL expiration @@ -67,53 +67,26 @@ class CacheActions: cache_key: str, request: PreparedRequest, cache_control: bool = False, - **kwargs, - ): - """Initialize from request info and cache settings""" - if cache_control and has_cache_headers(request.headers): - return cls.from_headers(cache_key, request.headers) - else: - return cls.from_settings(cache_key, request.url, cache_control=cache_control, **kwargs) - - @classmethod - def from_headers(cls, cache_key: str, headers: Mapping): - """Initialize from request headers""" - directives = get_cache_directives(headers) - do_not_cache = directives.get('max-age') == DO_NOT_CACHE - return cls( - cache_control=True, - cache_key=cache_key, - expire_after=directives.get('max-age'), - skip_read=do_not_cache or 'no-store' in directives or 'no-cache' in directives, - skip_write=do_not_cache or 'no-store' in directives, - ) - - @classmethod - def from_settings( - cls, - cache_key: str, - url: str = None, - cache_control: bool = True, - request_expire_after: ExpirationTime = None, session_expire_after: ExpirationTime = None, urls_expire_after: ExpirationPatterns = None, **kwargs, ): - """Initialize from cache settings""" + """Initialize from request info and cache settings""" # Check expire_after values in order of precedence + directives = get_cache_directives(request.headers) expire_after = coalesce( - request_expire_after, - get_url_expiration(url, urls_expire_after), + directives.get('max-age'), + get_url_expiration(request.url, urls_expire_after), session_expire_after, ) - do_not_cache = expire_after == DO_NOT_CACHE + return cls( cache_control=cache_control, cache_key=cache_key, expire_after=expire_after, - skip_read=do_not_cache, - skip_write=do_not_cache, + skip_read=do_not_cache or 'no-store' in directives or 'no-cache' in directives, + skip_write=do_not_cache or 'no-store' in directives, ) @property @@ -122,8 +95,11 @@ class CacheActions: return get_expiration_datetime(self.expire_after) def update_from_cached_response(self, response: CachedResponse): - """Used after fetching a cached response, but before potentially sending a new request. - Check for relevant cache headers on a cached response, and set corresponding request headers. + """Check for relevant cache headers on a cached response, and set corresponding request + headers for a conditional request, if possible. + + Used after fetching a cached response, but before potentially sending a new request + (if expired). """ if not response or not response.is_expired: return @@ -135,15 +111,18 @@ class CacheActions: self.request_headers = {k: v for k, v in self.request_headers.items() if v} def update_from_response(self, response: Response): - """Used after receiving a new response but before saving it to the cache. - Update expiration + actions based on response headers, if not previously set. + """Update expiration + actions based on response headers, if not previously set. + + Used after receiving a new response but before saving it to the cache. """ if not self.cache_control or not response: return directives = get_cache_directives(response.headers) do_not_cache = directives.get('max-age') == DO_NOT_CACHE - self.expire_after = coalesce(self.expires, directives.get('max-age'), directives.get('expires')) + self.expire_after = coalesce( + directives.get('max-age'), directives.get('expires'), self.expire_after + ) self.skip_write = self.skip_write or do_not_cache or 'no-store' in directives @@ -173,6 +152,12 @@ def get_expiration_datetime(expire_after: ExpirationTime) -> Optional[datetime]: return datetime.utcnow() + expire_after +def get_expiration_seconds(expire_after: ExpirationTime) -> Optional[float]: + """Convert an expiration value in any supported format to an expiration time in seconds""" + expires = get_expiration_datetime(expire_after) + return (expires - datetime.utcnow()).total_seconds() if expires else None + + def get_cache_directives(headers: Mapping) -> Dict: """Get all Cache-Control directives, and handle multiple headers and comma-separated lists""" if not headers: @@ -200,12 +185,6 @@ def get_url_expiration( return None -def has_cache_headers(headers: Mapping) -> bool: - """Determine if headers contain supported cache directives""" - has_cache_control = any([d in headers.get('Cache-Control', '') for d in CACHE_DIRECTIVES]) - return has_cache_control or bool(headers.get('Expires')) - - def parse_http_date(value: str) -> Optional[datetime]: """Attempt to parse an HTTP (RFC 5322-compatible) timestamp""" try: |
