Move to OrderedDict-based cacheclass.

This commit is contained in:
Ethan Dalool 2018-09-30 01:45:19 -07:00
parent 0e060be462
commit 09d9339efb

View file

@ -1,61 +1,31 @@
import collections import collections
import itertools
import time
class Cache: class Cache:
def __init__(self, maxage=None, maxlen=None, shrink_delay=None): def __init__(self, maxlen):
self.maxage = maxage
self.maxlen = maxlen self.maxlen = maxlen
self.shrink_delay = shrink_delay self.cache = collections.OrderedDict()
self.last_shrink = time.time()
self._dict = {}
self._recency = {}
def __contains__(self, key): def __contains__(self, key):
return key in self._dict return key in self.cache
def __getitem__(self, key): def __getitem__(self, key):
self._shrink() value = self.cache.pop(key)
value = self._dict[key] self.cache[key] = value
self._maintain(key)
return value return value
def __len__(self):
return len(self.cache)
def __setitem__(self, key, value): def __setitem__(self, key, value):
self._shrink() try:
self._dict[key] = value self.cache.pop(key)
self._maintain(key) except KeyError:
if len(self.cache) >= self.maxlen:
def _maintain(self, key): self.cache.popitem(last=False)
self._recency[key] = time.time() self.cache[key] = value
def _shrink(self):
if self.shrink_delay is not None:
if time.time() - self.last_shrink > self.shrink_delay:
return
now = time.time()
self.last_shrink = now
if self.maxlen is None:
pop_count = 0
else:
pop_count = len(self._dict) - self.maxlen
pop_count = max(0, pop_count)
keys = sorted(self._recency.keys(), key=self._recency.get)
for key in itertools.islice(keys, 0, pop_count):
self.remove(key)
if self.maxage is not None:
for key in itertools.islice(keys, pop_count, None):
last_used = self._recency[key]
age = now - last_used
if age > self.maxage:
self.remove(key)
def clear(self): def clear(self):
self._dict.clear() self.cache.clear()
self._recency.clear()
def get(self, key, fallback=None): def get(self, key, fallback=None):
try: try:
@ -63,9 +33,11 @@ class Cache:
except KeyError: except KeyError:
return fallback return fallback
def pop(self, key):
return self.cache.pop(key)
def remove(self, key): def remove(self, key):
try: try:
self._dict.pop(key) self.pop(key)
self._recency.pop(key)
except KeyError: except KeyError:
return pass