summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Cook <jordan.cook@pioneer.com>2021-09-13 17:55:05 -0500
committerJordan Cook <jordan.cook@pioneer.com>2021-09-18 17:43:12 -0500
commit252868973c81d13b6407f2e74513d5e8b0a37a55 (patch)
tree77efc11226679f3ef71861bc886b67b2291143ac
parentdbee4da8293e8410deeb77ddb5c2a6f5cc4c31eb (diff)
downloadrequests-cache-252868973c81d13b6407f2e74513d5e8b0a37a55.tar.gz
Use only integers for expire_after values in seconds, since this is what Cache-Control: max-age requires
-rw-r--r--requests_cache/cache_control.py5
-rw-r--r--tests/integration/base_cache_test.py26
-rw-r--r--tests/unit/test_session.py22
3 files changed, 28 insertions, 25 deletions
diff --git a/requests_cache/cache_control.py b/requests_cache/cache_control.py
index 79add63..434c5c2 100644
--- a/requests_cache/cache_control.py
+++ b/requests_cache/cache_control.py
@@ -14,6 +14,7 @@ from datetime import datetime, timedelta, timezone
from email.utils import parsedate_to_datetime
from fnmatch import fnmatch
from logging import getLogger
+from math import ceil
from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Tuple, Union
from attr import define, field
@@ -152,10 +153,10 @@ def get_expiration_datetime(expire_after: ExpirationTime) -> Optional[datetime]:
return datetime.utcnow() + expire_after
-def get_expiration_seconds(expire_after: ExpirationTime) -> Optional[float]:
+def get_expiration_seconds(expire_after: ExpirationTime) -> Optional[int]:
"""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
+ return ceil((expires - datetime.utcnow()).total_seconds()) if expires else None
def get_cache_directives(headers: Mapping) -> Dict:
diff --git a/tests/integration/base_cache_test.py b/tests/integration/base_cache_test.py
index 3542cd5..b435b4f 100644
--- a/tests/integration/base_cache_test.py
+++ b/tests/integration/base_cache_test.py
@@ -132,30 +132,32 @@ class BaseCacheTest:
response_3 = get_json(httpbin('cookies/set/test3/test4'))
assert response_3 == get_json(httpbin('cookies'))
- @pytest.mark.parametrize('cache_control', [True, False])
@pytest.mark.parametrize(
- 'request_headers, expected_expiration',
+ 'cache_control, request_headers, expected_expiration',
[
- ({}, 60),
- ({'Cache-Control': 'max-age=360'}, 360),
- ({'Cache-Control': 'no-store'}, None),
- ({'Expires': HTTPDATE_STR, 'Cache-Control': 'max-age=360'}, 360),
+ (True, {}, 60),
+ (True, {'Cache-Control': 'max-age=360'}, 60),
+ (True, {'Cache-Control': 'no-store'}, None),
+ (True, {'Expires': HTTPDATE_STR, 'Cache-Control': 'max-age=360'}, 60),
+ (False, {}, None),
+ (False, {'Cache-Control': 'max-age=360'}, 360),
+ (False, {'Cache-Control': 'no-store'}, None),
+ (False, {'Expires': HTTPDATE_STR, 'Cache-Control': 'max-age=360'}, 360),
],
)
- def test_cache_control_expiration(self, request_headers, expected_expiration, cache_control):
+ def test_cache_control_expiration(self, cache_control, request_headers, expected_expiration):
"""Test cache headers for both requests and responses. The `/cache/{seconds}` endpoint returns
Cache-Control headers, which should be used unless request headers are sent.
No headers should be used if `cache_control=False`.
"""
session = self.init_session(cache_control=cache_control)
- now = datetime.utcnow()
session.get(httpbin('cache/60'), headers=request_headers)
response = session.get(httpbin('cache/60'), headers=request_headers)
- if expected_expiration is None or cache_control is False:
+ if expected_expiration is None:
assert response.expires is None
else:
- assert_delta_approx_equal(now, response.expires, expected_expiration)
+ assert_delta_approx_equal(datetime.utcnow(), response.expires, expected_expiration)
@pytest.mark.parametrize(
'cached_response_headers, expected_from_cache',
@@ -205,13 +207,13 @@ class BaseCacheTest:
assert session.post(httpbin('post'), files={'file1': BytesIO(b'10' * 1024)}).from_cache
def test_remove_expired_responses(self):
- session = self.init_session(expire_after=0.01)
+ session = self.init_session(expire_after=1)
# Populate the cache with several responses that should expire immediately
for response_format in HTTPBIN_FORMATS:
session.get(httpbin(response_format))
session.get(httpbin('redirect/1'))
- sleep(0.01)
+ sleep(1)
# Cache a response + redirects, which should be the only non-expired cache items
session.get(httpbin('get'), expire_after=-1)
diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py
index 697e55a..73f522a 100644
--- a/tests/unit/test_session.py
+++ b/tests/unit/test_session.py
@@ -636,18 +636,20 @@ def test_remove_expired_responses__per_request(mock_session):
mock_session.mock_adapter.register_uri('GET', second_url, status_code=200)
mock_session.mock_adapter.register_uri('GET', third_url, status_code=200)
mock_session.get(MOCKED_URL)
- mock_session.get(second_url, expire_after=0.4)
- mock_session.get(third_url, expire_after=0.8)
+ mock_session.get(second_url, expire_after=1)
+ mock_session.get(third_url, expire_after=2)
# All 3 responses should still be cached
mock_session.remove_expired_responses()
+ for response in mock_session.cache.responses.values():
+ print('Expires:', response.expires - datetime.utcnow() if response.expires else None)
assert len(mock_session.cache.responses) == 3
- # One should be expired after 0.4s, and another should be expired after 0.8s
- time.sleep(0.4)
+ # One should be expired after 1s, and another should be expired after 2s
+ time.sleep(1)
mock_session.remove_expired_responses()
assert len(mock_session.cache.responses) == 2
- time.sleep(0.4)
+ time.sleep(2)
mock_session.remove_expired_responses()
assert len(mock_session.cache.responses) == 1
@@ -655,21 +657,19 @@ def test_remove_expired_responses__per_request(mock_session):
def test_per_request__expiration(mock_session):
"""No per-session expiration is set, but then overridden with per-request expiration"""
mock_session.expire_after = None
- response = mock_session.get(MOCKED_URL, expire_after=0.01)
+ response = mock_session.get(MOCKED_URL, expire_after=1)
assert response.from_cache is False
- time.sleep(0.01)
+ time.sleep(1)
response = mock_session.get(MOCKED_URL)
assert response.from_cache is False
def test_per_request__no_expiration(mock_session):
"""A per-session expiration is set, but then overridden with no per-request expiration"""
- mock_session.expire_after = 0.01
+ mock_session.expire_after = 1
response = mock_session.get(MOCKED_URL, expire_after=-1)
assert response.from_cache is False
- time.sleep(0.01)
- response = mock_session.get(MOCKED_URL)
- assert response.from_cache is True
+ assert response.expires is None
def test_unpickle_errors(mock_session):