-
Notifications
You must be signed in to change notification settings - Fork 439
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #893 from ix5/caching-drop-in
[server] core: Replace flask-caching dependency with drop-in
- Loading branch information
Showing
4 changed files
with
142 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import pickle | ||
from time import time | ||
|
||
# Imported from cachelib: https://github.com/pallets-eco/cachelib | ||
# As of v0.7.0, commit 4e23926effc41303c9e305663eafa81192442241 | ||
# License: BSD 3-Clause, Copyright 2018 Pallets | ||
|
||
# BaseSerializer and SimpleSerializer: src/cachelib/serializers.py | ||
# BaseCache and NullCache: src/cachelib/base.py | ||
# SimpleCache: src/cachelib/simple.py | ||
# Type hints and unused functions removed | ||
|
||
|
||
class BaseSerializer: | ||
"""BaseSerializer.loads and BaseSerializer.dumps | ||
work on top of pickle.loads and pickle.dumps. Dumping/loading | ||
strings and byte strings is the default for most cache types. | ||
""" | ||
|
||
def dumps(self, value, protocol=pickle.HIGHEST_PROTOCOL): | ||
try: | ||
serialized = pickle.dumps(value, protocol) | ||
except (pickle.PickleError, pickle.PicklingError) as e: | ||
self._warn(e) | ||
return serialized | ||
|
||
def loads(self, bvalue): | ||
try: | ||
data = pickle.loads(bvalue) | ||
except pickle.PickleError as e: | ||
self._warn(e) | ||
return None | ||
else: | ||
return data | ||
|
||
|
||
class SimpleSerializer(BaseSerializer): | ||
"""Default serializer for SimpleCache.""" | ||
|
||
|
||
class BaseCache: | ||
def __init__(self, default_timeout: int = 300): | ||
self.default_timeout = default_timeout | ||
|
||
def _normalize_timeout(self, timeout): | ||
if timeout is None: | ||
timeout = self.default_timeout | ||
return timeout | ||
|
||
def get(self, key): | ||
return None | ||
|
||
def delete(self, key): | ||
return True | ||
|
||
def set(self, key, value, timeout=None): | ||
return True | ||
|
||
|
||
class NullCache(BaseCache): | ||
pass | ||
|
||
|
||
class SimpleCache(BaseCache): | ||
|
||
"""Simple memory cache for single process environments. This class exists | ||
mainly for the development server and is not 100% thread safe. It tries | ||
to use as many atomic operations as possible and no locks for simplicity | ||
but it could happen under heavy load that keys are added multiple times. | ||
:param threshold: the maximum number of items the cache stores before | ||
it starts deleting some. | ||
:param default_timeout: the default timeout that is used if no timeout is | ||
specified on :meth:`~BaseCache.set`. A timeout of | ||
0 indicates that the cache never expires. | ||
""" | ||
|
||
serializer = SimpleSerializer() | ||
|
||
def __init__( | ||
self, | ||
threshold: int = 500, | ||
default_timeout: int = 300, | ||
): | ||
BaseCache.__init__(self, default_timeout) | ||
self._cache = {} | ||
self._threshold = threshold or 500 # threshold = 0 | ||
|
||
def _over_threshold(self): | ||
return len(self._cache) > self._threshold | ||
|
||
def _remove_expired(self, now): | ||
toremove = [k for k, (expires, _) in self._cache.items() if expires < now] | ||
for k in toremove: | ||
self._cache.pop(k, None) | ||
|
||
def _remove_older(self): | ||
k_ordered = ( | ||
k | ||
for k, v in sorted( | ||
self._cache.items(), key=lambda item: item[1][0] | ||
) | ||
) | ||
for k in k_ordered: | ||
self._cache.pop(k, None) | ||
if not self._over_threshold(): | ||
break | ||
|
||
def _prune(self): | ||
if self._over_threshold(): | ||
now = time() | ||
self._remove_expired(now) | ||
# remove older items if still over threshold | ||
if self._over_threshold(): | ||
self._remove_older() | ||
|
||
def _normalize_timeout(self, timeout): | ||
timeout = BaseCache._normalize_timeout(self, timeout) | ||
if timeout > 0: | ||
timeout = int(time()) + timeout | ||
return timeout | ||
|
||
def get(self, key): | ||
try: | ||
expires, value = self._cache[key] | ||
if expires == 0 or expires > time(): | ||
return self.serializer.loads(value) | ||
except KeyError: | ||
return None | ||
|
||
def set(self, key, value, timeout=None): | ||
expires = self._normalize_timeout(timeout) | ||
self._prune() | ||
self._cache[key] = (expires, self.serializer.dumps(value)) | ||
return True | ||
|
||
def delete(self, key): | ||
return self._cache.pop(key, None) is not None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters