Source

gnocchi-tools / gnocchi / tools / cache.py

'''Buffering and Caching Descriptors'''
import functools
from django.core.cache import get_cache
import pickle

class buffered_property(object):
    '''Buffer the result of a method on the class instance'''
    def __init__(self, getter):
        functools.update_wrapper(self, getter)
        self.getter = getter
        self.propname = '__cache__%s' % getter.__name__

    def __get__(self, instance, owner):
        if instance is None:
            return None
        if not hasattr(instance, self.propname):
            setattr(instance, self.propname, self.getter(instance))
        return getattr(instance, self.propname)

    def __set__(self, instance, value):
        setattr(instance, self.propname, value)

    def __delete__(self, instance):
        delattr(instance, self.propname)

class _cached_property(object):
    '''Cache the result of a method in the Django cache'''
    def __init__(self, getter, backend='default', timeout=None,
        use_pickle=False, buffer=True):
        '''Get values from cache'''
        functools.update_wrapper(self, getter)
        self.getter = getter
        self.timeout = timeout
        self.pickle = use_pickle
        self.cache = get_cache(backend)
        self.buffer = buffer
        self.propname = '__cache__%s' % getter.__name__

    def _make_key(self, instance):
        '''Build the cache key to use'''
        parts = [instance.__class__.__name__]
        if hasattr(instance, '_meta'):
            # If this looks like a Model, use app label and pk
            parts.insert(0, instance._meta.app_label)
            parts.append(str(instance.pk))
        parts.append(self.__name__)
        return ':'.join(parts)

    def __get__(self, instance, owner):
        if instance is None:
            return None
        if self.buffer:
            try:
                return getattr(instance, self.propname)
            except AttributeError:
                pass
        key = self._make_key(instance)
        value = self.cache.get(key)
        if value is None:
            value = self.getter(instance)
            if self.pickle:
                value = pickle.dumps(value)
            self.cache.set(key, value, self.timeout)
        if self.pickle:
            value = pickle.loads(value)
        if self.buffer:
            setattr(instance, self.propname, value)
        return value

    def __delete__(self, instance):
        key = self._make_key(instance)
        self.cache.delete(key)
        if self.buffer:
            try:
                delattr(instance, self.propname)
            except AttributeError:
                pass

def cached_property(func=None, backend='default', timeout=None, use_pickle=False, buffer=True):
    if func is None:
        return functools.partial(cached_property,
            backend=backend,
            timeout=timeout,
            use_pickle=use_pickle
        )
    return _cached_property(func, backend=backend, timeout=timeout,
        use_pickle=use_pickle, buffer=buffer)

class CacheDict(object):
    '''A wrapper around a Cache to give it a dict feel'''
    def __init__(self, backend='default', timeout=None, prefix=None, pickle=False):
        self.pickle = pickle
        self.timeout = timeout
        self.prefix = prefix
        self.cache = get_cache(backend)

    def _key(self, key):
        if self.prefix:
            return ':'.join([self.prefix, key])
        return key

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

    def __getitem__(self, key):
        _key = self._key(key)
        if not self.cache.has_key(_key):
            raise KeyError(key)
        value = self.cache.get(_key)
        if self.pickle:
            value = pickle.dumps(value)
        return value

    def __setitem__(self, key, value):
        if self.pickle:
            value = pickle.dumps(value)
        self.cache.set(self._key(key), value, timeout=self.timeout)

    def __delitem__(self, key):
        _key = self._key(key)
        if not self.cache.has_key(_key):
            raise AttributeError(key)
        self.cache.delete(_key)