Welcome, guest | Sign In | My Account | Store | Cart
'''A simple implementation of a LRU dict that supports discarding by maximum
capacity and by maximum time not being used.'''

from collections import OrderedDict
import time

class LRUDict(OrderedDict):
    '''An dict that can discard least-recently-used items, either by maximum capacity
    or by time to live.
    An item's ttl is refreshed (aka the item is considered "used") by direct access
    via [] or get() only, not via iterating over the whole collection with items()
    for example.
    Expired entries only get purged after insertions or changes. Either call purge()
    manually or check an item's ttl with ttl() if that's unacceptable.
    def __init__(self, *args, maxduration=None, maxsize=128, **kwargs):
        '''Same arguments as OrderedDict with these 2 additions:
        maxduration: number of seconds entries are kept. 0 or None means no timelimit.
        maxsize: maximum number of entries being kept.'''
        super().__init__(*args, **kwargs)
        self.maxduration = maxduration
        self.maxsize = maxsize

    def purge(self):
        """Removes expired or overflowing entries."""
        if self.maxsize:
            # pop until maximum capacity is reached
            overflowing = max(0, len(self) - self.maxsize)
            for _ in range(overflowing):
        if self.maxduration:
            # expiration limit
            limit = time.time() - self.maxduration
            # as long as there are still items in the dictionary
            while self:
                # look at the oldest (front)
                _, lru = next(iter(super().values()))
                # if it is within the timelimit, we're fine
                if lru > limit:
                # otherwise continue to pop the front

    def __getitem__(self, key):
        # retrieve item
        value = super().__getitem__(key)[0]
        # update lru time
        super().__setitem__(key, (value, time.time()))
        return value

    def get(self, key, default=None):
            return self[key]
        except KeyError:
            return default

    def ttl(self, key):
        '''Returns the number of seconds this item will live.
        The item might still be deleted if maxsize is reached.
        The time to live can be negative, as for expired items
        that have not been purged yet.'''
        if self.maxduration:
            lru = super().__getitem__(key)[1]
            return self.maxduration - (time.time() - lru)

    def __setitem__(self, key, value):
        super().__setitem__(key, (value, time.time()))
    def items(self):
        # remove ttl from values
        return ((k, v) for k, (v, _) in super().items())
    def values(self):
        # remove ttl from values
        return (v for v, _ in super().values())

def main():
    dct = LRUDict(maxduration=2)
    print(dct)  # empty
    dct["a"] = 5
    print(dct)  # a
    dct["b"] = 10
    print(dct)  # a, b
    dct["c"] = 20
    print(dct)  # b, c
    print(dct)  # c
    for k, v in dct.items():
        print("k:%s, v:%s" % (k, v))
    for v in dct.values():
        print("v:%s" % (v, ))

if __name__ == "__main__":