0ba54ed4fc
Makes accesses simpler and reduces the number of dictionaries to one, in an effort to remove race conditions
57 lines
1.5 KiB
Python
57 lines
1.5 KiB
Python
from datetime import datetime
|
|
|
|
class ExpiresEntry(object):
|
|
""" A single entry under a ExpiresDict. """
|
|
def __init__(self, value, expires=None):
|
|
self.value = value
|
|
self._expiration = expires
|
|
|
|
@property
|
|
def expired(self):
|
|
if self._expiration is None:
|
|
return False
|
|
|
|
return datetime.now() >= self._expiration
|
|
|
|
|
|
class ExpiresDict(object):
|
|
""" ExpiresDict defines a dictionary-like class whose keys have expiration. The rebuilder is
|
|
a function that returns the full contents of the cached dictionary as a dict of the keys
|
|
and whose values are TTLEntry's.
|
|
"""
|
|
def __init__(self, rebuilder):
|
|
self._rebuilder = rebuilder
|
|
self._items = {}
|
|
|
|
def __getitem__(self, key):
|
|
found = self.get(key)
|
|
if found is None:
|
|
raise KeyError
|
|
|
|
return found
|
|
|
|
def get(self, key, default_value=None):
|
|
# Check the cache first. If the key is found and it has not yet expired,
|
|
# return it.
|
|
found = self._items.get(key)
|
|
if found is not None and not found.expired:
|
|
return found.value
|
|
|
|
# Otherwise the key has expired or was not found. Rebuild the cache and check it again.
|
|
items = self._rebuild()
|
|
found_item = items.get(key)
|
|
if found_item is None:
|
|
return default_value
|
|
|
|
return found_item.value
|
|
|
|
def __contains__(self, key):
|
|
return self.get(key) is not None
|
|
|
|
def _rebuild(self):
|
|
items = self._rebuilder()
|
|
self._items = items
|
|
return items
|
|
|
|
def set(self, key, value, expires=None):
|
|
self._items[key] = ExpiresEntry(value, expires=expires)
|