flaskext-durus / flaskext / keep.py

"""
$URL: svn+ssh://sting.mems-exchange.org/repos/trunk/DurusWorks/qp/lib/keep.py $
$Id: keep.py 32474 2010-06-09 15:09:26Z dbinger $
"""
from __future__ import absolute_import

from durus.btree import BTree
from durus.persistent import PersistentObject
from durus.persistent_dict import PersistentDict

from .spec import either, datetime_without_tz, mapping, spec, integer
from .spec import require, get_spec_problems, anything, specify

from datetime import datetime

def rand_int(bits):
    bytes, remainder = divmod(bits, 8)
    if remainder == 0:
        return int(randbytes(bytes), 16)
    else:
        return int(randbytes(bytes + 1), 16) >> (8 - remainder)

class Keyed (object):
    """
    An item with an int key used as a key in an Keep.
    Note that the key attribute is set when the item is put in the Keep,
    so there is no set_key() method.
    This is a mixin for PersistentObject classes.
    """
    key_is = integer

    __slots__ = []

    def __init__(self):
        assert isinstance(self, PersistentObject)
        self.key = None

    def get_key(self):
        return self.key


class KeyCounter (PersistentObject):
    """
    The base class.  Generates keys using random 64 bit ints.
    """
    def next(self):
        """() -> integer
        This base implementation just uses a big random number.
        """
        return rand_int(64)


class Counter (KeyCounter):
    """
    Generates keys by incrementing them.
    """
    next_available_is = integer

    __slots__ = ['next_available']

    def __init__(self):
        self.next_available = 1

    def next(self):
        result = self.next_available
        self.next_available = self.next_available + 1
        return result


class GrowingRandomCounter (KeyCounter):
    """
    Keep a key counter that generates keys randomly, and gradually
    expands the numbers of bits in the random numbers.
    The idea is to take advantage of randomly distributed keys, but
    to try to keep the numbers relatively small.
    """
    bits_is = integer
    used_is = integer

    __slots__ = ['bits', 'used']

    def __init__(self):
        specify(self, bits=10, used=0)

    def next(self):
        self.used = self.used + 1
        # If more than 1% of the possible values have been used,
        # double the space of possible values.
        if (self.used / (2 ** self.bits)) > 0.01:
            self.bits += 1
        return rand_int(self.bits)


class Keep (object):
    """
    A simple database that stores a mapping of objects by key.
    This is a mixin for PersistentObject classes.
    """
    value_spec_is = spec(
        anything,
        "Specifies the type of values allowed in mapping")
    mapping_is = mapping({integer:Keyed}, either(PersistentDict, BTree))
    key_counter_is = KeyCounter
    __slots__ = []

    def __init__(self, value_spec=Keyed, mapping_class=BTree, counter_class=Counter):
        assert isinstance(self, PersistentObject)
        self.mapping = mapping_class()
        self.value_spec = value_spec
        self.set_counter(counter_class())

    def set_counter(self, counter):
        specify(self, key_counter=counter)

    def __len__(self):
        return len(self.mapping)

    def get_mapping(self):
        return self.mapping

    def get(self, key, default=None):
        return self.mapping.get(key, default)

    def itervalues(self):
        for value in self.mapping.itervalues():
            yield value

    def iterkeys(self):
        for key in self.mapping.iterkeys():
            yield key

    def iteritems(self):
        for item in self.mapping.iteritems():
            yield item

    def add(self, value):
        require(value, self.value_spec)
        assert value.key is None
        for attempt in range(1000):
            value.key = self.key_counter.next()
            if value.key not in self.mapping:
                break
        else:
            raise KeyError('Unable to find unused key for %r.' % value)
        if get_spec_problems(value):
            raise TypeError(''.join(get_spec_problems(value)))
        self.mapping[value.key] = value

    def remove(self, value):
        require(value, self.value_spec)
        assert value.key is self.mapping[value.key].key
        del self.mapping[value.key]


class Stamped (object):
    """
    Provides a timestamp.
    This is a mixin for PersistentObject classes.  
    """
    stamp_is = datetime_without_tz

    __slots__ = []

    def __init__(self):
        assert isinstance(self, PersistentObject)
        self.set_stamp()

    def get_stamp(self):
        return self.stamp

    def set_stamp(self):
        self.stamp = datetime.utcnow()


def stamp_sorted(stamped_sequence):
    items = [(x.stamp, x) for x in stamped_sequence]
    return [x for (stamp, x) in sorted(items)]

def reverse_stamp_sorted(stamped_sequence):
    result = stamp_sorted(stamped_sequence)
    result.reverse()
    return result
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.