Extended cacheable callables and properties

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
from copy import copy
from django.core.cache import cache


class Cacheable(object):
    '''A higher level API for caching results of callables.

    This class provides a simple API for caching and retrieving transparently
    the result of a call using Django's cache, as well as (re)setting and
    deleting the cached value.

    In addition to being themselves callable, ``Cacheable`` instances are
    non-data descriptors and work seamlessly as methods.

    Example::

        class Foo(object):
            @Cacheable.decorate(key='bar_{1}')
            def bar(self, x):
                print('Computing bar(%s)..' % x)
                return x*x

        >>> foo = Foo()
        >>> foo.bar(4)
        Computing bar(4)..
        16
        >>> foo.bar(4)              # result is now cached
        16
        >>> foo.bar.delete_cache(4) # delete the cache for this param
        >>> foo.bar(4)              # recompute (and cache) it
        Computing bar(4)..
        16
    '''

    def __init__(self, func, key, timeout=None):
        '''Initializes a cacheable wrapper of ``func``.

        :param func: The callable to be wrapped. It may take any number of
            positional and/or keywords arguments.
        :param key: Specifies how to determine the cache key for a given
            ``func(*args, **kwds)`` call; it can be:

            - A callable: The cache key is computed as ``key(*args, **kwds)``.
              Obviously ``key`` must have the same (or compatible) signature
              with ``func``.
            - A string: A format string in PEP 3101 syntax. The cache key is
              determined as ``key.format(*args, **kwds)``.
        :param timeout: If given, that timeout will be used for the key;
            otherwise the default cache timeout will be used.
        '''
        self._func = func
        self._timeout = timeout
        self._obj = None    # for bound methods' im_self object
        if callable(key):
            self._key = key
        elif isinstance(key, basestring):
            self._key = key.format
        else:
            raise TypeError('%s keys are invalid' % key.__class__.__name__)


    @classmethod
    def decorate(cls, *args, **kwds):
        '''A decorator for wrapping a callable into a :class:`Cacheable` instance.'''
        return lambda func: cls(func, *args, **kwds)

    def __call__(self, *args, **kwds):
        '''Returns the cached result of ``func(*args, **kwds)`` or computes and
        caches it if it's not already cached.

        This method alone covers the majority of use cases, allowing clients to
        use ``Cacheable`` instances as plain callables.

        :returns: The cached or computed result.
        '''
        if self._obj is not None:
            args = (self._obj,) + args
        key = self._key(*args, **kwds)
        value = cache.get(key)
        if value is None:
            value = self._func(*args, **kwds)
            cache.set(key, value, timeout=self._timeout)
        return value

    def set_cache(self, *args, **kwds):
        '''Computes ``func(*args, **kwds)`` and stores it in the cache.

        Unlike :meth:`__call__`, this method sets the cache unconditionally,
        without checking first if a key corresponding to a call with the same
        parameters already exists.

        :returns: The newly computed (and cached) result.
        '''
        if self._obj is not None:
            args = (self._obj,) + args
        key = self._key(*args, **kwds)
        value = self._func(*args, **kwds)
        cache.set(key, value, timeout=self._timeout)
        return value

    def delete_cache(self, *args, **kwds):
        '''Deletes the cached result (if any) corresponding to a call with
        ``args`` and ``kwds``.
        '''
        cache.delete(self.get_cache_key(*args, **kwds))

    def get_cache_key(self, *args, **kwds):
        '''Returns the cache key corresponding to a call with ``args`` and ``kwds``.

        This is mainly for debugging and for interfacing with external services;
        clients of this class normally don't need to deal with cache keys explicitly.
        '''
        if self._obj is not None:
            return self._key(self._obj, *args, **kwds)
        return self._key(*args, **kwds)

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        new = copy(self)
        new._obj = obj
        return new


class CacheableProperty(property, Cacheable):
    '''A :class:`Cacheable` that is also a property.

    Example::

        class Foo(object):
            @CacheableProperty.decorate(key='bar_{0}')
            def bar(self):
                print('Computing bar()..')
                return 42

        >>> foo = Foo()
        >>> foo.bar       # just like a regular property
        Computing bar()..
        42
        >>> foo.bar       # result is now cached
        42
        >>> # Cacheable methods are available through the class
        >>> Foo.bar.delete_cache(foo)
        >>> foo.bar
        Computing bar()..
        42
    '''

    def __init__(self, func, key, timeout=None, fset=None, fdel=None, doc=None):
        Cacheable.__init__(self, func, key, timeout=timeout)
        property.__init__(self, fget=self.__call__, fset=fset, fdel=fdel)
        self.__doc__ = doc or getattr(func, '__doc__', None)

More like this

  1. Extended i18n base model by alcinnz 1 year ago
  2. DRY with common model fields (another way) by jmrbcu 6 years, 9 months ago
  3. Coffeescript compilation by delfick 1 year, 2 months ago
  4. Sorl Thumbnail + Amazon S3 by skoczen 4 years, 10 months ago
  5. Scalable and invalidateble cache_page decorator by marinho 4 years, 11 months ago

Comments

(Forgotten your password?)