Spaces:
Sleeping
Sleeping
Upload backend/venv/lib/python3.10/site-packages/diskcache/djangocache.py with huggingface_hub
Browse files
backend/venv/lib/python3.10/site-packages/diskcache/djangocache.py
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Django-compatible disk and file backed cache."""
|
| 2 |
+
|
| 3 |
+
from functools import wraps
|
| 4 |
+
|
| 5 |
+
from django.core.cache.backends.base import BaseCache
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
from django.core.cache.backends.base import DEFAULT_TIMEOUT
|
| 9 |
+
except ImportError: # pragma: no cover
|
| 10 |
+
# For older versions of Django simply use 300 seconds.
|
| 11 |
+
DEFAULT_TIMEOUT = 300
|
| 12 |
+
|
| 13 |
+
from .core import ENOVAL, args_to_key, full_name
|
| 14 |
+
from .fanout import FanoutCache
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class DjangoCache(BaseCache):
|
| 18 |
+
"""Django-compatible disk and file backed cache."""
|
| 19 |
+
|
| 20 |
+
def __init__(self, directory, params):
|
| 21 |
+
"""Initialize DjangoCache instance.
|
| 22 |
+
|
| 23 |
+
:param str directory: cache directory
|
| 24 |
+
:param dict params: cache parameters
|
| 25 |
+
|
| 26 |
+
"""
|
| 27 |
+
super().__init__(params)
|
| 28 |
+
shards = params.get('SHARDS', 8)
|
| 29 |
+
timeout = params.get('DATABASE_TIMEOUT', 0.010)
|
| 30 |
+
options = params.get('OPTIONS', {})
|
| 31 |
+
self._cache = FanoutCache(directory, shards, timeout, **options)
|
| 32 |
+
|
| 33 |
+
@property
|
| 34 |
+
def directory(self):
|
| 35 |
+
"""Cache directory."""
|
| 36 |
+
return self._cache.directory
|
| 37 |
+
|
| 38 |
+
def cache(self, name):
|
| 39 |
+
"""Return Cache with given `name` in subdirectory.
|
| 40 |
+
|
| 41 |
+
:param str name: subdirectory name for Cache
|
| 42 |
+
:return: Cache with given name
|
| 43 |
+
|
| 44 |
+
"""
|
| 45 |
+
return self._cache.cache(name)
|
| 46 |
+
|
| 47 |
+
def deque(self, name, maxlen=None):
|
| 48 |
+
"""Return Deque with given `name` in subdirectory.
|
| 49 |
+
|
| 50 |
+
:param str name: subdirectory name for Deque
|
| 51 |
+
:param maxlen: max length (default None, no max)
|
| 52 |
+
:return: Deque with given name
|
| 53 |
+
|
| 54 |
+
"""
|
| 55 |
+
return self._cache.deque(name, maxlen=maxlen)
|
| 56 |
+
|
| 57 |
+
def index(self, name):
|
| 58 |
+
"""Return Index with given `name` in subdirectory.
|
| 59 |
+
|
| 60 |
+
:param str name: subdirectory name for Index
|
| 61 |
+
:return: Index with given name
|
| 62 |
+
|
| 63 |
+
"""
|
| 64 |
+
return self._cache.index(name)
|
| 65 |
+
|
| 66 |
+
def add(
|
| 67 |
+
self,
|
| 68 |
+
key,
|
| 69 |
+
value,
|
| 70 |
+
timeout=DEFAULT_TIMEOUT,
|
| 71 |
+
version=None,
|
| 72 |
+
read=False,
|
| 73 |
+
tag=None,
|
| 74 |
+
retry=True,
|
| 75 |
+
):
|
| 76 |
+
"""Set a value in the cache if the key does not already exist. If
|
| 77 |
+
timeout is given, that timeout will be used for the key; otherwise the
|
| 78 |
+
default cache timeout will be used.
|
| 79 |
+
|
| 80 |
+
Return True if the value was stored, False otherwise.
|
| 81 |
+
|
| 82 |
+
:param key: key for item
|
| 83 |
+
:param value: value for item
|
| 84 |
+
:param float timeout: seconds until the item expires
|
| 85 |
+
(default 300 seconds)
|
| 86 |
+
:param int version: key version number (default None, cache parameter)
|
| 87 |
+
:param bool read: read value as bytes from file (default False)
|
| 88 |
+
:param str tag: text to associate with key (default None)
|
| 89 |
+
:param bool retry: retry if database timeout occurs (default True)
|
| 90 |
+
:return: True if item was added
|
| 91 |
+
|
| 92 |
+
"""
|
| 93 |
+
# pylint: disable=arguments-differ
|
| 94 |
+
key = self.make_key(key, version=version)
|
| 95 |
+
timeout = self.get_backend_timeout(timeout=timeout)
|
| 96 |
+
return self._cache.add(key, value, timeout, read, tag, retry)
|
| 97 |
+
|
| 98 |
+
def get(
|
| 99 |
+
self,
|
| 100 |
+
key,
|
| 101 |
+
default=None,
|
| 102 |
+
version=None,
|
| 103 |
+
read=False,
|
| 104 |
+
expire_time=False,
|
| 105 |
+
tag=False,
|
| 106 |
+
retry=False,
|
| 107 |
+
):
|
| 108 |
+
"""Fetch a given key from the cache. If the key does not exist, return
|
| 109 |
+
default, which itself defaults to None.
|
| 110 |
+
|
| 111 |
+
:param key: key for item
|
| 112 |
+
:param default: return value if key is missing (default None)
|
| 113 |
+
:param int version: key version number (default None, cache parameter)
|
| 114 |
+
:param bool read: if True, return file handle to value
|
| 115 |
+
(default False)
|
| 116 |
+
:param float expire_time: if True, return expire_time in tuple
|
| 117 |
+
(default False)
|
| 118 |
+
:param tag: if True, return tag in tuple (default False)
|
| 119 |
+
:param bool retry: retry if database timeout occurs (default False)
|
| 120 |
+
:return: value for item if key is found else default
|
| 121 |
+
|
| 122 |
+
"""
|
| 123 |
+
# pylint: disable=arguments-differ
|
| 124 |
+
key = self.make_key(key, version=version)
|
| 125 |
+
return self._cache.get(key, default, read, expire_time, tag, retry)
|
| 126 |
+
|
| 127 |
+
def read(self, key, version=None):
|
| 128 |
+
"""Return file handle corresponding to `key` from Cache.
|
| 129 |
+
|
| 130 |
+
:param key: Python key to retrieve
|
| 131 |
+
:param int version: key version number (default None, cache parameter)
|
| 132 |
+
:return: file open for reading in binary mode
|
| 133 |
+
:raises KeyError: if key is not found
|
| 134 |
+
|
| 135 |
+
"""
|
| 136 |
+
key = self.make_key(key, version=version)
|
| 137 |
+
return self._cache.read(key)
|
| 138 |
+
|
| 139 |
+
def set(
|
| 140 |
+
self,
|
| 141 |
+
key,
|
| 142 |
+
value,
|
| 143 |
+
timeout=DEFAULT_TIMEOUT,
|
| 144 |
+
version=None,
|
| 145 |
+
read=False,
|
| 146 |
+
tag=None,
|
| 147 |
+
retry=True,
|
| 148 |
+
):
|
| 149 |
+
"""Set a value in the cache. If timeout is given, that timeout will be
|
| 150 |
+
used for the key; otherwise the default cache timeout will be used.
|
| 151 |
+
|
| 152 |
+
:param key: key for item
|
| 153 |
+
:param value: value for item
|
| 154 |
+
:param float timeout: seconds until the item expires
|
| 155 |
+
(default 300 seconds)
|
| 156 |
+
:param int version: key version number (default None, cache parameter)
|
| 157 |
+
:param bool read: read value as bytes from file (default False)
|
| 158 |
+
:param str tag: text to associate with key (default None)
|
| 159 |
+
:param bool retry: retry if database timeout occurs (default True)
|
| 160 |
+
:return: True if item was set
|
| 161 |
+
|
| 162 |
+
"""
|
| 163 |
+
# pylint: disable=arguments-differ
|
| 164 |
+
key = self.make_key(key, version=version)
|
| 165 |
+
timeout = self.get_backend_timeout(timeout=timeout)
|
| 166 |
+
return self._cache.set(key, value, timeout, read, tag, retry)
|
| 167 |
+
|
| 168 |
+
def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None, retry=True):
|
| 169 |
+
"""Touch a key in the cache. If timeout is given, that timeout will be
|
| 170 |
+
used for the key; otherwise the default cache timeout will be used.
|
| 171 |
+
|
| 172 |
+
:param key: key for item
|
| 173 |
+
:param float timeout: seconds until the item expires
|
| 174 |
+
(default 300 seconds)
|
| 175 |
+
:param int version: key version number (default None, cache parameter)
|
| 176 |
+
:param bool retry: retry if database timeout occurs (default True)
|
| 177 |
+
:return: True if key was touched
|
| 178 |
+
|
| 179 |
+
"""
|
| 180 |
+
# pylint: disable=arguments-differ
|
| 181 |
+
key = self.make_key(key, version=version)
|
| 182 |
+
timeout = self.get_backend_timeout(timeout=timeout)
|
| 183 |
+
return self._cache.touch(key, timeout, retry)
|
| 184 |
+
|
| 185 |
+
def pop(
|
| 186 |
+
self,
|
| 187 |
+
key,
|
| 188 |
+
default=None,
|
| 189 |
+
version=None,
|
| 190 |
+
expire_time=False,
|
| 191 |
+
tag=False,
|
| 192 |
+
retry=True,
|
| 193 |
+
):
|
| 194 |
+
"""Remove corresponding item for `key` from cache and return value.
|
| 195 |
+
|
| 196 |
+
If `key` is missing, return `default`.
|
| 197 |
+
|
| 198 |
+
Operation is atomic. Concurrent operations will be serialized.
|
| 199 |
+
|
| 200 |
+
:param key: key for item
|
| 201 |
+
:param default: return value if key is missing (default None)
|
| 202 |
+
:param int version: key version number (default None, cache parameter)
|
| 203 |
+
:param float expire_time: if True, return expire_time in tuple
|
| 204 |
+
(default False)
|
| 205 |
+
:param tag: if True, return tag in tuple (default False)
|
| 206 |
+
:param bool retry: retry if database timeout occurs (default True)
|
| 207 |
+
:return: value for item if key is found else default
|
| 208 |
+
|
| 209 |
+
"""
|
| 210 |
+
key = self.make_key(key, version=version)
|
| 211 |
+
return self._cache.pop(key, default, expire_time, tag, retry)
|
| 212 |
+
|
| 213 |
+
def delete(self, key, version=None, retry=True):
|
| 214 |
+
"""Delete a key from the cache, failing silently.
|
| 215 |
+
|
| 216 |
+
:param key: key for item
|
| 217 |
+
:param int version: key version number (default None, cache parameter)
|
| 218 |
+
:param bool retry: retry if database timeout occurs (default True)
|
| 219 |
+
:return: True if item was deleted
|
| 220 |
+
|
| 221 |
+
"""
|
| 222 |
+
# pylint: disable=arguments-differ
|
| 223 |
+
key = self.make_key(key, version=version)
|
| 224 |
+
return self._cache.delete(key, retry)
|
| 225 |
+
|
| 226 |
+
def incr(self, key, delta=1, version=None, default=None, retry=True):
|
| 227 |
+
"""Increment value by delta for item with key.
|
| 228 |
+
|
| 229 |
+
If key is missing and default is None then raise KeyError. Else if key
|
| 230 |
+
is missing and default is not None then use default for value.
|
| 231 |
+
|
| 232 |
+
Operation is atomic. All concurrent increment operations will be
|
| 233 |
+
counted individually.
|
| 234 |
+
|
| 235 |
+
Assumes value may be stored in a SQLite column. Most builds that target
|
| 236 |
+
machines with 64-bit pointer widths will support 64-bit signed
|
| 237 |
+
integers.
|
| 238 |
+
|
| 239 |
+
:param key: key for item
|
| 240 |
+
:param int delta: amount to increment (default 1)
|
| 241 |
+
:param int version: key version number (default None, cache parameter)
|
| 242 |
+
:param int default: value if key is missing (default None)
|
| 243 |
+
:param bool retry: retry if database timeout occurs (default True)
|
| 244 |
+
:return: new value for item on success else None
|
| 245 |
+
:raises ValueError: if key is not found and default is None
|
| 246 |
+
|
| 247 |
+
"""
|
| 248 |
+
# pylint: disable=arguments-differ
|
| 249 |
+
key = self.make_key(key, version=version)
|
| 250 |
+
try:
|
| 251 |
+
return self._cache.incr(key, delta, default, retry)
|
| 252 |
+
except KeyError:
|
| 253 |
+
raise ValueError("Key '%s' not found" % key) from None
|
| 254 |
+
|
| 255 |
+
def decr(self, key, delta=1, version=None, default=None, retry=True):
|
| 256 |
+
"""Decrement value by delta for item with key.
|
| 257 |
+
|
| 258 |
+
If key is missing and default is None then raise KeyError. Else if key
|
| 259 |
+
is missing and default is not None then use default for value.
|
| 260 |
+
|
| 261 |
+
Operation is atomic. All concurrent decrement operations will be
|
| 262 |
+
counted individually.
|
| 263 |
+
|
| 264 |
+
Unlike Memcached, negative values are supported. Value may be
|
| 265 |
+
decremented below zero.
|
| 266 |
+
|
| 267 |
+
Assumes value may be stored in a SQLite column. Most builds that target
|
| 268 |
+
machines with 64-bit pointer widths will support 64-bit signed
|
| 269 |
+
integers.
|
| 270 |
+
|
| 271 |
+
:param key: key for item
|
| 272 |
+
:param int delta: amount to decrement (default 1)
|
| 273 |
+
:param int version: key version number (default None, cache parameter)
|
| 274 |
+
:param int default: value if key is missing (default None)
|
| 275 |
+
:param bool retry: retry if database timeout occurs (default True)
|
| 276 |
+
:return: new value for item on success else None
|
| 277 |
+
:raises ValueError: if key is not found and default is None
|
| 278 |
+
|
| 279 |
+
"""
|
| 280 |
+
# pylint: disable=arguments-differ
|
| 281 |
+
return self.incr(key, -delta, version, default, retry)
|
| 282 |
+
|
| 283 |
+
def has_key(self, key, version=None):
|
| 284 |
+
"""Returns True if the key is in the cache and has not expired.
|
| 285 |
+
|
| 286 |
+
:param key: key for item
|
| 287 |
+
:param int version: key version number (default None, cache parameter)
|
| 288 |
+
:return: True if key is found
|
| 289 |
+
|
| 290 |
+
"""
|
| 291 |
+
key = self.make_key(key, version=version)
|
| 292 |
+
return key in self._cache
|
| 293 |
+
|
| 294 |
+
def expire(self):
|
| 295 |
+
"""Remove expired items from cache.
|
| 296 |
+
|
| 297 |
+
:return: count of items removed
|
| 298 |
+
|
| 299 |
+
"""
|
| 300 |
+
return self._cache.expire()
|
| 301 |
+
|
| 302 |
+
def stats(self, enable=True, reset=False):
|
| 303 |
+
"""Return cache statistics hits and misses.
|
| 304 |
+
|
| 305 |
+
:param bool enable: enable collecting statistics (default True)
|
| 306 |
+
:param bool reset: reset hits and misses to 0 (default False)
|
| 307 |
+
:return: (hits, misses)
|
| 308 |
+
|
| 309 |
+
"""
|
| 310 |
+
return self._cache.stats(enable=enable, reset=reset)
|
| 311 |
+
|
| 312 |
+
def create_tag_index(self):
|
| 313 |
+
"""Create tag index on cache database.
|
| 314 |
+
|
| 315 |
+
Better to initialize cache with `tag_index=True` than use this.
|
| 316 |
+
|
| 317 |
+
:raises Timeout: if database timeout occurs
|
| 318 |
+
|
| 319 |
+
"""
|
| 320 |
+
self._cache.create_tag_index()
|
| 321 |
+
|
| 322 |
+
def drop_tag_index(self):
|
| 323 |
+
"""Drop tag index on cache database.
|
| 324 |
+
|
| 325 |
+
:raises Timeout: if database timeout occurs
|
| 326 |
+
|
| 327 |
+
"""
|
| 328 |
+
self._cache.drop_tag_index()
|
| 329 |
+
|
| 330 |
+
def evict(self, tag):
|
| 331 |
+
"""Remove items with matching `tag` from cache.
|
| 332 |
+
|
| 333 |
+
:param str tag: tag identifying items
|
| 334 |
+
:return: count of items removed
|
| 335 |
+
|
| 336 |
+
"""
|
| 337 |
+
return self._cache.evict(tag)
|
| 338 |
+
|
| 339 |
+
def cull(self):
|
| 340 |
+
"""Cull items from cache until volume is less than size limit.
|
| 341 |
+
|
| 342 |
+
:return: count of items removed
|
| 343 |
+
|
| 344 |
+
"""
|
| 345 |
+
return self._cache.cull()
|
| 346 |
+
|
| 347 |
+
def clear(self):
|
| 348 |
+
"""Remove *all* values from the cache at once."""
|
| 349 |
+
return self._cache.clear()
|
| 350 |
+
|
| 351 |
+
def close(self, **kwargs):
|
| 352 |
+
"""Close the cache connection."""
|
| 353 |
+
# pylint: disable=unused-argument
|
| 354 |
+
self._cache.close()
|
| 355 |
+
|
| 356 |
+
def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT):
|
| 357 |
+
"""Return seconds to expiration.
|
| 358 |
+
|
| 359 |
+
:param float timeout: seconds until the item expires
|
| 360 |
+
(default 300 seconds)
|
| 361 |
+
|
| 362 |
+
"""
|
| 363 |
+
if timeout == DEFAULT_TIMEOUT:
|
| 364 |
+
timeout = self.default_timeout
|
| 365 |
+
elif timeout == 0:
|
| 366 |
+
# ticket 21147 - avoid time.time() related precision issues
|
| 367 |
+
timeout = -1
|
| 368 |
+
return None if timeout is None else timeout
|
| 369 |
+
|
| 370 |
+
def memoize(
|
| 371 |
+
self,
|
| 372 |
+
name=None,
|
| 373 |
+
timeout=DEFAULT_TIMEOUT,
|
| 374 |
+
version=None,
|
| 375 |
+
typed=False,
|
| 376 |
+
tag=None,
|
| 377 |
+
ignore=(),
|
| 378 |
+
):
|
| 379 |
+
"""Memoizing cache decorator.
|
| 380 |
+
|
| 381 |
+
Decorator to wrap callable with memoizing function using cache.
|
| 382 |
+
Repeated calls with the same arguments will lookup result in cache and
|
| 383 |
+
avoid function evaluation.
|
| 384 |
+
|
| 385 |
+
If name is set to None (default), the callable name will be determined
|
| 386 |
+
automatically.
|
| 387 |
+
|
| 388 |
+
When timeout is set to zero, function results will not be set in the
|
| 389 |
+
cache. Cache lookups still occur, however. Read
|
| 390 |
+
:doc:`case-study-landing-page-caching` for example usage.
|
| 391 |
+
|
| 392 |
+
If typed is set to True, function arguments of different types will be
|
| 393 |
+
cached separately. For example, f(3) and f(3.0) will be treated as
|
| 394 |
+
distinct calls with distinct results.
|
| 395 |
+
|
| 396 |
+
The original underlying function is accessible through the __wrapped__
|
| 397 |
+
attribute. This is useful for introspection, for bypassing the cache,
|
| 398 |
+
or for rewrapping the function with a different cache.
|
| 399 |
+
|
| 400 |
+
An additional `__cache_key__` attribute can be used to generate the
|
| 401 |
+
cache key used for the given arguments.
|
| 402 |
+
|
| 403 |
+
Remember to call memoize when decorating a callable. If you forget,
|
| 404 |
+
then a TypeError will occur.
|
| 405 |
+
|
| 406 |
+
:param str name: name given for callable (default None, automatic)
|
| 407 |
+
:param float timeout: seconds until the item expires
|
| 408 |
+
(default 300 seconds)
|
| 409 |
+
:param int version: key version number (default None, cache parameter)
|
| 410 |
+
:param bool typed: cache different types separately (default False)
|
| 411 |
+
:param str tag: text to associate with arguments (default None)
|
| 412 |
+
:param set ignore: positional or keyword args to ignore (default ())
|
| 413 |
+
:return: callable decorator
|
| 414 |
+
|
| 415 |
+
"""
|
| 416 |
+
# Caution: Nearly identical code exists in Cache.memoize
|
| 417 |
+
if callable(name):
|
| 418 |
+
raise TypeError('name cannot be callable')
|
| 419 |
+
|
| 420 |
+
def decorator(func):
|
| 421 |
+
"""Decorator created by memoize() for callable `func`."""
|
| 422 |
+
base = (full_name(func),) if name is None else (name,)
|
| 423 |
+
|
| 424 |
+
@wraps(func)
|
| 425 |
+
def wrapper(*args, **kwargs):
|
| 426 |
+
"""Wrapper for callable to cache arguments and return values."""
|
| 427 |
+
key = wrapper.__cache_key__(*args, **kwargs)
|
| 428 |
+
result = self.get(key, ENOVAL, version, retry=True)
|
| 429 |
+
|
| 430 |
+
if result is ENOVAL:
|
| 431 |
+
result = func(*args, **kwargs)
|
| 432 |
+
valid_timeout = (
|
| 433 |
+
timeout is None
|
| 434 |
+
or timeout == DEFAULT_TIMEOUT
|
| 435 |
+
or timeout > 0
|
| 436 |
+
)
|
| 437 |
+
if valid_timeout:
|
| 438 |
+
self.set(
|
| 439 |
+
key,
|
| 440 |
+
result,
|
| 441 |
+
timeout,
|
| 442 |
+
version,
|
| 443 |
+
tag=tag,
|
| 444 |
+
retry=True,
|
| 445 |
+
)
|
| 446 |
+
|
| 447 |
+
return result
|
| 448 |
+
|
| 449 |
+
def __cache_key__(*args, **kwargs):
|
| 450 |
+
"""Make key for cache given function arguments."""
|
| 451 |
+
return args_to_key(base, args, kwargs, typed, ignore)
|
| 452 |
+
|
| 453 |
+
wrapper.__cache_key__ = __cache_key__
|
| 454 |
+
return wrapper
|
| 455 |
+
|
| 456 |
+
return decorator
|