Example #1
0
 def __init__(self, time_provider, max_size=None):
     """Create an expiration dictionary.
     @param time_provider: who provide the time
     @type time_provider: L{ITimeProvider}
     @param max_size: maximum size before forced packing
     @type max_size: int"""
     self._time = ITimeProvider(time_provider)
     self._items = {}  # {KEY: ExpItem(TIME, VALUE)}
     self._max_size = RunningAverage(max_size or self.DEFAULT_MAX_SIZE)
     self._last_pack = 0
Example #2
0
 def __init__(self, time_provider, max_size=None):
     '''Create an expiration queue.
     @param time_provider: who provide the time
     @type time_provider: L{ITimeProvider}
     @param max_size: maximum size before forced packing
     @type max_size: int'''
     self._time = ITimeProvider(time_provider)
     self._heap = []
     self._max_size = max_size or self.DEFAULT_MAX_SIZE
     self._last_pack = 0
Example #3
0
 def __init__(self, time_provider, max_size=None, on_expire=None):
     """Create an expiration queue.
     @param time_provider: who provide the time
     @type time_provider: L{ITimeProvider}
     @param max_size: maximum size before forced packing
     @type max_size: int"""
     self._time = ITimeProvider(time_provider)
     self._heap = []
     self._max_size = RunningAverage(max_size or self.DEFAULT_MAX_SIZE)
     self._last_pack = 0
     self._on_expire = on_expire
Example #4
0
class ExpQueue(ExpBase):
    """
    @warning: Getting the length, and therefore constructs like
              "if exp_dict: ..." or "bool(exp_dict)"
              will iterate over all elements.
    @warning: Comparison operations are very expensive.
    """

    DEFAULT_MAX_SIZE = 100

    type_name = "xqueue"

    classProvides(serialization.IRestorator)
    implements(serialization.ISerializable)

    __slots__ = ("_time", "_heap", "_max_size", "_last_pack")

    def __init__(self, time_provider, max_size=None, on_expire=None):
        """Create an expiration queue.
        @param time_provider: who provide the time
        @type time_provider: L{ITimeProvider}
        @param max_size: maximum size before forced packing
        @type max_size: int"""
        self._time = ITimeProvider(time_provider)
        self._heap = []
        self._max_size = RunningAverage(max_size or self.DEFAULT_MAX_SIZE)
        self._last_pack = 0
        self._on_expire = on_expire

    def pack(self):
        """Packs the set by removing all expired items."""
        self._pack(self._time.get_time())

    def clear(self):
        """Removes all the values from the queue."""
        self._heap = []

    def add(self, value, expiration=None, relative=False):
        """Adds an entry to the queue with specified expiration and value.
        @param value: black box associated with the key
        @type value: any python structure or L{ISerializable}
        @param expiration: the time at which the entry will expire.
        @type expiration: float
        @param relative: if the specified expiration time is relative
                         to EPOC UTC or from now.
        @type relative: bool
        @return: nothing"""
        self._lazy_pack()
        now = self._time.get_time()
        if expiration is not None:
            now = self._time.get_time()
            if relative:
                expiration = now + expiration
            if expiration <= now:
                return
        heapq.heappush(self._heap, ExpItem(expiration, value))

    def pop(self):
        """Pops and returns the value with the smaller expiration.
        @returns: value
        @rtype: any python structure or L{ISerializable}"""
        self._lazy_pack()
        now = self._time.get_time()
        while True:
            try:
                item = heapq.heappop(self._heap)
                if item.exp is None or item.exp > now:
                    return item.value
                elif callable(self._on_expire):
                    self._on_expire(item.value)
            except IndexError:
                raise Empty(), None, sys.exc_info()[2]

    def size(self):
        """Returns the current size counting expired values."""
        return len(self._heap)

    def __iter__(self):
        """Returns an iterator over queue's values."""
        self._lazy_pack()
        now = self._time.get_time()
        for item in iter(self._heap):
            if item.exp is None or item.exp > now:
                yield item.value
                now = self._time.get_time()

    def __len__(self):
        now = self._time.get_time()
        return len([i for i in self._heap if i.exp is None or i.exp > now])

    def __eq__(self, other):
        if not issubclass(type(other), type(self)):
            return NotImplemented
        now = self._time.get_time()
        a = [(i.pri, i.value) for i in self._heap if i.exp is None or i.exp > now]
        b = [(i.pri, i.value) for i in other._heap if i.exp is None or i.exp > now]
        a.sort()
        b.sort()
        return a == b

    def __ne__(self, other):
        eq = self.__eq__(other)
        return eq if eq == NotImplemented else not eq

    ### ISerializable Method ###

    def snapshot(self):
        now = self._time.get_time()
        return self._time, [i.snapshot() for i in self._heap if i.exp is None or i.exp > now]

    def recover(self, snapshot):
        self._time, data = snapshot
        self._heap = [ExpItem.restore(d) for d in data]
        self._last_pack = 0

    ### Private Methods ###

    def _lazy_pack(self):
        if len(self._heap) > self._max_size.get_value() * 1.25:
            now = self._time.get_time()
            # Regulate lazy packing rate
            if (now - self._last_pack) >= (1.0 / MAX_LAZY_PACK_PER_SECOND):
                self._pack(now)

    def _pack(self, now):
        new_heap = []
        for i in self._heap:
            if i.exp is None or i.exp > now:
                new_heap.append(i)
            elif callable(self._on_expire):
                self._on_expire(i.value)

        heapq.heapify(new_heap)
        self._heap = new_heap
        self._last_pack = now
        self._max_size.add_point(len(new_heap))
Example #5
0
class ExpDict(ExpBase):
    """
    @warning: Getting the length, and therefore construct like
              "if exp_dict: ..." or "bool(exp_dict)",
              will iterate over all elements.
    @warning: Comparison operations are very expensive.
    """

    DEFAULT_MAX_SIZE = 100

    type_name = "xdict"

    classProvides(serialization.IRestorator)
    implements(serialization.ISerializable)

    __slots__ = ("_time", "_items", "_max_size", "_last_pack")

    def __init__(self, time_provider, max_size=None):
        """Create an expiration dictionary.
        @param time_provider: who provide the time
        @type time_provider: L{ITimeProvider}
        @param max_size: maximum size before forced packing
        @type max_size: int"""
        self._time = ITimeProvider(time_provider)
        self._items = {}  # {KEY: ExpItem(TIME, VALUE)}
        self._max_size = RunningAverage(max_size or self.DEFAULT_MAX_SIZE)
        self._last_pack = 0

    def clear(self):
        """Removes all items from the dictionary."""
        self._items.clear()

    def pack(self):
        """Packs the dictionary by removing all expired items."""
        self._pack(self._time.get_time())

    def set(self, key, value=None, expiration=None, relative=False):
        """Adds an entry to the dictionary with specified expiration and value.
        @param key: unique key of the entry, used to remove or test ownership
        @type key: any immutable
        @param value: black box associated with the key
        @type value: any python structure or L{ISerializable}
        @param expiration: the time at which the entry will expire.
        @type expiration: float
        @param relative: if the specified expiration time is relative
                         to EPOC UTC or from now.
        @type relative: bool
        @return: nothing"""
        self._lazy_pack()
        if expiration is not None:
            now = self._time.get_time()
            if relative:
                expiration = now + expiration
            if expiration <= now:
                return
        self._items[key] = ExpItem(expiration, value)

    def remove(self, key):
        """Removes the dictionary entry with with specified key .
        @param key: unique key of the entry, used to remove or test ownership
        @type key: any immutable
        @return: item value
        @rtype: any python structure or L{ISerializable}"""
        self._lazy_pack()
        now = self._time.get_time()
        if self._items:
            item = self._items.pop(key)
            if item.exp is None or item.exp > now:
                return item.value
        raise KeyError(key)

    def pop(self, key, *default):
        """Pops and returns the dictionary entry with with specified key.
        @param key: unique key of the entry, used to remove or test ownership
        @type key: any immutable
        @return: value
        @rtype: any python structure or L{ISerializable}"""
        self._lazy_pack()
        now = self._time.get_time()
        if self._items:
            try:
                item = self._items.pop(key)
                if item.exp is None or item.exp > now:
                    return item.value
                raise KeyError(key)
            except KeyError:
                if len(default) == 1:
                    return default[0]
                else:
                    raise

    def get(self, key, default=None):
        """Retrieve value from the entry with specified key.
        @param key: unique key of the entry, used to remove or test ownership
        @type key: any immutable
        @param default: value returned if no entry is found with specified key
        @type default: any python structure or L{ISerializable}
        @return: entry value or default value
        @rtype: any python structure or L{ISerializable}"""
        item = self._get_item(key)
        return default if item is None else item.value

    def get_expiration(self, key):
        item = self._get_item(key)
        if item is None:
            raise KeyError(key)
        return item.exp

    def iterkeys(self):
        """Returns an iterator over the dictionary keys."""
        self._lazy_pack()
        now = self._time.get_time()
        for key, item in self._items.iteritems():
            if item.exp is None or item.exp > now:
                yield key
                now = self._time.get_time()

    def itervalues(self):
        """Returns an iterator over the dictionary values."""
        self._lazy_pack()
        now = self._time.get_time()
        for item in self._items.itervalues():
            if item.exp is None or item.exp > now:
                yield item.value
                now = self._time.get_time()

    def values(self):
        return list(self.itervalues())

    def keys(self):
        return list(self.iterkeys())

    def iteritems(self):
        """Returns an iterator over tuples (key, value)."""
        self._lazy_pack()
        now = self._time.get_time()
        for key, item in self._items.iteritems():
            if item.exp is None or item.exp > now:
                yield key, item.value
                now = self._time.get_time()

    def size(self):
        """Returns the current size counting expired elements."""
        return len(self._items)

    def __setitem__(self, key, value):
        self._lazy_pack()
        self._items[key] = ExpItem(None, value)

    def __getitem__(self, key):
        item = self._get_item(key)
        if item is not None:
            return item.value
        raise KeyError(key)

    def __delitem__(self, key):
        item = self._get_item(key)
        if item is not None:
            del self._items[key]
            return
        raise KeyError(key)

    def __contains__(self, key):
        return self._get_item(key) is not None

    def __iter__(self):
        return self.iterkeys()

    def __len__(self):
        now = self._time.get_time()
        return len([i for i in self._items.itervalues() if i.exp is None or i.exp > now])

    def __eq__(self, other):
        if not issubclass(type(other), type(self)):
            return NotImplemented
        now = self._time.get_time()
        a = [(k, i.pri, i.value) for k, i in self._items.iteritems() if i.exp is None or i.exp > now]
        b = [(k, i.pri, i.value) for k, i in other._items.iteritems() if i.exp is None or i.exp > now]
        a.sort()
        b.sort()
        return a == b

    def __ne__(self, other):
        eq = self.__eq__(other)
        return eq if eq == NotImplemented else not eq

    def __repr__(self):
        values = ["%s=%s" % (k, v) for k, v in self.iteritems()]
        return "<xdict: {%s}>" % (", ".join(values),)

    ### ISerializable Method ###

    def snapshot(self):
        return (self._time, self._max_size.get_value(), dict([(k, i.snapshot()) for k, i in self._items.iteritems()]))

    def recover(self, snapshot):
        self._time, max_size, data = snapshot
        self._max_size = RunningAverage(max_size)
        self._items = dict([(k, ExpItem.restore(s)) for k, s in data.iteritems()])
        self._last_pack = 0

    ### Private Methods ###

    def _lazy_pack(self):
        if len(self._items) > self._max_size.get_value() * 1.25:
            now = self._time.get_time()
            # Regulate lazy packing rate
            if (now - self._last_pack) >= (1.0 / MAX_LAZY_PACK_PER_SECOND):
                self._pack(now)

    def _pack(self, now):
        self._items = dict([(k, i) for k, i in self._items.iteritems() if i.exp is None or i.exp > now])
        self._last_pack = now
        self._max_size.add_point(len(self._items))

    def _get_item(self, key):
        self._lazy_pack()
        now = self._time.get_time()
        item = self._items.get(key, None)
        if item is not None:
            if item.exp is None or item.exp > now:
                return item
            del self._items[key]
        return None