Update diskcache 5.4.0 (1cb1425) → 5.6.1 (4d30686).

This commit is contained in:
JackDandy 2023-04-12 15:40:24 +01:00
parent a3288eff39
commit 6bdb463147
7 changed files with 136 additions and 40 deletions

View file

@ -1,5 +1,6 @@
### 3.29.0 (2023-xx-xx xx:xx:00 UTC) ### 3.29.0 (2023-xx-xx xx:xx:00 UTC)
* Update diskcache 5.4.0 (1cb1425) to 5.6.1 (4d30686)
* Update filelock 3.9.0 (ce3e891) to 3.11.0 (d3241b9) * Update filelock 3.9.0 (ce3e891) to 3.11.0 (d3241b9)
* Update Msgpack 1.0.4 (b5acfd5) to 1.0.5 (0516c2c) * Update Msgpack 1.0.4 (b5acfd5) to 1.0.5 (0516c2c)
* Update Requests library 2.28.1 (ec553c2) to 2.29.0 (87d63de) * Update Requests library 2.28.1 (ec553c2) to 2.29.0 (87d63de)

View file

@ -61,8 +61,8 @@ except Exception: # pylint: disable=broad-except # pragma: no cover
pass pass
__title__ = 'diskcache' __title__ = 'diskcache'
__version__ = '5.4.0' __version__ = '5.6.1'
__build__ = 0x050400 __build__ = 0x050601
__author__ = 'Grant Jenks' __author__ = 'Grant Jenks'
__license__ = 'Apache 2.0' __license__ = 'Apache 2.0'
__copyright__ = 'Copyright 2016-2022 Grant Jenks' __copyright__ = 'Copyright 2016-2023 Grant Jenks'

View file

@ -50,14 +50,14 @@ DEFAULT_SETTINGS = {
'statistics': 0, # False 'statistics': 0, # False
'tag_index': 0, # False 'tag_index': 0, # False
'eviction_policy': 'least-recently-stored', 'eviction_policy': 'least-recently-stored',
'size_limit': 2 ** 30, # 1gb 'size_limit': 2**30, # 1gb
'cull_limit': 10, 'cull_limit': 10,
'sqlite_auto_vacuum': 1, # FULL 'sqlite_auto_vacuum': 1, # FULL
'sqlite_cache_size': 2 ** 13, # 8,192 pages 'sqlite_cache_size': 2**13, # 8,192 pages
'sqlite_journal_mode': 'wal', 'sqlite_journal_mode': 'wal',
'sqlite_mmap_size': 2 ** 26, # 64mb 'sqlite_mmap_size': 2**26, # 64mb
'sqlite_synchronous': 1, # NORMAL 'sqlite_synchronous': 1, # NORMAL
'disk_min_file_size': 2 ** 15, # 32kb 'disk_min_file_size': 2**15, # 32kb
'disk_pickle_protocol': pickle.HIGHEST_PROTOCOL, 'disk_pickle_protocol': pickle.HIGHEST_PROTOCOL,
} }
@ -171,7 +171,7 @@ class Disk:
:return: corresponding Python key :return: corresponding Python key
""" """
# pylint: disable=no-self-use,unidiomatic-typecheck # pylint: disable=unidiomatic-typecheck
if raw: if raw:
return bytes(key) if type(key) is sqlite3.Binary else key return bytes(key) if type(key) is sqlite3.Binary else key
else: else:
@ -213,7 +213,7 @@ class Disk:
size = op.getsize(full_path) size = op.getsize(full_path)
return size, MODE_TEXT, filename, None return size, MODE_TEXT, filename, None
elif read: elif read:
reader = ft.partial(value.read, 2 ** 22) reader = ft.partial(value.read, 2**22)
filename, full_path = self.filename(key, value) filename, full_path = self.filename(key, value)
iterator = iter(reader, b'') iterator = iter(reader, b'')
size = self._write(full_path, iterator, 'xb') size = self._write(full_path, iterator, 'xb')
@ -229,7 +229,6 @@ class Disk:
return len(result), MODE_PICKLE, filename, None return len(result), MODE_PICKLE, filename, None
def _write(self, full_path, iterator, mode, encoding=None): def _write(self, full_path, iterator, mode, encoding=None):
# pylint: disable=no-self-use
full_dir, _ = op.split(full_path) full_dir, _ = op.split(full_path)
for count in range(1, 11): for count in range(1, 11):
@ -265,7 +264,7 @@ class Disk:
:raises: IOError if the value cannot be read :raises: IOError if the value cannot be read
""" """
# pylint: disable=no-self-use,unidiomatic-typecheck,consider-using-with # pylint: disable=unidiomatic-typecheck,consider-using-with
if mode == MODE_RAW: if mode == MODE_RAW:
return bytes(value) if type(value) is sqlite3.Binary else value return bytes(value) if type(value) is sqlite3.Binary else value
elif mode == MODE_BINARY: elif mode == MODE_BINARY:
@ -435,6 +434,7 @@ class Cache:
if directory is None: if directory is None:
directory = tempfile.mkdtemp(prefix='diskcache-') directory = tempfile.mkdtemp(prefix='diskcache-')
directory = str(directory)
directory = op.expanduser(directory) directory = op.expanduser(directory)
directory = op.expandvars(directory) directory = op.expandvars(directory)
@ -1380,6 +1380,7 @@ class Cache:
:raises Timeout: if database timeout occurs :raises Timeout: if database timeout occurs
""" """
# pylint: disable=unnecessary-dunder-call
try: try:
return self.__delitem__(key, retry=retry) return self.__delitem__(key, retry=retry)
except KeyError: except KeyError:

View file

@ -44,14 +44,15 @@ class DjangoCache(BaseCache):
""" """
return self._cache.cache(name) return self._cache.cache(name)
def deque(self, name): def deque(self, name, maxlen=None):
"""Return Deque with given `name` in subdirectory. """Return Deque with given `name` in subdirectory.
:param str name: subdirectory name for Deque :param str name: subdirectory name for Deque
:param maxlen: max length (default None, no max)
:return: Deque with given name :return: Deque with given name
""" """
return self._cache.deque(name) return self._cache.deque(name, maxlen=maxlen)
def index(self, name): def index(self, name):
"""Return Index with given `name` in subdirectory. """Return Index with given `name` in subdirectory.

View file

@ -30,6 +30,7 @@ class FanoutCache:
""" """
if directory is None: if directory is None:
directory = tempfile.mkdtemp(prefix='diskcache-') directory = tempfile.mkdtemp(prefix='diskcache-')
directory = str(directory)
directory = op.expanduser(directory) directory = op.expanduser(directory)
directory = op.expandvars(directory) directory = op.expandvars(directory)
@ -45,7 +46,7 @@ class FanoutCache:
timeout=timeout, timeout=timeout,
disk=disk, disk=disk,
size_limit=size_limit, size_limit=size_limit,
**settings **settings,
) )
for num in range(shards) for num in range(shards)
) )
@ -573,9 +574,11 @@ class FanoutCache:
break break
return result return result
def cache(self, name): def cache(self, name, timeout=60, disk=None, **settings):
"""Return Cache with given `name` in subdirectory. """Return Cache with given `name` in subdirectory.
If disk is none (default), uses the fanout cache disk.
>>> fanout_cache = FanoutCache() >>> fanout_cache = FanoutCache()
>>> cache = fanout_cache.cache('test') >>> cache = fanout_cache.cache('test')
>>> cache.set('abc', 123) >>> cache.set('abc', 123)
@ -588,6 +591,9 @@ class FanoutCache:
True True
:param str name: subdirectory name for Cache :param str name: subdirectory name for Cache
:param float timeout: SQLite connection timeout
:param disk: Disk type or subclass for serialization
:param settings: any of DEFAULT_SETTINGS
:return: Cache with given name :return: Cache with given name
""" """
@ -598,11 +604,16 @@ class FanoutCache:
except KeyError: except KeyError:
parts = name.split('/') parts = name.split('/')
directory = op.join(self._directory, 'cache', *parts) directory = op.join(self._directory, 'cache', *parts)
temp = Cache(directory=directory, disk=self._disk) temp = Cache(
directory=directory,
timeout=timeout,
disk=self._disk if disk is None else Disk,
**settings,
)
_caches[name] = temp _caches[name] = temp
return temp return temp
def deque(self, name): def deque(self, name, maxlen=None):
"""Return Deque with given `name` in subdirectory. """Return Deque with given `name` in subdirectory.
>>> cache = FanoutCache() >>> cache = FanoutCache()
@ -616,6 +627,7 @@ class FanoutCache:
1 1
:param str name: subdirectory name for Deque :param str name: subdirectory name for Deque
:param maxlen: max length (default None, no max)
:return: Deque with given name :return: Deque with given name
""" """
@ -626,8 +638,12 @@ class FanoutCache:
except KeyError: except KeyError:
parts = name.split('/') parts = name.split('/')
directory = op.join(self._directory, 'deque', *parts) directory = op.join(self._directory, 'deque', *parts)
cache = Cache(directory=directory, disk=self._disk) cache = Cache(
deque = Deque.fromcache(cache) directory=directory,
disk=self._disk,
eviction_policy='none',
)
deque = Deque.fromcache(cache, maxlen=maxlen)
_deques[name] = deque _deques[name] = deque
return deque return deque
@ -658,7 +674,11 @@ class FanoutCache:
except KeyError: except KeyError:
parts = name.split('/') parts = name.split('/')
directory = op.join(self._directory, 'index', *parts) directory = op.join(self._directory, 'index', *parts)
cache = Cache(directory=directory, disk=self._disk) cache = Cache(
directory=directory,
disk=self._disk,
eviction_policy='none',
)
index = Index.fromcache(cache) index = Index.fromcache(cache)
_indexes[name] = index _indexes[name] = index
return index return index

View file

@ -76,7 +76,7 @@ class Deque(Sequence):
""" """
def __init__(self, iterable=(), directory=None): def __init__(self, iterable=(), directory=None, maxlen=None):
"""Initialize deque instance. """Initialize deque instance.
If directory is None then temporary directory created. The directory If directory is None then temporary directory created. The directory
@ -87,10 +87,11 @@ class Deque(Sequence):
""" """
self._cache = Cache(directory, eviction_policy='none') self._cache = Cache(directory, eviction_policy='none')
self.extend(iterable) self._maxlen = float('inf') if maxlen is None else maxlen
self._extend(iterable)
@classmethod @classmethod
def fromcache(cls, cache, iterable=()): def fromcache(cls, cache, iterable=(), maxlen=None):
"""Initialize deque using `cache`. """Initialize deque using `cache`.
>>> cache = Cache() >>> cache = Cache()
@ -112,7 +113,8 @@ class Deque(Sequence):
# pylint: disable=no-member,protected-access # pylint: disable=no-member,protected-access
self = cls.__new__(cls) self = cls.__new__(cls)
self._cache = cache self._cache = cache
self.extend(iterable) self._maxlen = float('inf') if maxlen is None else maxlen
self._extend(iterable)
return self return self
@property @property
@ -125,6 +127,31 @@ class Deque(Sequence):
"""Directory path where deque is stored.""" """Directory path where deque is stored."""
return self._cache.directory return self._cache.directory
@property
def maxlen(self):
"""Max length of the deque."""
return self._maxlen
@maxlen.setter
def maxlen(self, value):
"""Set max length of the deque.
Pops items from left while length greater than max.
>>> deque = Deque()
>>> deque.extendleft('abcde')
>>> deque.maxlen = 3
>>> list(deque)
['c', 'd', 'e']
:param value: max length
"""
self._maxlen = value
with self._cache.transact(retry=True):
while len(self._cache) > self._maxlen:
self._popleft()
def _index(self, index, func): def _index(self, index, func):
len_self = len(self) len_self = len(self)
@ -245,7 +272,7 @@ class Deque(Sequence):
:return: deque with added items :return: deque with added items
""" """
self.extend(iterable) self._extend(iterable)
return self return self
def __iter__(self): def __iter__(self):
@ -293,10 +320,11 @@ class Deque(Sequence):
pass pass
def __getstate__(self): def __getstate__(self):
return self.directory return self.directory, self.maxlen
def __setstate__(self, state): def __setstate__(self, state):
self.__init__(directory=state) directory, maxlen = state
self.__init__(directory=directory, maxlen=maxlen)
def append(self, value): def append(self, value):
"""Add `value` to back of deque. """Add `value` to back of deque.
@ -311,7 +339,12 @@ class Deque(Sequence):
:param value: value to add to back of deque :param value: value to add to back of deque
""" """
self._cache.push(value, retry=True) with self._cache.transact(retry=True):
self._cache.push(value, retry=True)
if len(self._cache) > self._maxlen:
self._popleft()
_append = append
def appendleft(self, value): def appendleft(self, value):
"""Add `value` to front of deque. """Add `value` to front of deque.
@ -326,7 +359,12 @@ class Deque(Sequence):
:param value: value to add to front of deque :param value: value to add to front of deque
""" """
self._cache.push(value, side='front', retry=True) with self._cache.transact(retry=True):
self._cache.push(value, side='front', retry=True)
if len(self._cache) > self._maxlen:
self._pop()
_appendleft = appendleft
def clear(self): def clear(self):
"""Remove all elements from deque. """Remove all elements from deque.
@ -341,6 +379,13 @@ class Deque(Sequence):
""" """
self._cache.clear(retry=True) self._cache.clear(retry=True)
_clear = clear
def copy(self):
"""Copy deque with same directory and max length."""
TypeSelf = type(self)
return TypeSelf(directory=self.directory, maxlen=self.maxlen)
def count(self, value): def count(self, value):
"""Return number of occurrences of `value` in deque. """Return number of occurrences of `value` in deque.
@ -366,7 +411,9 @@ class Deque(Sequence):
""" """
for value in iterable: for value in iterable:
self.append(value) self._append(value)
_extend = extend
def extendleft(self, iterable): def extendleft(self, iterable):
"""Extend front side of deque with value from `iterable`. """Extend front side of deque with value from `iterable`.
@ -380,7 +427,7 @@ class Deque(Sequence):
""" """
for value in iterable: for value in iterable:
self.appendleft(value) self._appendleft(value)
def peek(self): def peek(self):
"""Peek at value at back of deque. """Peek at value at back of deque.
@ -460,6 +507,8 @@ class Deque(Sequence):
raise IndexError('pop from an empty deque') raise IndexError('pop from an empty deque')
return value return value
_pop = pop
def popleft(self): def popleft(self):
"""Remove and return value at front of deque. """Remove and return value at front of deque.
@ -484,6 +533,8 @@ class Deque(Sequence):
raise IndexError('pop from an empty deque') raise IndexError('pop from an empty deque')
return value return value
_popleft = popleft
def remove(self, value): def remove(self, value):
"""Remove first occurrence of `value` in deque. """Remove first occurrence of `value` in deque.
@ -531,15 +582,17 @@ class Deque(Sequence):
['c', 'b', 'a'] ['c', 'b', 'a']
""" """
# pylint: disable=protected-access
# GrantJ 2019-03-22 Consider using an algorithm that swaps the values # GrantJ 2019-03-22 Consider using an algorithm that swaps the values
# at two keys. Like self._cache.swap(key1, key2, retry=True) The swap # at two keys. Like self._cache.swap(key1, key2, retry=True) The swap
# method would exchange the values at two given keys. Then, using a # method would exchange the values at two given keys. Then, using a
# forward iterator and a reverse iterator, the reversis method could # forward iterator and a reverse iterator, the reverse method could
# avoid making copies of the values. # avoid making copies of the values.
temp = Deque(iterable=reversed(self)) temp = Deque(iterable=reversed(self))
self.clear() self._clear()
self.extend(temp) self._extend(temp)
directory = temp.directory directory = temp.directory
temp._cache.close()
del temp del temp
rmtree(directory) rmtree(directory)
@ -574,22 +627,22 @@ class Deque(Sequence):
for _ in range(steps): for _ in range(steps):
try: try:
value = self.pop() value = self._pop()
except IndexError: except IndexError:
return return
else: else:
self.appendleft(value) self._appendleft(value)
else: else:
steps *= -1 steps *= -1
steps %= len_self steps %= len_self
for _ in range(steps): for _ in range(steps):
try: try:
value = self.popleft() value = self._popleft()
except IndexError: except IndexError:
return return
else: else:
self.append(value) self._append(value)
__hash__ = None # type: ignore __hash__ = None # type: ignore
@ -668,7 +721,9 @@ class Index(MutableMapping):
args = args[1:] args = args[1:]
directory = None directory = None
self._cache = Cache(directory, eviction_policy='none') self._cache = Cache(directory, eviction_policy='none')
self.update(*args, **kwargs) self._update(*args, **kwargs)
_update = MutableMapping.update
@classmethod @classmethod
def fromcache(cls, cache, *args, **kwargs): def fromcache(cls, cache, *args, **kwargs):
@ -694,7 +749,7 @@ class Index(MutableMapping):
# pylint: disable=no-member,protected-access # pylint: disable=no-member,protected-access
self = cls.__new__(cls) self = cls.__new__(cls)
self._cache = cache self._cache = cache
self.update(*args, **kwargs) self._update(*args, **kwargs)
return self return self
@property @property

View file

@ -17,6 +17,9 @@ class Averager:
Sometimes known as "online statistics," the running average maintains the Sometimes known as "online statistics," the running average maintains the
total and count. The average can then be calculated at any time. total and count. The average can then be calculated at any time.
Assumes the key will not be evicted. Set the eviction policy to 'none' on
the cache to guarantee the key is not evicted.
>>> import diskcache >>> import diskcache
>>> cache = diskcache.FanoutCache() >>> cache = diskcache.FanoutCache()
>>> ave = Averager(cache, 'latency') >>> ave = Averager(cache, 'latency')
@ -65,6 +68,9 @@ class Averager:
class Lock: class Lock:
"""Recipe for cross-process and cross-thread lock. """Recipe for cross-process and cross-thread lock.
Assumes the key will not be evicted. Set the eviction policy to 'none' on
the cache to guarantee the key is not evicted.
>>> import diskcache >>> import diskcache
>>> cache = diskcache.Cache() >>> cache = diskcache.Cache()
>>> lock = Lock(cache, 'report-123') >>> lock = Lock(cache, 'report-123')
@ -113,6 +119,9 @@ class Lock:
class RLock: class RLock:
"""Recipe for cross-process and cross-thread re-entrant lock. """Recipe for cross-process and cross-thread re-entrant lock.
Assumes the key will not be evicted. Set the eviction policy to 'none' on
the cache to guarantee the key is not evicted.
>>> import diskcache >>> import diskcache
>>> cache = diskcache.Cache() >>> cache = diskcache.Cache()
>>> rlock = RLock(cache, 'user-123') >>> rlock = RLock(cache, 'user-123')
@ -181,6 +190,9 @@ class RLock:
class BoundedSemaphore: class BoundedSemaphore:
"""Recipe for cross-process and cross-thread bounded semaphore. """Recipe for cross-process and cross-thread bounded semaphore.
Assumes the key will not be evicted. Set the eviction policy to 'none' on
the cache to guarantee the key is not evicted.
>>> import diskcache >>> import diskcache
>>> cache = diskcache.Cache() >>> cache = diskcache.Cache()
>>> semaphore = BoundedSemaphore(cache, 'max-cons', value=2) >>> semaphore = BoundedSemaphore(cache, 'max-cons', value=2)
@ -251,6 +263,9 @@ def throttle(
): ):
"""Decorator to throttle calls to function. """Decorator to throttle calls to function.
Assumes keys will not be evicted. Set the eviction policy to 'none' on the
cache to guarantee the keys are not evicted.
>>> import diskcache, time >>> import diskcache, time
>>> cache = diskcache.Cache() >>> cache = diskcache.Cache()
>>> count = 0 >>> count = 0
@ -305,6 +320,9 @@ def barrier(cache, lock_factory, name=None, expire=None, tag=None):
Supports different kinds of locks: Lock, RLock, BoundedSemaphore. Supports different kinds of locks: Lock, RLock, BoundedSemaphore.
Assumes keys will not be evicted. Set the eviction policy to 'none' on the
cache to guarantee the keys are not evicted.
>>> import diskcache, time >>> import diskcache, time
>>> cache = diskcache.Cache() >>> cache = diskcache.Cache()
>>> @barrier(cache, Lock) >>> @barrier(cache, Lock)