Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-CacheControl for
openSUSE:Factory checked in at 2022-08-03 21:16:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-CacheControl (Old)
and /work/SRC/openSUSE:Factory/.python-CacheControl.new.1533 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-CacheControl"
Wed Aug 3 21:16:28 2022 rev:10 rq:992331 version:0.12.11
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-CacheControl/python-CacheControl.changes
2021-12-09 19:45:19.281124939 +0100
+++
/work/SRC/openSUSE:Factory/.python-CacheControl.new.1533/python-CacheControl.changes
2022-08-03 21:16:37.163429690 +0200
@@ -1,0 +2,8 @@
+Tue Aug 2 10:26:39 UTC 2022 - Otto Hollmann <[email protected]>
+
+- Update to v0.12.11
+ * Added new variant of FileCache, SeparateBodyFileCache, which uses
+ less memory by storing the body in a separate file than metadata,
+ and streaming data in and out directly to/from that file.
+
+-------------------------------------------------------------------
Old:
----
CacheControl-0.12.10.tar.gz
New:
----
CacheControl-0.12.11.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-CacheControl.spec ++++++
--- /var/tmp/diff_new_pack.5Vp38i/_old 2022-08-03 21:16:37.651430971 +0200
+++ /var/tmp/diff_new_pack.5Vp38i/_new 2022-08-03 21:16:37.655430981 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-CacheControl
#
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python3-%{**}}
%define skip_python2 1
Name: python-CacheControl
-Version: 0.12.10
+Version: 0.12.11
Release: 0
Summary: Caching library for Python requests
License: Apache-2.0
++++++ CacheControl-0.12.10.tar.gz -> CacheControl-0.12.11.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/.bumpversion.cfg
new/cachecontrol-0.12.11/.bumpversion.cfg
--- old/cachecontrol-0.12.10/.bumpversion.cfg 2021-11-05 18:07:09.000000000
+0100
+++ new/cachecontrol-0.12.11/.bumpversion.cfg 2022-04-19 19:20:49.000000000
+0200
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 0.12.10
+current_version = 0.12.11
files = setup.py cachecontrol/__init__.py docs/conf.py
commit = True
tag = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/.gitignore
new/cachecontrol-0.12.11/.gitignore
--- old/cachecontrol-0.12.10/.gitignore 2021-11-05 18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/.gitignore 2022-04-19 19:20:49.000000000 +0200
@@ -14,4 +14,6 @@
.Python
docs/_build
build/
-.tox
\ No newline at end of file
+.tox
+.venv
+web_cache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/cachecontrol/__init__.py
new/cachecontrol-0.12.11/cachecontrol/__init__.py
--- old/cachecontrol-0.12.10/cachecontrol/__init__.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/cachecontrol/__init__.py 2022-04-19
19:20:49.000000000 +0200
@@ -8,7 +8,7 @@
"""
__author__ = "Eric Larson"
__email__ = "[email protected]"
-__version__ = "0.12.10"
+__version__ = "0.12.11"
from .wrapper import CacheControl
from .adapter import CacheControlAdapter
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/cachecontrol/cache.py
new/cachecontrol-0.12.11/cachecontrol/cache.py
--- old/cachecontrol-0.12.10/cachecontrol/cache.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/cachecontrol/cache.py 2022-04-19
19:20:49.000000000 +0200
@@ -41,3 +41,25 @@
with self.lock:
if key in self.data:
self.data.pop(key)
+
+
+class SeparateBodyBaseCache(BaseCache):
+ """
+ In this variant, the body is not stored mixed in with the metadata, but is
+ passed in (as a bytes-like object) in a separate call to ``set_body()``.
+
+ That is, the expected interaction pattern is::
+
+ cache.set(key, serialized_metadata)
+ cache.set_body(key)
+
+ Similarly, the body should be loaded separately via ``get_body()``.
+ """
+ def set_body(self, key, body):
+ raise NotImplementedError()
+
+ def get_body(self, key):
+ """
+ Return the body as file-like object.
+ """
+ raise NotImplementedError()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/cachecontrol/caches/__init__.py
new/cachecontrol-0.12.11/cachecontrol/caches/__init__.py
--- old/cachecontrol-0.12.10/cachecontrol/caches/__init__.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/cachecontrol/caches/__init__.py 2022-04-19
19:20:49.000000000 +0200
@@ -2,5 +2,8 @@
#
# SPDX-License-Identifier: Apache-2.0
-from .file_cache import FileCache # noqa
-from .redis_cache import RedisCache # noqa
+from .file_cache import FileCache, SeparateBodyFileCache
+from .redis_cache import RedisCache
+
+
+__all__ = ["FileCache", "SeparateBodyFileCache", "RedisCache"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cachecontrol-0.12.10/cachecontrol/caches/file_cache.py
new/cachecontrol-0.12.11/cachecontrol/caches/file_cache.py
--- old/cachecontrol-0.12.10/cachecontrol/caches/file_cache.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/cachecontrol/caches/file_cache.py 2022-04-19
19:20:49.000000000 +0200
@@ -6,7 +6,7 @@
import os
from textwrap import dedent
-from ..cache import BaseCache
+from ..cache import BaseCache, SeparateBodyBaseCache
from ..controller import CacheController
try:
@@ -57,7 +57,8 @@
raise
-class FileCache(BaseCache):
+class _FileCacheMixin:
+ """Shared implementation for both FileCache variants."""
def __init__(
self,
@@ -120,20 +121,25 @@
def set(self, key, value, expires=None):
name = self._fn(key)
+ self._write(name, value)
+ def _write(self, path, data: bytes):
+ """
+ Safely write the data to the given path.
+ """
# Make sure the directory exists
try:
- os.makedirs(os.path.dirname(name), self.dirmode)
+ os.makedirs(os.path.dirname(path), self.dirmode)
except (IOError, OSError):
pass
- with self.lock_class(name) as lock:
+ with self.lock_class(path) as lock:
# Write our actual file
with _secure_open_write(lock.path, self.filemode) as fh:
- fh.write(value)
+ fh.write(data)
- def delete(self, key):
- name = self._fn(key)
+ def _delete(self, key, suffix):
+ name = self._fn(key) + suffix
if not self.forever:
try:
os.remove(name)
@@ -141,6 +147,38 @@
pass
+class FileCache(_FileCacheMixin, BaseCache):
+ """
+ Traditional FileCache: body is stored in memory, so not suitable for large
+ downloads.
+ """
+
+ def delete(self, key):
+ self._delete(key, "")
+
+
+class SeparateBodyFileCache(_FileCacheMixin, SeparateBodyBaseCache):
+ """
+ Memory-efficient FileCache: body is stored in a separate file, reducing
+ peak memory usage.
+ """
+
+ def get_body(self, key):
+ name = self._fn(key) + ".body"
+ try:
+ return open(name, "rb")
+ except FileNotFoundError:
+ return None
+
+ def set_body(self, key, body):
+ name = self._fn(key) + ".body"
+ self._write(name, body)
+
+ def delete(self, key):
+ self._delete(key, "")
+ self._delete(key, ".body")
+
+
def url_to_file_path(url, filecache):
"""Return the file cache path based on the URL.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/cachecontrol-0.12.10/cachecontrol/caches/redis_cache.py
new/cachecontrol-0.12.11/cachecontrol/caches/redis_cache.py
--- old/cachecontrol-0.12.10/cachecontrol/caches/redis_cache.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/cachecontrol/caches/redis_cache.py 2022-04-19
19:20:49.000000000 +0200
@@ -19,9 +19,11 @@
def set(self, key, value, expires=None):
if not expires:
self.conn.set(key, value)
- else:
+ elif isinstance(expires, datetime):
expires = expires - datetime.utcnow()
self.conn.setex(key, int(expires.total_seconds()), value)
+ else:
+ self.conn.setex(key, expires, value)
def delete(self, key):
self.conn.delete(key)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/cachecontrol/controller.py
new/cachecontrol-0.12.11/cachecontrol/controller.py
--- old/cachecontrol-0.12.10/cachecontrol/controller.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/cachecontrol/controller.py 2022-04-19
19:20:49.000000000 +0200
@@ -13,7 +13,7 @@
from requests.structures import CaseInsensitiveDict
-from .cache import DictCache
+from .cache import DictCache, SeparateBodyBaseCache
from .serialize import Serializer
@@ -27,15 +27,14 @@
def parse_uri(uri):
"""Parses a URI using the regex given in Appendix B of RFC 3986.
- (scheme, authority, path, query, fragment) = parse_uri(uri)
+ (scheme, authority, path, query, fragment) = parse_uri(uri)
"""
groups = URI.match(uri).groups()
return (groups[1], groups[3], groups[4], groups[6], groups[8])
class CacheController(object):
- """An interface to see if request should cached or not.
- """
+ """An interface to see if request should cached or not."""
def __init__(
self, cache=None, cache_etags=True, serializer=None, status_codes=None
@@ -147,8 +146,13 @@
logger.debug("No cache entry available")
return False
+ if isinstance(self.cache, SeparateBodyBaseCache):
+ body_file = self.cache.get_body(cache_url)
+ else:
+ body_file = None
+
# Check whether it can be deserialized
- resp = self.serializer.loads(request, cache_data)
+ resp = self.serializer.loads(request, cache_data, body_file)
if not resp:
logger.warning("Cache entry deserialization failed, entry ignored")
return False
@@ -251,6 +255,26 @@
return new_headers
+ def _cache_set(self, cache_url, request, response, body=None,
expires_time=None):
+ """
+ Store the data in the cache.
+ """
+ if isinstance(self.cache, SeparateBodyBaseCache):
+ # We pass in the body separately; just put a placeholder empty
+ # string in the metadata.
+ self.cache.set(
+ cache_url,
+ self.serializer.dumps(request, response, b""),
+ expires=expires_time,
+ )
+ self.cache.set_body(cache_url, body)
+ else:
+ self.cache.set(
+ cache_url,
+ self.serializer.dumps(request, response, body),
+ expires=expires_time,
+ )
+
def cache_response(self, request, response, body=None, status_codes=None):
"""
Algorithm for caching requests.
@@ -326,17 +350,13 @@
logger.debug("etag object cached for {0}
seconds".format(expires_time))
logger.debug("Caching due to etag")
- self.cache.set(
- cache_url,
- self.serializer.dumps(request, response, body),
- expires=expires_time,
- )
+ self._cache_set(cache_url, request, response, body, expires_time)
# Add to the cache any permanent redirects. We do this before looking
# that the Date headers.
elif int(response.status) in PERMANENT_REDIRECT_STATUSES:
logger.debug("Caching permanent redirect")
- self.cache.set(cache_url, self.serializer.dumps(request, response,
b""))
+ self._cache_set(cache_url, request, response, b"")
# Add to the cache if the response headers demand it. If there
# is no date header then we can't do anything about expiring
@@ -347,10 +367,12 @@
if "max-age" in cc and cc["max-age"] > 0:
logger.debug("Caching b/c date exists and max-age > 0")
expires_time = cc["max-age"]
- self.cache.set(
+ self._cache_set(
cache_url,
- self.serializer.dumps(request, response, body),
- expires=expires_time,
+ request,
+ response,
+ body,
+ expires_time,
)
# If the request can expire, it means we should cache it
@@ -368,10 +390,12 @@
expires_time
)
)
- self.cache.set(
+ self._cache_set(
cache_url,
- self.serializer.dumps(request, response, body=body),
- expires=expires_time,
+ request,
+ response,
+ body,
+ expires_time,
)
def update_cached_response(self, request, response):
@@ -410,6 +434,6 @@
cached_response.status = 200
# update our cache
- self.cache.set(cache_url, self.serializer.dumps(request,
cached_response))
+ self._cache_set(cache_url, request, cached_response)
return cached_response
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/cachecontrol/serialize.py
new/cachecontrol-0.12.11/cachecontrol/serialize.py
--- old/cachecontrol-0.12.10/cachecontrol/serialize.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/cachecontrol/serialize.py 2022-04-19
19:20:49.000000000 +0200
@@ -44,7 +44,7 @@
# enough to have msgpack know the difference.
data = {
u"response": {
- u"body": body,
+ u"body": body, # Empty bytestring if body is stored separately
u"headers": dict(
(text_type(k), text_type(v)) for k, v in
response.headers.items()
),
@@ -69,7 +69,7 @@
return b",".join([b"cc=4", msgpack.dumps(data, use_bin_type=True)])
- def loads(self, request, data):
+ def loads(self, request, data, body_file=None):
# Short circuit if we've been given an empty set of data
if not data:
return
@@ -92,14 +92,14 @@
# Dispatch to the actual load method for the given version
try:
- return getattr(self, "_loads_v{}".format(ver))(request, data)
+ return getattr(self, "_loads_v{}".format(ver))(request, data,
body_file)
except AttributeError:
# This is a version we don't have a loads function for, so we'll
# just treat it as a miss and return None
return
- def prepare_response(self, request, cached):
+ def prepare_response(self, request, cached, body_file=None):
"""Verify our vary headers match and construct a real urllib3
HTTPResponse object.
"""
@@ -125,7 +125,10 @@
cached["response"]["headers"] = headers
try:
- body = io.BytesIO(body_raw)
+ if body_file is None:
+ body = io.BytesIO(body_raw)
+ else:
+ body = body_file
except TypeError:
# This can happen if cachecontrol serialized to v1 format (pickle)
# using Python 2. A Python 2 str(byte string) will be unpickled as
@@ -137,21 +140,22 @@
return HTTPResponse(body=body, preload_content=False,
**cached["response"])
- def _loads_v0(self, request, data):
+ def _loads_v0(self, request, data, body_file=None):
# The original legacy cache data. This doesn't contain enough
# information to construct everything we need, so we'll treat this as
# a miss.
return
- def _loads_v1(self, request, data):
+ def _loads_v1(self, request, data, body_file=None):
try:
cached = pickle.loads(data)
except ValueError:
return
- return self.prepare_response(request, cached)
+ return self.prepare_response(request, cached, body_file)
- def _loads_v2(self, request, data):
+ def _loads_v2(self, request, data, body_file=None):
+ assert body_file is None
try:
cached = json.loads(zlib.decompress(data).decode("utf8"))
except (ValueError, zlib.error):
@@ -169,18 +173,18 @@
for k, v in cached["vary"].items()
)
- return self.prepare_response(request, cached)
+ return self.prepare_response(request, cached, body_file)
- def _loads_v3(self, request, data):
+ def _loads_v3(self, request, data, body_file):
# Due to Python 2 encoding issues, it's impossible to know for sure
# exactly how to load v3 entries, thus we'll treat these as a miss so
# that they get rewritten out as v4 entries.
return
- def _loads_v4(self, request, data):
+ def _loads_v4(self, request, data, body_file=None):
try:
cached = msgpack.loads(data, raw=False)
except ValueError:
return
- return self.prepare_response(request, cached)
+ return self.prepare_response(request, cached, body_file)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/docs/conf.py
new/cachecontrol-0.12.11/docs/conf.py
--- old/cachecontrol-0.12.10/docs/conf.py 2021-11-05 18:07:09.000000000
+0100
+++ new/cachecontrol-0.12.11/docs/conf.py 2022-04-19 19:20:49.000000000
+0200
@@ -52,9 +52,9 @@
# built documents.
#
# The short X.Y version.
-version = "0.12.10"
+version = "0.12.11"
# The full version, including alpha/beta/rc tags.
-release = "0.12.10"
+release = "0.12.11"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/docs/release_notes.rst
new/cachecontrol-0.12.11/docs/release_notes.rst
--- old/cachecontrol-0.12.10/docs/release_notes.rst 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/docs/release_notes.rst 2022-04-19
19:20:49.000000000 +0200
@@ -7,7 +7,12 @@
Release Notes
===============
-0.13.0
+0.12.11
+=======
+
+* Added new variant of ``FileCache``, ``SeparateBodyFileCache``, which uses
less memory by storing the body in a separate file than metadata, and streaming
data in and out directly to/from that file. Implemented by [Itamar
Turner-Trauring](https://pythonspeed.com), work sponsored by
[G-Research](https://www.gresearch.co.uk/technology-innovation-and-open-source/).
+
+0.12.7
======
* Dropped support for Python 2.7, 3.4, 3.5.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/docs/storage.rst
new/cachecontrol-0.12.11/docs/storage.rst
--- old/cachecontrol-0.12.10/docs/storage.rst 2021-11-05 18:07:09.000000000
+0100
+++ new/cachecontrol-0.12.11/docs/storage.rst 2022-04-19 19:20:49.000000000
+0200
@@ -56,17 +56,33 @@
forever_cache = FileCache('.web_cache', forever=True)
sess = CacheControl(requests.Session(), forever_cache)
+SeparateBodyFileCache
+=====================
-:A Note About Pickle:
+This is similar to ``FileCache``, but far more memory efficient, and therefore
recommended if you expect to be caching large downloads.
+``FileCache`` results in memory usage that can be 2?? or 3?? of the downloaded
file, whereas ``SeparateBodyFileCache`` should have fixed memory usage.
- It should be noted that the `FileCache` uses pickle to store the
- cached response. Prior to `requests 2.1`_, `requests.Response`
- objects were not 'pickleable' due to the use of `IOBase` base
- classes in `urllib3` `HTTPResponse` objects. In CacheControl we work
- around this by patching the Response objects with the appropriate
- `__getstate__` and `__setstate__` methods when the requests version
- doesn't natively support Response pickling.
+The body of the request is stored in a separate file than metadata, and
streamed in and out.
+It requires `lockfile`_ be installed as it prevents multiple threads from
writing to the same file at the same time.
+
+.. note::
+
+ You can install this dependency automatically with pip
+ by requesting the *filecache* extra: ::
+
+ pip install cachecontrol[filecache]
+
+Here is an example of using the cache::
+
+ import requests
+ from cachecontrol import CacheControl
+ from cachecontrol.caches SeparateBodyFileCache
+
+ sess = CacheControl(requests.Session(),
+ cache=SeparatedBodyFileCache('.web_cache'))
+
+``SeparateBodyFileCache`` supports the same options as ``FileCache``.
RedisCache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/setup.py
new/cachecontrol-0.12.11/setup.py
--- old/cachecontrol-0.12.10/setup.py 2021-11-05 18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/setup.py 2022-04-19 19:20:49.000000000 +0200
@@ -6,7 +6,7 @@
long_description = open("README.rst").read()
-VERSION = "0.12.10"
+VERSION = "0.12.11"
setup_params = dict(
name="CacheControl",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/tests/test_cache_control.py
new/cachecontrol-0.12.11/tests/test_cache_control.py
--- old/cachecontrol-0.12.10/tests/test_cache_control.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/tests/test_cache_control.py 2022-04-19
19:20:49.000000000 +0200
@@ -21,7 +21,7 @@
def dumps(self, request, response):
return response
- def loads(self, request, data):
+ def loads(self, request, data, body_file=None):
return data
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/tests/test_etag.py
new/cachecontrol-0.12.11/tests/test_etag.py
--- old/cachecontrol-0.12.10/tests/test_etag.py 2021-11-05 18:07:09.000000000
+0100
+++ new/cachecontrol-0.12.11/tests/test_etag.py 2022-04-19 19:20:49.000000000
+0200
@@ -18,7 +18,7 @@
def dumps(self, request, response, body=None):
return response
- def loads(self, request, data):
+ def loads(self, request, data, body_file=None):
if data and getattr(data, "chunked", False):
data.chunked = False
return data
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/tests/test_max_age.py
new/cachecontrol-0.12.11/tests/test_max_age.py
--- old/cachecontrol-0.12.10/tests/test_max_age.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/tests/test_max_age.py 2022-04-19
19:20:49.000000000 +0200
@@ -15,7 +15,7 @@
def dumps(self, request, response, body=None):
return response
- def loads(self, request, data):
+ def loads(self, request, data, body_file=None):
if data and getattr(data, "chunked", False):
data.chunked = False
return data
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/tests/test_storage_filecache.py
new/cachecontrol-0.12.11/tests/test_storage_filecache.py
--- old/cachecontrol-0.12.10/tests/test_storage_filecache.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/tests/test_storage_filecache.py 2022-04-19
19:20:49.000000000 +0200
@@ -13,7 +13,7 @@
import pytest
import requests
from cachecontrol import CacheControl
-from cachecontrol.caches import FileCache
+from cachecontrol.caches import FileCache, SeparateBodyFileCache
from lockfile import LockFile
from lockfile.mkdirlockfile import MkdirLockFile
@@ -25,12 +25,14 @@
return "&{}={}".format(key, val)
-class TestStorageFileCache(object):
+class FileCacheTestsMixin(object):
+
+ FileCacheClass = None # Either FileCache or SeparateBodyFileCache
@pytest.fixture()
def sess(self, url, tmpdir):
self.url = url
- self.cache = FileCache(str(tmpdir))
+ self.cache = self.FileCacheClass(str(tmpdir))
sess = CacheControl(requests.Session(), cache=self.cache)
yield sess
@@ -94,7 +96,7 @@
def test_cant_use_dir_and_lock_class(self, tmpdir):
with pytest.raises(ValueError):
- FileCache(str(tmpdir), use_dir_lock=True, lock_class=object())
+ self.FileCacheClass(str(tmpdir), use_dir_lock=True,
lock_class=object())
@pytest.mark.parametrize(
("value", "expected"),
@@ -102,16 +104,16 @@
)
def test_simple_lockfile_arg(self, tmpdir, value, expected):
if value is not None:
- cache = FileCache(str(tmpdir), use_dir_lock=value)
+ cache = self.FileCacheClass(str(tmpdir), use_dir_lock=value)
else:
- cache = FileCache(str(tmpdir))
+ cache = self.FileCacheClass(str(tmpdir))
assert issubclass(cache.lock_class, expected)
cache.close()
def test_lock_class(self, tmpdir):
lock_class = object()
- cache = FileCache(str(tmpdir), lock_class=lock_class)
+ cache = self.FileCacheClass(str(tmpdir), lock_class=lock_class)
assert cache.lock_class is lock_class
cache.close()
@@ -126,3 +128,58 @@
url = self.url + "".join(sample(string.ascii_lowercase, randint(2, 4)))
sess.put(url)
assert True # test verifies no exceptions were raised
+
+
+class TestFileCache(FileCacheTestsMixin):
+ """
+ Tests for ``FileCache``.
+ """
+
+ FileCacheClass = FileCache
+
+ def test_body_stored_inline(self, sess):
+ """The body is stored together with the metadata."""
+ url = self.url + "cache_60"
+ response = sess.get(url)
+ body = response.content
+ response2 = sess.get(url)
+ assert response2.from_cache
+ assert response2.content == body
+
+ # OK now let's violate some abstraction boundaries to make sure body
+ # was stored in metadata file.
+ with open(self.cache._fn(url), "rb") as f:
+ assert body in f.read()
+ assert not os.path.exists(self.cache._fn(url) + ".body")
+
+
+class TestSeparateBodyFileCache(FileCacheTestsMixin):
+ """
+ Tests for ``SeparateBodyFileCache``
+ """
+
+ FileCacheClass = SeparateBodyFileCache
+
+ def test_body_actually_stored_separately(self, sess):
+ """
+ Body is stored and can be retrieved from the SeparateBodyFileCache,
with assurances
+ it's actually being loaded from separate file than metadata.
+ """
+ url = self.url + "cache_60"
+ response = sess.get(url)
+ body = response.content
+ response2 = sess.get(url)
+ assert response2.from_cache
+ assert response2.content == body
+
+ # OK now let's violate some abstraction boundaries to make sure body
+ # actually came from separate file.
+ with open(self.cache._fn(url), "rb") as f:
+ assert body not in f.read()
+ with open(self.cache._fn(url) + ".body", "rb") as f:
+ assert body == f.read()
+ with open(self.cache._fn(url) + ".body", "wb") as f:
+ f.write(b"CORRUPTED")
+ response2 = sess.get(url)
+ assert response2.from_cache
+ assert response2.content == b"CORRUPTED"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/cachecontrol-0.12.10/tests/test_storage_redis.py
new/cachecontrol-0.12.11/tests/test_storage_redis.py
--- old/cachecontrol-0.12.10/tests/test_storage_redis.py 2021-11-05
18:07:09.000000000 +0100
+++ new/cachecontrol-0.12.11/tests/test_storage_redis.py 2022-04-19
19:20:49.000000000 +0200
@@ -14,6 +14,10 @@
self.conn = Mock()
self.cache = RedisCache(self.conn)
- def test_set_expiration(self):
+ def test_set_expiration_datetime(self):
self.cache.set("foo", "bar", expires=datetime(2014, 2, 2))
assert self.conn.setex.called
+
+ def test_set_expiration_int(self):
+ self.cache.set("foo", "bar", expires=600)
+ assert self.conn.setex.called