Created
February 26, 2020 09:24
-
-
Save lesywix/2d58797fd6367b52e8c5e979f9dcdc5f to your computer and use it in GitHub Desktop.
A simple cache of ttl base
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
import collections | |
import threading | |
try: | |
from time import monotonic as default_timer | |
except ImportError: | |
from time import time as default_timer | |
class TTLCache: | |
def __init__(self, maxsize, ttl, concurrent=False): | |
self.maxsize = maxsize | |
self.ttl = ttl | |
self._values = {} | |
self._expire_times = collections.OrderedDict() | |
self._access_times = collections.OrderedDict() | |
self._rlock = threading.RLock() if concurrent else None | |
def clear(self): | |
self._values.clear() | |
self._expire_times.clear() | |
self._access_times.clear() | |
def get(self, key): | |
try: | |
return self.__getitem__(key) | |
except KeyError: | |
return None | |
def __contains__(self, key): | |
return bool(key in self._values) | |
def __setitem__(self, key, value): | |
t = int(default_timer()) | |
self.__delete__(key) | |
self._values[key] = value | |
self._access_times[key] = t | |
self._expire_times[key] = t + self.ttl | |
self.cleanup() | |
def __getitem__(self, key): | |
t = int(default_timer()) | |
del self._access_times[key] | |
self._access_times[key] = t | |
self.cleanup() | |
return self._values[key] | |
def __delete__(self, key): | |
if key in self._values: | |
del self._values[key] | |
del self._expire_times[key] | |
del self._access_times[key] | |
def __len__(self): | |
return len(self._values) | |
def cleanup(self): | |
"""clean expired or oversize data""" | |
t = int(default_timer()) | |
# clean expired key | |
for k in self._expire_times.keys(): | |
if self._expire_times[k] < t: | |
self.__delete__(k) | |
else: | |
break | |
# clean least recently used key | |
while len(self._values) > self.maxsize: | |
for k in self._access_times.keys(): | |
self.__delete__(k) | |
break | |
def caching(self, fn): | |
"""decorator to cache function calls""" | |
def _new_fn(*args, **kwargs): | |
key = fn.__name__ + '#' + repr((args, kwargs)) | |
try: | |
if self._rlock: | |
with self._rlock: | |
return self.__getitem__(key) | |
else: | |
return self.__getitem__(key) | |
except KeyError: | |
rv = fn(*args, **kwargs) | |
if self._rlock: | |
with self._rlock: | |
self.__setitem__(key, rv) | |
else: | |
self.__setitem__(key, rv) | |
return rv | |
return _new_fn | |
def debug(self): | |
print('cache value: ', self._values) | |
print('cache expire time: ', self._expire_times) | |
print('cache access time: ', self._access_times) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment