Skip to content

Instantly share code, notes, and snippets.

@lesywix
Created February 26, 2020 09:24
Show Gist options
  • Save lesywix/2d58797fd6367b52e8c5e979f9dcdc5f to your computer and use it in GitHub Desktop.
Save lesywix/2d58797fd6367b52e8c5e979f9dcdc5f to your computer and use it in GitHub Desktop.
A simple cache of ttl base
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