2017-03-30 22:56:01 +00:00
|
|
|
import collections
|
2017-04-01 07:08:56 +00:00
|
|
|
import itertools
|
2017-03-30 22:56:01 +00:00
|
|
|
import time
|
2017-04-01 07:08:56 +00:00
|
|
|
|
2017-03-30 22:56:01 +00:00
|
|
|
class Cache:
|
2017-04-01 07:08:56 +00:00
|
|
|
def __init__(self, maxage=None, maxlen=None, shrink_delay=None):
|
|
|
|
self.maxage = maxage
|
2017-03-30 22:56:01 +00:00
|
|
|
self.maxlen = maxlen
|
2017-04-01 07:08:56 +00:00
|
|
|
self.shrink_delay = shrink_delay
|
|
|
|
self.last_shrink = time.time()
|
|
|
|
|
2017-03-30 22:56:01 +00:00
|
|
|
self._dict = {}
|
|
|
|
self._recency = {}
|
|
|
|
|
|
|
|
def __getitem__(self, key):
|
2017-04-01 07:08:56 +00:00
|
|
|
self._shrink()
|
2017-03-30 22:56:01 +00:00
|
|
|
value = self._dict[key]
|
|
|
|
self._maintain(key)
|
|
|
|
return value
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
self._shrink()
|
|
|
|
self._dict[key] = value
|
|
|
|
self._maintain(key)
|
|
|
|
|
|
|
|
def _maintain(self, key):
|
|
|
|
self._recency[key] = time.time()
|
|
|
|
|
|
|
|
def _shrink(self):
|
2017-04-01 07:08:56 +00:00
|
|
|
if self.shrink_delay is not None:
|
|
|
|
if time.time() - self.last_shrink > self.shrink_delay:
|
|
|
|
return
|
2017-03-30 22:56:01 +00:00
|
|
|
|
2017-04-01 07:08:56 +00:00
|
|
|
now = time.time()
|
|
|
|
self.last_shrink = now
|
2017-03-30 22:56:01 +00:00
|
|
|
|
2017-04-01 07:08:56 +00:00
|
|
|
if self.maxlen is None:
|
|
|
|
pop_count = 0
|
|
|
|
else:
|
|
|
|
pop_count = len(self._dict) - self.maxlen
|
2017-04-01 07:32:13 +00:00
|
|
|
pop_count = max(0, pop_count)
|
2017-03-30 22:56:01 +00:00
|
|
|
keys = sorted(self._recency.keys(), key=self._recency.get)
|
2017-04-01 07:08:56 +00:00
|
|
|
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)
|
2017-03-30 22:56:01 +00:00
|
|
|
|
2017-05-07 01:28:41 +00:00
|
|
|
def clear(self):
|
|
|
|
self._dict.clear()
|
|
|
|
self._recency.clear()
|
|
|
|
|
2017-03-30 22:56:01 +00:00
|
|
|
def get(self, key, fallback=None):
|
|
|
|
try:
|
|
|
|
return self[key]
|
|
|
|
except KeyError:
|
|
|
|
return fallback
|
|
|
|
|
|
|
|
def remove(self, key):
|
|
|
|
try:
|
|
|
|
self._dict.pop(key)
|
|
|
|
self._recency.pop(key)
|
|
|
|
except KeyError:
|
|
|
|
return
|