import time, typing, uuid class Cache(object): def __init__(self): self._items = {} self._item_to_id = {} def cache(self, item: typing.Any) -> str: return self._cache(item, None) def temporary_cache(self, item: typing.Any, timeout: float)-> str: return self._cache(item, time.monotonic()+timeout) def _cache(self, item: typing.Any, timeout: typing.Optional[float]) -> str: id = str(uuid.uuid4()) self._items[id] = [item, timeout] self._item_to_id[item] = id return id def next_expiration(self) -> typing.Optional[float]: expirations = [self._items[id][1] for id in self._items] expirations = list(filter(None, expirations)) if not expirations: return None expirations = [e-time.monotonic() for e in expirations] return max(min(expirations), 0) def expire(self): now = time.monotonic() expired = [] for id in self._items: item, expiration = self._items[id] if expiration and expiration <= now: expired.append(id) for id in expired: item, expiration = self._items[id] del self._items[id] del self._item_to_id[item] def has_item(self, item: typing.Any) -> bool: return item in self._item_to_id def get(self, id: str) -> typing.Any: item, expiration = self._items[id] return item def get_expiration(self, item: typing.Any) -> float: id = self._item_to_id[item] item, expiration = self._items[id] return expiration def until_expiration(self, item: typing.Any) -> float: expiration = self.get_expiration(item) return expiration-time.monotonic()