1from __future__ import with_statement
2from .. import Lock, NeedRegenerationException
3from ..util import NameRegistry
4from . import exception
5from ..util import PluginLoader, memoized_property, coerce_string_conf
6from .util import function_key_generator, function_multi_key_generator
7from .api import NO_VALUE, CachedValue
8from .proxy import ProxyBackend
9from ..util import compat
10import time
11import datetime
12from numbers import Number
13from functools import wraps, partial
14import threading
15from decorator import decorate
16
17_backend_loader = PluginLoader("dogpile.cache")
18register_backend = _backend_loader.register
19from . import backends  # noqa
20
21value_version = 1
22"""An integer placed in the :class:`.CachedValue`
23so that new versions of dogpile.cache can detect cached
24values from a previous, backwards-incompatible version.
25
26"""
27
28
29class RegionInvalidationStrategy(object):
30    """Region invalidation strategy interface
31
32    Implement this interface and pass implementation instance
33    to :meth:`.CacheRegion.configure` to override default region invalidation.
34
35    Example::
36
37        class CustomInvalidationStrategy(RegionInvalidationStrategy):
38
39            def __init__(self):
40                self._soft_invalidated = None
41                self._hard_invalidated = None
42
43            def invalidate(self, hard=None):
44                if hard:
45                    self._soft_invalidated = None
46                    self._hard_invalidated = time.time()
47                else:
48                    self._soft_invalidated = time.time()
49                    self._hard_invalidated = None
50
51            def is_invalidated(self, timestamp):
52                return ((self._soft_invalidated and
53                         timestamp < self._soft_invalidated) or
54                        (self._hard_invalidated and
55                         timestamp < self._hard_invalidated))
56
57            def was_hard_invalidated(self):
58                return bool(self._hard_invalidated)
59
60            def is_hard_invalidated(self, timestamp):
61                return (self._hard_invalidated and
62                        timestamp < self._hard_invalidated)
63
64            def was_soft_invalidated(self):
65                return bool(self._soft_invalidated)
66
67            def is_soft_invalidated(self, timestamp):
68                return (self._soft_invalidated and
69                        timestamp < self._soft_invalidated)
70
71    The custom implementation is injected into a :class:`.CacheRegion`
72    at configure time using the
73    :paramref:`.CacheRegion.configure.region_invalidator` parameter::
74
75        region = CacheRegion()
76
77        region = region.configure(region_invalidator=CustomInvalidationStrategy())
78
79    Invalidation strategies that wish to have access to the
80    :class:`.CacheRegion` itself should construct the invalidator given the
81    region as an argument::
82
83        class MyInvalidator(RegionInvalidationStrategy):
84            def __init__(self, region):
85                self.region = region
86                # ...
87
88            # ...
89
90        region = CacheRegion()
91        region = region.configure(region_invalidator=MyInvalidator(region))
92
93    .. versionadded:: 0.6.2
94
95    .. seealso::
96
97        :paramref:`.CacheRegion.configure.region_invalidator`
98
99    """
100
101    def invalidate(self, hard=True):
102        """Region invalidation.
103
104        :class:`.CacheRegion` propagated call.
105        The default invalidation system works by setting
106        a current timestamp (using ``time.time()``) to consider all older
107        timestamps effectively invalidated.
108
109        """
110
111        raise NotImplementedError()
112
113    def is_hard_invalidated(self, timestamp):
114        """Check timestamp to determine if it was hard invalidated.
115
116        :return: Boolean. True if ``timestamp`` is older than
117         the last region invalidation time and region is invalidated
118         in hard mode.
119
120        """
121
122        raise NotImplementedError()
123
124    def is_soft_invalidated(self, timestamp):
125        """Check timestamp to determine if it was soft invalidated.
126
127        :return: Boolean. True if ``timestamp`` is older than
128         the last region invalidation time and region is invalidated
129         in soft mode.
130
131        """
132
133        raise NotImplementedError()
134
135    def is_invalidated(self, timestamp):
136        """Check timestamp to determine if it was invalidated.
137
138        :return: Boolean. True if ``timestamp`` is older than
139         the last region invalidation time.
140
141        """
142
143        raise NotImplementedError()
144
145    def was_soft_invalidated(self):
146        """Indicate the region was invalidated in soft mode.
147
148        :return: Boolean. True if region was invalidated in soft mode.
149
150        """
151
152        raise NotImplementedError()
153
154    def was_hard_invalidated(self):
155        """Indicate the region was invalidated in hard mode.
156
157        :return: Boolean. True if region was invalidated in hard mode.
158
159        """
160
161        raise NotImplementedError()
162
163
164class DefaultInvalidationStrategy(RegionInvalidationStrategy):
165
166    def __init__(self):
167        self._is_hard_invalidated = None
168        self._invalidated = None
169
170    def invalidate(self, hard=True):
171        self._is_hard_invalidated = bool(hard)
172        self._invalidated = time.time()
173
174    def is_invalidated(self, timestamp):
175        return (self._invalidated is not None and
176                timestamp < self._invalidated)
177
178    def was_hard_invalidated(self):
179        return self._is_hard_invalidated is True
180
181    def is_hard_invalidated(self, timestamp):
182        return self.was_hard_invalidated() and self.is_invalidated(timestamp)
183
184    def was_soft_invalidated(self):
185        return self._is_hard_invalidated is False
186
187    def is_soft_invalidated(self, timestamp):
188        return self.was_soft_invalidated() and self.is_invalidated(timestamp)
189
190
191class CacheRegion(object):
192    r"""A front end to a particular cache backend.
193
194    :param name: Optional, a string name for the region.
195     This isn't used internally
196     but can be accessed via the ``.name`` parameter, helpful
197     for configuring a region from a config file.
198    :param function_key_generator:  Optional.  A
199     function that will produce a "cache key" given
200     a data creation function and arguments, when using
201     the :meth:`.CacheRegion.cache_on_arguments` method.
202     The structure of this function
203     should be two levels: given the data creation function,
204     return a new function that generates the key based on
205     the given arguments.  Such as::
206
207        def my_key_generator(namespace, fn, **kw):
208            fname = fn.__name__
209            def generate_key(*arg):
210                return namespace + "_" + fname + "_".join(str(s) for s in arg)
211            return generate_key
212
213
214        region = make_region(
215            function_key_generator = my_key_generator
216        ).configure(
217            "dogpile.cache.dbm",
218            expiration_time=300,
219            arguments={
220                "filename":"file.dbm"
221            }
222        )
223
224     The ``namespace`` is that passed to
225     :meth:`.CacheRegion.cache_on_arguments`.  It's not consulted
226     outside this function, so in fact can be of any form.
227     For example, it can be passed as a tuple, used to specify
228     arguments to pluck from \**kw::
229
230        def my_key_generator(namespace, fn):
231            def generate_key(*arg, **kw):
232                return ":".join(
233                        [kw[k] for k in namespace] +
234                        [str(x) for x in arg]
235                    )
236            return generate_key
237
238
239     Where the decorator might be used as::
240
241        @my_region.cache_on_arguments(namespace=('x', 'y'))
242        def my_function(a, b, **kw):
243            return my_data()
244
245     .. seealso::
246
247        :func:`.function_key_generator` - default key generator
248
249        :func:`.kwarg_function_key_generator` - optional gen that also
250        uses keyword arguments
251
252    :param function_multi_key_generator: Optional.
253     Similar to ``function_key_generator`` parameter, but it's used in
254     :meth:`.CacheRegion.cache_multi_on_arguments`. Generated function
255     should return list of keys. For example::
256
257        def my_multi_key_generator(namespace, fn, **kw):
258            namespace = fn.__name__ + (namespace or '')
259
260            def generate_keys(*args):
261                return [namespace + ':' + str(a) for a in args]
262
263            return generate_keys
264
265    :param key_mangler: Function which will be used on all incoming
266     keys before passing to the backend.  Defaults to ``None``,
267     in which case the key mangling function recommended by
268     the cache backend will be used.    A typical mangler
269     is the SHA1 mangler found at :func:`.sha1_mangle_key`
270     which coerces keys into a SHA1
271     hash, so that the string length is fixed.  To
272     disable all key mangling, set to ``False``.   Another typical
273     mangler is the built-in Python function ``str``, which can be used
274     to convert non-string or Unicode keys to bytestrings, which is
275     needed when using a backend such as bsddb or dbm under Python 2.x
276     in conjunction with Unicode keys.
277    :param async_creation_runner:  A callable that, when specified,
278     will be passed to and called by dogpile.lock when
279     there is a stale value present in the cache.  It will be passed the
280     mutex and is responsible releasing that mutex when finished.
281     This can be used to defer the computation of expensive creator
282     functions to later points in the future by way of, for example, a
283     background thread, a long-running queue, or a task manager system
284     like Celery.
285
286     For a specific example using async_creation_runner, new values can
287     be created in a background thread like so::
288
289        import threading
290
291        def async_creation_runner(cache, somekey, creator, mutex):
292            ''' Used by dogpile.core:Lock when appropriate  '''
293            def runner():
294                try:
295                    value = creator()
296                    cache.set(somekey, value)
297                finally:
298                    mutex.release()
299
300            thread = threading.Thread(target=runner)
301            thread.start()
302
303
304        region = make_region(
305            async_creation_runner=async_creation_runner,
306        ).configure(
307            'dogpile.cache.memcached',
308            expiration_time=5,
309            arguments={
310                'url': '127.0.0.1:11211',
311                'distributed_lock': True,
312            }
313        )
314
315     Remember that the first request for a key with no associated
316     value will always block; async_creator will not be invoked.
317     However, subsequent requests for cached-but-expired values will
318     still return promptly.  They will be refreshed by whatever
319     asynchronous means the provided async_creation_runner callable
320     implements.
321
322     By default the async_creation_runner is disabled and is set
323     to ``None``.
324
325     .. versionadded:: 0.4.2 added the async_creation_runner
326        feature.
327
328    """
329
330    def __init__(
331            self,
332            name=None,
333            function_key_generator=function_key_generator,
334            function_multi_key_generator=function_multi_key_generator,
335            key_mangler=None,
336            async_creation_runner=None,
337    ):
338        """Construct a new :class:`.CacheRegion`."""
339        self.name = name
340        self.function_key_generator = function_key_generator
341        self.function_multi_key_generator = function_multi_key_generator
342        self.key_mangler = self._user_defined_key_mangler = key_mangler
343        self.async_creation_runner = async_creation_runner
344        self.region_invalidator = DefaultInvalidationStrategy()
345
346    def configure(
347            self, backend,
348            expiration_time=None,
349            arguments=None,
350            _config_argument_dict=None,
351            _config_prefix=None,
352            wrap=None,
353            replace_existing_backend=False,
354            region_invalidator=None
355    ):
356        """Configure a :class:`.CacheRegion`.
357
358        The :class:`.CacheRegion` itself
359        is returned.
360
361        :param backend:   Required.  This is the name of the
362         :class:`.CacheBackend` to use, and is resolved by loading
363         the class from the ``dogpile.cache`` entrypoint.
364
365        :param expiration_time:   Optional.  The expiration time passed
366         to the dogpile system.  May be passed as an integer number
367         of seconds, or as a ``datetime.timedelta`` value.
368
369         .. versionadded 0.5.0
370            ``expiration_time`` may be optionally passed as a
371            ``datetime.timedelta`` value.
372
373         The :meth:`.CacheRegion.get_or_create`
374         method as well as the :meth:`.CacheRegion.cache_on_arguments`
375         decorator (though note:  **not** the :meth:`.CacheRegion.get`
376         method) will call upon the value creation function after this
377         time period has passed since the last generation.
378
379        :param arguments: Optional.  The structure here is passed
380         directly to the constructor of the :class:`.CacheBackend`
381         in use, though is typically a dictionary.
382
383        :param wrap: Optional.  A list of :class:`.ProxyBackend`
384         classes and/or instances, each of which will be applied
385         in a chain to ultimately wrap the original backend,
386         so that custom functionality augmentation can be applied.
387
388         .. versionadded:: 0.5.0
389
390         .. seealso::
391
392            :ref:`changing_backend_behavior`
393
394        :param replace_existing_backend: if True, the existing cache backend
395         will be replaced.  Without this flag, an exception is raised if
396         a backend is already configured.
397
398         .. versionadded:: 0.5.7
399
400        :param region_invalidator: Optional. Override default invalidation
401         strategy with custom implementation of
402         :class:`.RegionInvalidationStrategy`.
403
404         .. versionadded:: 0.6.2
405
406         """
407
408        if "backend" in self.__dict__ and not replace_existing_backend:
409            raise exception.RegionAlreadyConfigured(
410                "This region is already "
411                "configured with backend: %s.  "
412                "Specify replace_existing_backend=True to replace."
413                % self.backend)
414
415        try:
416            backend_cls = _backend_loader.load(backend)
417        except PluginLoader.NotFound:
418            raise exception.PluginNotFound(
419                "Couldn't find cache plugin to load: %s" % backend)
420
421        if _config_argument_dict:
422            self.backend = backend_cls.from_config_dict(
423                _config_argument_dict,
424                _config_prefix
425            )
426        else:
427            self.backend = backend_cls(arguments or {})
428
429        if not expiration_time or isinstance(expiration_time, Number):
430            self.expiration_time = expiration_time
431        elif isinstance(expiration_time, datetime.timedelta):
432            self.expiration_time = int(
433                compat.timedelta_total_seconds(expiration_time))
434        else:
435            raise exception.ValidationError(
436                'expiration_time is not a number or timedelta.')
437
438        if not self._user_defined_key_mangler:
439            self.key_mangler = self.backend.key_mangler
440
441        self._lock_registry = NameRegistry(self._create_mutex)
442
443        if getattr(wrap, '__iter__', False):
444            for wrapper in reversed(wrap):
445                self.wrap(wrapper)
446
447        if region_invalidator:
448            self.region_invalidator = region_invalidator
449
450        return self
451
452    def wrap(self, proxy):
453        ''' Takes a ProxyBackend instance or class and wraps the
454        attached backend. '''
455
456        # if we were passed a type rather than an instance then
457        # initialize it.
458        if type(proxy) == type:
459            proxy = proxy()
460
461        if not issubclass(type(proxy), ProxyBackend):
462            raise TypeError("Type %s is not a valid ProxyBackend"
463                            % type(proxy))
464
465        self.backend = proxy.wrap(self.backend)
466
467    def _mutex(self, key):
468        return self._lock_registry.get(key)
469
470    class _LockWrapper(object):
471        """weakref-capable wrapper for threading.Lock"""
472        def __init__(self):
473            self.lock = threading.Lock()
474
475        def acquire(self, wait=True):
476            return self.lock.acquire(wait)
477
478        def release(self):
479            self.lock.release()
480
481    def _create_mutex(self, key):
482        mutex = self.backend.get_mutex(key)
483        if mutex is not None:
484            return mutex
485        else:
486            return self._LockWrapper()
487
488    # cached value
489    _actual_backend = None
490
491    @property
492    def actual_backend(self):
493        """Return the ultimate backend underneath any proxies.
494
495        The backend might be the result of one or more ``proxy.wrap``
496        applications. If so, derive the actual underlying backend.
497
498        .. versionadded:: 0.6.6
499
500        """
501        if self._actual_backend is None:
502            _backend = self.backend
503            while hasattr(_backend, 'proxied'):
504                _backend = _backend.proxied
505            self._actual_backend = _backend
506        return self._actual_backend
507
508    def invalidate(self, hard=True):
509        """Invalidate this :class:`.CacheRegion`.
510
511        The default invalidation system works by setting
512        a current timestamp (using ``time.time()``)
513        representing the "minimum creation time" for
514        a value.  Any retrieved value whose creation
515        time is prior to this timestamp
516        is considered to be stale.  It does not
517        affect the data in the cache in any way, and is
518        **local to this instance of :class:`.CacheRegion`.**
519
520        .. warning::
521
522            The :meth:`.CacheRegion.invalidate` method's default mode of
523            operation is to set a timestamp **local to this CacheRegion
524            in this Python process only**.   It does not impact other Python
525            processes or regions as the timestamp is **only stored locally in
526            memory**.  To implement invalidation where the
527            timestamp is stored in the cache or similar so that all Python
528            processes can be affected by an invalidation timestamp, implement a
529            custom :class:`.RegionInvalidationStrategy`.
530
531        Once set, the invalidation time is honored by
532        the :meth:`.CacheRegion.get_or_create`,
533        :meth:`.CacheRegion.get_or_create_multi` and
534        :meth:`.CacheRegion.get` methods.
535
536        The method supports both "hard" and "soft" invalidation
537        options.  With "hard" invalidation,
538        :meth:`.CacheRegion.get_or_create` will force an immediate
539        regeneration of the value which all getters will wait for.
540        With "soft" invalidation, subsequent getters will return the
541        "old" value until the new one is available.
542
543        Usage of "soft" invalidation requires that the region or the method
544        is given a non-None expiration time.
545
546        .. versionadded:: 0.3.0
547
548        :param hard: if True, cache values will all require immediate
549         regeneration; dogpile logic won't be used.  If False, the
550         creation time of existing values will be pushed back before
551         the expiration time so that a return+regen will be invoked.
552
553         .. versionadded:: 0.5.1
554
555        """
556        self.region_invalidator.invalidate(hard)
557
558    def configure_from_config(self, config_dict, prefix):
559        """Configure from a configuration dictionary
560        and a prefix.
561
562        Example::
563
564            local_region = make_region()
565            memcached_region = make_region()
566
567            # regions are ready to use for function
568            # decorators, but not yet for actual caching
569
570            # later, when config is available
571            myconfig = {
572                "cache.local.backend":"dogpile.cache.dbm",
573                "cache.local.arguments.filename":"/path/to/dbmfile.dbm",
574                "cache.memcached.backend":"dogpile.cache.pylibmc",
575                "cache.memcached.arguments.url":"127.0.0.1, 10.0.0.1",
576            }
577            local_region.configure_from_config(myconfig, "cache.local.")
578            memcached_region.configure_from_config(myconfig,
579                                                "cache.memcached.")
580
581        """
582        config_dict = coerce_string_conf(config_dict)
583        return self.configure(
584            config_dict["%sbackend" % prefix],
585            expiration_time=config_dict.get(
586                "%sexpiration_time" % prefix, None),
587            _config_argument_dict=config_dict,
588            _config_prefix="%sarguments." % prefix,
589            wrap=config_dict.get(
590                "%swrap" % prefix, None),
591            replace_existing_backend=config_dict.get(
592                "%sreplace_existing_backend" % prefix, False),
593        )
594
595    @memoized_property
596    def backend(self):
597        raise exception.RegionNotConfigured(
598            "No backend is configured on this region.")
599
600    @property
601    def is_configured(self):
602        """Return True if the backend has been configured via the
603        :meth:`.CacheRegion.configure` method already.
604
605        .. versionadded:: 0.5.1
606
607        """
608        return 'backend' in self.__dict__
609
610    def get(self, key, expiration_time=None, ignore_expiration=False):
611        """Return a value from the cache, based on the given key.
612
613        If the value is not present, the method returns the token
614        ``NO_VALUE``. ``NO_VALUE`` evaluates to False, but is separate from
615        ``None`` to distinguish between a cached value of ``None``.
616
617        By default, the configured expiration time of the
618        :class:`.CacheRegion`, or alternatively the expiration
619        time supplied by the ``expiration_time`` argument,
620        is tested against the creation time of the retrieved
621        value versus the current time (as reported by ``time.time()``).
622        If stale, the cached value is ignored and the ``NO_VALUE``
623        token is returned.  Passing the flag ``ignore_expiration=True``
624        bypasses the expiration time check.
625
626        .. versionchanged:: 0.3.0
627           :meth:`.CacheRegion.get` now checks the value's creation time
628           against the expiration time, rather than returning
629           the value unconditionally.
630
631        The method also interprets the cached value in terms
632        of the current "invalidation" time as set by
633        the :meth:`.invalidate` method.   If a value is present,
634        but its creation time is older than the current
635        invalidation time, the ``NO_VALUE`` token is returned.
636        Passing the flag ``ignore_expiration=True`` bypasses
637        the invalidation time check.
638
639        .. versionadded:: 0.3.0
640           Support for the :meth:`.CacheRegion.invalidate`
641           method.
642
643        :param key: Key to be retrieved. While it's typical for a key to be a
644         string, it is ultimately passed directly down to the cache backend,
645         before being optionally processed by the key_mangler function, so can
646         be of any type recognized by the backend or by the key_mangler
647         function, if present.
648
649        :param expiration_time: Optional expiration time value
650         which will supersede that configured on the :class:`.CacheRegion`
651         itself.
652
653         .. versionadded:: 0.3.0
654
655        :param ignore_expiration: if ``True``, the value is returned
656         from the cache if present, regardless of configured
657         expiration times or whether or not :meth:`.invalidate`
658         was called.
659
660         .. versionadded:: 0.3.0
661
662        """
663
664        if self.key_mangler:
665            key = self.key_mangler(key)
666        value = self.backend.get(key)
667        value = self._unexpired_value_fn(
668            expiration_time, ignore_expiration)(value)
669
670        return value.payload
671
672    def _unexpired_value_fn(self, expiration_time, ignore_expiration):
673        if ignore_expiration:
674            return lambda value: value
675        else:
676            if expiration_time is None:
677                expiration_time = self.expiration_time
678
679            current_time = time.time()
680
681            def value_fn(value):
682                if value is NO_VALUE:
683                    return value
684                elif expiration_time is not None and \
685                        current_time - value.metadata["ct"] > expiration_time:
686                    return NO_VALUE
687                elif self.region_invalidator.is_invalidated(
688                        value.metadata["ct"]):
689                    return NO_VALUE
690                else:
691                    return value
692
693            return value_fn
694
695    def get_multi(self, keys, expiration_time=None, ignore_expiration=False):
696        """Return multiple values from the cache, based on the given keys.
697
698        Returns values as a list matching the keys given.
699
700        E.g.::
701
702            values = region.get_multi(["one", "two", "three"])
703
704        To convert values to a dictionary, use ``zip()``::
705
706            keys = ["one", "two", "three"]
707            values = region.get_multi(keys)
708            dictionary = dict(zip(keys, values))
709
710        Keys which aren't present in the list are returned as
711        the ``NO_VALUE`` token.  ``NO_VALUE`` evaluates to False,
712        but is separate from
713        ``None`` to distinguish between a cached value of ``None``.
714
715        By default, the configured expiration time of the
716        :class:`.CacheRegion`, or alternatively the expiration
717        time supplied by the ``expiration_time`` argument,
718        is tested against the creation time of the retrieved
719        value versus the current time (as reported by ``time.time()``).
720        If stale, the cached value is ignored and the ``NO_VALUE``
721        token is returned.  Passing the flag ``ignore_expiration=True``
722        bypasses the expiration time check.
723
724        .. versionadded:: 0.5.0
725
726        """
727        if not keys:
728            return []
729
730        if self.key_mangler:
731            keys = list(map(lambda key: self.key_mangler(key), keys))
732
733        backend_values = self.backend.get_multi(keys)
734
735        _unexpired_value_fn = self._unexpired_value_fn(
736            expiration_time, ignore_expiration)
737        return [
738            value.payload if value is not NO_VALUE else value
739            for value in
740            (
741                _unexpired_value_fn(value) for value in
742                backend_values
743            )
744        ]
745
746    def get_or_create(
747            self, key, creator, expiration_time=None, should_cache_fn=None,
748            creator_args=None):
749        """Return a cached value based on the given key.
750
751        If the value does not exist or is considered to be expired
752        based on its creation time, the given
753        creation function may or may not be used to recreate the value
754        and persist the newly generated value in the cache.
755
756        Whether or not the function is used depends on if the
757        *dogpile lock* can be acquired or not.  If it can't, it means
758        a different thread or process is already running a creation
759        function for this key against the cache.  When the dogpile
760        lock cannot be acquired, the method will block if no
761        previous value is available, until the lock is released and
762        a new value available.  If a previous value
763        is available, that value is returned immediately without blocking.
764
765        If the :meth:`.invalidate` method has been called, and
766        the retrieved value's timestamp is older than the invalidation
767        timestamp, the value is unconditionally prevented from
768        being returned.  The method will attempt to acquire the dogpile
769        lock to generate a new value, or will wait
770        until the lock is released to return the new value.
771
772        .. versionchanged:: 0.3.0
773          The value is unconditionally regenerated if the creation
774          time is older than the last call to :meth:`.invalidate`.
775
776        :param key: Key to be retrieved. While it's typical for a key to be a
777         string, it is ultimately passed directly down to the cache backend,
778         before being optionally processed by the key_mangler function, so can
779         be of any type recognized by the backend or by the key_mangler
780         function, if present.
781
782        :param creator: function which creates a new value.
783
784        :param creator_args: optional tuple of (args, kwargs) that will be
785         passed to the creator function if present.
786
787         .. versionadded:: 0.7.0
788
789        :param expiration_time: optional expiration time which will overide
790         the expiration time already configured on this :class:`.CacheRegion`
791         if not None.   To set no expiration, use the value -1.
792
793        :param should_cache_fn: optional callable function which will receive
794         the value returned by the "creator", and will then return True or
795         False, indicating if the value should actually be cached or not.  If
796         it returns False, the value is still returned, but isn't cached.
797         E.g.::
798
799            def dont_cache_none(value):
800                return value is not None
801
802            value = region.get_or_create("some key",
803                                create_value,
804                                should_cache_fn=dont_cache_none)
805
806         Above, the function returns the value of create_value() if
807         the cache is invalid, however if the return value is None,
808         it won't be cached.
809
810         .. versionadded:: 0.4.3
811
812        .. seealso::
813
814            :meth:`.CacheRegion.cache_on_arguments` - applies
815            :meth:`.get_or_create` to any function using a decorator.
816
817            :meth:`.CacheRegion.get_or_create_multi` - multiple key/value
818             version
819
820        """
821        orig_key = key
822        if self.key_mangler:
823            key = self.key_mangler(key)
824
825        def get_value():
826            value = self.backend.get(key)
827            if (value is NO_VALUE or value.metadata['v'] != value_version or
828                    self.region_invalidator.is_hard_invalidated(
829                        value.metadata["ct"])):
830                raise NeedRegenerationException()
831            ct = value.metadata["ct"]
832            if self.region_invalidator.is_soft_invalidated(ct):
833                ct = time.time() - expiration_time - .0001
834
835            return value.payload, ct
836
837        def gen_value():
838            if creator_args:
839                created_value = creator(*creator_args[0], **creator_args[1])
840            else:
841                created_value = creator()
842            value = self._value(created_value)
843
844            if not should_cache_fn or \
845                    should_cache_fn(created_value):
846                self.backend.set(key, value)
847
848            return value.payload, value.metadata["ct"]
849
850        if expiration_time is None:
851            expiration_time = self.expiration_time
852
853        if (expiration_time is None and
854                self.region_invalidator.was_soft_invalidated()):
855            raise exception.DogpileCacheException(
856                "Non-None expiration time required "
857                "for soft invalidation")
858
859        if expiration_time == -1:
860            expiration_time = None
861
862        if self.async_creation_runner:
863            def async_creator(mutex):
864                if creator_args:
865                    @wraps(creator)
866                    def go():
867                        return creator(*creator_args[0], **creator_args[1])
868                else:
869                    go = creator
870                return self.async_creation_runner(self, orig_key, go, mutex)
871        else:
872            async_creator = None
873
874        with Lock(
875                self._mutex(key),
876                gen_value,
877                get_value,
878                expiration_time,
879                async_creator) as value:
880            return value
881
882    def get_or_create_multi(
883            self, keys, creator, expiration_time=None, should_cache_fn=None):
884        """Return a sequence of cached values based on a sequence of keys.
885
886        The behavior for generation of values based on keys corresponds
887        to that of :meth:`.Region.get_or_create`, with the exception that
888        the ``creator()`` function may be asked to generate any subset of
889        the given keys.   The list of keys to be generated is passed to
890        ``creator()``, and ``creator()`` should return the generated values
891        as a sequence corresponding to the order of the keys.
892
893        The method uses the same approach as :meth:`.Region.get_multi`
894        and :meth:`.Region.set_multi` to get and set values from the
895        backend.
896
897        If you are using a :class:`.CacheBackend` or :class:`.ProxyBackend`
898        that modifies values, take note this function invokes
899        ``.set_multi()`` for newly generated values using the same values it
900        returns to the calling function. A correct implementation of
901        ``.set_multi()`` will not modify values in-place on the submitted
902        ``mapping`` dict.
903
904        :param keys: Sequence of keys to be retrieved.
905
906        :param creator: function which accepts a sequence of keys and
907         returns a sequence of new values.
908
909        :param expiration_time: optional expiration time which will overide
910         the expiration time already configured on this :class:`.CacheRegion`
911         if not None.   To set no expiration, use the value -1.
912
913        :param should_cache_fn: optional callable function which will receive
914         each value returned by the "creator", and will then return True or
915         False, indicating if the value should actually be cached or not.  If
916         it returns False, the value is still returned, but isn't cached.
917
918        .. versionadded:: 0.5.0
919
920        .. seealso::
921
922
923            :meth:`.CacheRegion.cache_multi_on_arguments`
924
925            :meth:`.CacheRegion.get_or_create`
926
927        """
928
929        def get_value(key):
930            value = values.get(key, NO_VALUE)
931
932            if (value is NO_VALUE or value.metadata['v'] != value_version or
933                    self.region_invalidator.is_hard_invalidated(
934                        value.metadata['ct'])):
935                # dogpile.core understands a 0 here as
936                # "the value is not available", e.g.
937                # _has_value() will return False.
938                return value.payload, 0
939            else:
940                ct = value.metadata["ct"]
941                if self.region_invalidator.is_soft_invalidated(ct):
942                    ct = time.time() - expiration_time - .0001
943
944                return value.payload, ct
945
946        def gen_value():
947            raise NotImplementedError()
948
949        def async_creator(key, mutex):
950            mutexes[key] = mutex
951
952        if expiration_time is None:
953            expiration_time = self.expiration_time
954
955        if (expiration_time is None and
956                self.region_invalidator.was_soft_invalidated()):
957            raise exception.DogpileCacheException(
958                "Non-None expiration time required "
959                "for soft invalidation")
960
961        if expiration_time == -1:
962            expiration_time = None
963
964        mutexes = {}
965
966        sorted_unique_keys = sorted(set(keys))
967
968        if self.key_mangler:
969            mangled_keys = [self.key_mangler(k) for k in sorted_unique_keys]
970        else:
971            mangled_keys = sorted_unique_keys
972
973        orig_to_mangled = dict(zip(sorted_unique_keys, mangled_keys))
974
975        values = dict(zip(mangled_keys, self.backend.get_multi(mangled_keys)))
976
977        for orig_key, mangled_key in orig_to_mangled.items():
978            with Lock(
979                    self._mutex(mangled_key),
980                    gen_value,
981                    lambda: get_value(mangled_key),
982                    expiration_time,
983                    async_creator=lambda mutex: async_creator(orig_key, mutex)
984            ):
985                pass
986        try:
987            if mutexes:
988                # sort the keys, the idea is to prevent deadlocks.
989                # though haven't been able to simulate one anyway.
990                keys_to_get = sorted(mutexes)
991                new_values = creator(*keys_to_get)
992
993                values_w_created = dict(
994                    (orig_to_mangled[k], self._value(v))
995                    for k, v in zip(keys_to_get, new_values)
996                )
997
998                if not should_cache_fn:
999                    self.backend.set_multi(values_w_created)
1000                else:
1001                    values_to_cache = dict(
1002                        (k, v)
1003                        for k, v in values_w_created.items()
1004                        if should_cache_fn(v[0])
1005                    )
1006
1007                    if values_to_cache:
1008                        self.backend.set_multi(values_to_cache)
1009
1010                values.update(values_w_created)
1011            return [values[orig_to_mangled[k]].payload for k in keys]
1012        finally:
1013            for mutex in mutexes.values():
1014                mutex.release()
1015
1016    def _value(self, value):
1017        """Return a :class:`.CachedValue` given a value."""
1018        return CachedValue(
1019            value,
1020            {
1021                "ct": time.time(),
1022                "v": value_version
1023            })
1024
1025    def set(self, key, value):
1026        """Place a new value in the cache under the given key."""
1027
1028        if self.key_mangler:
1029            key = self.key_mangler(key)
1030        self.backend.set(key, self._value(value))
1031
1032    def set_multi(self, mapping):
1033        """Place new values in the cache under the given keys.
1034
1035        .. versionadded:: 0.5.0
1036
1037        """
1038        if not mapping:
1039            return
1040
1041        if self.key_mangler:
1042            mapping = dict((
1043                self.key_mangler(k), self._value(v))
1044                for k, v in mapping.items())
1045        else:
1046            mapping = dict((k, self._value(v)) for k, v in mapping.items())
1047        self.backend.set_multi(mapping)
1048
1049    def delete(self, key):
1050        """Remove a value from the cache.
1051
1052        This operation is idempotent (can be called multiple times, or on a
1053        non-existent key, safely)
1054        """
1055
1056        if self.key_mangler:
1057            key = self.key_mangler(key)
1058
1059        self.backend.delete(key)
1060
1061    def delete_multi(self, keys):
1062        """Remove multiple values from the cache.
1063
1064        This operation is idempotent (can be called multiple times, or on a
1065        non-existent key, safely)
1066
1067        .. versionadded:: 0.5.0
1068
1069        """
1070
1071        if self.key_mangler:
1072            keys = list(map(lambda key: self.key_mangler(key), keys))
1073
1074        self.backend.delete_multi(keys)
1075
1076    def cache_on_arguments(
1077            self, namespace=None,
1078            expiration_time=None,
1079            should_cache_fn=None,
1080            to_str=compat.string_type,
1081            function_key_generator=None):
1082        """A function decorator that will cache the return
1083        value of the function using a key derived from the
1084        function itself and its arguments.
1085
1086        The decorator internally makes use of the
1087        :meth:`.CacheRegion.get_or_create` method to access the
1088        cache and conditionally call the function.  See that
1089        method for additional behavioral details.
1090
1091        E.g.::
1092
1093            @someregion.cache_on_arguments()
1094            def generate_something(x, y):
1095                return somedatabase.query(x, y)
1096
1097        The decorated function can then be called normally, where
1098        data will be pulled from the cache region unless a new
1099        value is needed::
1100
1101            result = generate_something(5, 6)
1102
1103        The function is also given an attribute ``invalidate()``, which
1104        provides for invalidation of the value.  Pass to ``invalidate()``
1105        the same arguments you'd pass to the function itself to represent
1106        a particular value::
1107
1108            generate_something.invalidate(5, 6)
1109
1110        Another attribute ``set()`` is added to provide extra caching
1111        possibilities relative to the function.   This is a convenience
1112        method for :meth:`.CacheRegion.set` which will store a given
1113        value directly without calling the decorated function.
1114        The value to be cached is passed as the first argument, and the
1115        arguments which would normally be passed to the function
1116        should follow::
1117
1118            generate_something.set(3, 5, 6)
1119
1120        The above example is equivalent to calling
1121        ``generate_something(5, 6)``, if the function were to produce
1122        the value ``3`` as the value to be cached.
1123
1124        .. versionadded:: 0.4.1 Added ``set()`` method to decorated function.
1125
1126        Similar to ``set()`` is ``refresh()``.   This attribute will
1127        invoke the decorated function and populate a new value into
1128        the cache with the new value, as well as returning that value::
1129
1130            newvalue = generate_something.refresh(5, 6)
1131
1132        .. versionadded:: 0.5.0 Added ``refresh()`` method to decorated
1133           function.
1134
1135        ``original()`` on other hand will invoke the decorated function
1136        without any caching::
1137
1138            newvalue = generate_something.original(5, 6)
1139
1140        .. versionadded:: 0.6.0 Added ``original()`` method to decorated
1141           function.
1142
1143        Lastly, the ``get()`` method returns either the value cached
1144        for the given key, or the token ``NO_VALUE`` if no such key
1145        exists::
1146
1147            value = generate_something.get(5, 6)
1148
1149        .. versionadded:: 0.5.3 Added ``get()`` method to decorated
1150           function.
1151
1152        The default key generation will use the name
1153        of the function, the module name for the function,
1154        the arguments passed, as well as an optional "namespace"
1155        parameter in order to generate a cache key.
1156
1157        Given a function ``one`` inside the module
1158        ``myapp.tools``::
1159
1160            @region.cache_on_arguments(namespace="foo")
1161            def one(a, b):
1162                return a + b
1163
1164        Above, calling ``one(3, 4)`` will produce a
1165        cache key as follows::
1166
1167            myapp.tools:one|foo|3 4
1168
1169        The key generator will ignore an initial argument
1170        of ``self`` or ``cls``, making the decorator suitable
1171        (with caveats) for use with instance or class methods.
1172        Given the example::
1173
1174            class MyClass(object):
1175                @region.cache_on_arguments(namespace="foo")
1176                def one(self, a, b):
1177                    return a + b
1178
1179        The cache key above for ``MyClass().one(3, 4)`` will
1180        again produce the same cache key of ``myapp.tools:one|foo|3 4`` -
1181        the name ``self`` is skipped.
1182
1183        The ``namespace`` parameter is optional, and is used
1184        normally to disambiguate two functions of the same
1185        name within the same module, as can occur when decorating
1186        instance or class methods as below::
1187
1188            class MyClass(object):
1189                @region.cache_on_arguments(namespace='MC')
1190                def somemethod(self, x, y):
1191                    ""
1192
1193            class MyOtherClass(object):
1194                @region.cache_on_arguments(namespace='MOC')
1195                def somemethod(self, x, y):
1196                    ""
1197
1198        Above, the ``namespace`` parameter disambiguates
1199        between ``somemethod`` on ``MyClass`` and ``MyOtherClass``.
1200        Python class declaration mechanics otherwise prevent
1201        the decorator from having awareness of the ``MyClass``
1202        and ``MyOtherClass`` names, as the function is received
1203        by the decorator before it becomes an instance method.
1204
1205        The function key generation can be entirely replaced
1206        on a per-region basis using the ``function_key_generator``
1207        argument present on :func:`.make_region` and
1208        :class:`.CacheRegion`. If defaults to
1209        :func:`.function_key_generator`.
1210
1211        :param namespace: optional string argument which will be
1212         established as part of the cache key.   This may be needed
1213         to disambiguate functions of the same name within the same
1214         source file, such as those
1215         associated with classes - note that the decorator itself
1216         can't see the parent class on a function as the class is
1217         being declared.
1218
1219        :param expiration_time: if not None, will override the normal
1220         expiration time.
1221
1222         May be specified as a callable, taking no arguments, that
1223         returns a value to be used as the ``expiration_time``. This callable
1224         will be called whenever the decorated function itself is called, in
1225         caching or retrieving. Thus, this can be used to
1226         determine a *dynamic* expiration time for the cached function
1227         result.  Example use cases include "cache the result until the
1228         end of the day, week or time period" and "cache until a certain date
1229         or time passes".
1230
1231         .. versionchanged:: 0.5.0
1232            ``expiration_time`` may be passed as a callable to
1233            :meth:`.CacheRegion.cache_on_arguments`.
1234
1235        :param should_cache_fn: passed to :meth:`.CacheRegion.get_or_create`.
1236
1237         .. versionadded:: 0.4.3
1238
1239        :param to_str: callable, will be called on each function argument
1240         in order to convert to a string.  Defaults to ``str()``.  If the
1241         function accepts non-ascii unicode arguments on Python 2.x, the
1242         ``unicode()`` builtin can be substituted, but note this will
1243         produce unicode cache keys which may require key mangling before
1244         reaching the cache.
1245
1246         .. versionadded:: 0.5.0
1247
1248        :param function_key_generator: a function that will produce a
1249         "cache key". This function will supersede the one configured on the
1250         :class:`.CacheRegion` itself.
1251
1252         .. versionadded:: 0.5.5
1253
1254        .. seealso::
1255
1256            :meth:`.CacheRegion.cache_multi_on_arguments`
1257
1258            :meth:`.CacheRegion.get_or_create`
1259
1260        """
1261        expiration_time_is_callable = compat.callable(expiration_time)
1262
1263        if function_key_generator is None:
1264            function_key_generator = self.function_key_generator
1265
1266        def get_or_create_for_user_func(key_generator, user_func, *arg, **kw):
1267            key = key_generator(*arg, **kw)
1268
1269            timeout = expiration_time() if expiration_time_is_callable \
1270                else expiration_time
1271            return self.get_or_create(key, user_func, timeout,
1272                                      should_cache_fn, (arg, kw))
1273
1274        def cache_decorator(user_func):
1275            if to_str is compat.string_type:
1276                # backwards compatible
1277                key_generator = function_key_generator(namespace, user_func)
1278            else:
1279                key_generator = function_key_generator(
1280                    namespace, user_func,
1281                    to_str=to_str)
1282
1283            def refresh(*arg, **kw):
1284                """
1285                Like invalidate, but regenerates the value instead
1286                """
1287                key = key_generator(*arg, **kw)
1288                value = user_func(*arg, **kw)
1289                self.set(key, value)
1290                return value
1291
1292            def invalidate(*arg, **kw):
1293                key = key_generator(*arg, **kw)
1294                self.delete(key)
1295
1296            def set_(value, *arg, **kw):
1297                key = key_generator(*arg, **kw)
1298                self.set(key, value)
1299
1300            def get(*arg, **kw):
1301                key = key_generator(*arg, **kw)
1302                return self.get(key)
1303
1304            user_func.set = set_
1305            user_func.invalidate = invalidate
1306            user_func.get = get
1307            user_func.refresh = refresh
1308            user_func.original = user_func
1309
1310            # Use `decorate` to preserve the signature of :param:`user_func`.
1311
1312            return decorate(user_func, partial(
1313                get_or_create_for_user_func, key_generator))
1314
1315        return cache_decorator
1316
1317    def cache_multi_on_arguments(
1318            self, namespace=None, expiration_time=None,
1319            should_cache_fn=None,
1320            asdict=False, to_str=compat.string_type,
1321            function_multi_key_generator=None):
1322        """A function decorator that will cache multiple return
1323        values from the function using a sequence of keys derived from the
1324        function itself and the arguments passed to it.
1325
1326        This method is the "multiple key" analogue to the
1327        :meth:`.CacheRegion.cache_on_arguments` method.
1328
1329        Example::
1330
1331            @someregion.cache_multi_on_arguments()
1332            def generate_something(*keys):
1333                return [
1334                    somedatabase.query(key)
1335                    for key in keys
1336                ]
1337
1338        The decorated function can be called normally.  The decorator
1339        will produce a list of cache keys using a mechanism similar to
1340        that of :meth:`.CacheRegion.cache_on_arguments`, combining the
1341        name of the function with the optional namespace and with the
1342        string form of each key.  It will then consult the cache using
1343        the same mechanism as that of :meth:`.CacheRegion.get_multi`
1344        to retrieve all current values; the originally passed keys
1345        corresponding to those values which aren't generated or need
1346        regeneration will be assembled into a new argument list, and
1347        the decorated function is then called with that subset of
1348        arguments.
1349
1350        The returned result is a list::
1351
1352            result = generate_something("key1", "key2", "key3")
1353
1354        The decorator internally makes use of the
1355        :meth:`.CacheRegion.get_or_create_multi` method to access the
1356        cache and conditionally call the function.  See that
1357        method for additional behavioral details.
1358
1359        Unlike the :meth:`.CacheRegion.cache_on_arguments` method,
1360        :meth:`.CacheRegion.cache_multi_on_arguments` works only with
1361        a single function signature, one which takes a simple list of
1362        keys as arguments.
1363
1364        Like :meth:`.CacheRegion.cache_on_arguments`, the decorated function
1365        is also provided with a ``set()`` method, which here accepts a
1366        mapping of keys and values to set in the cache::
1367
1368            generate_something.set({"k1": "value1",
1369                                    "k2": "value2", "k3": "value3"})
1370
1371        ...an ``invalidate()`` method, which has the effect of deleting
1372        the given sequence of keys using the same mechanism as that of
1373        :meth:`.CacheRegion.delete_multi`::
1374
1375            generate_something.invalidate("k1", "k2", "k3")
1376
1377        ...a ``refresh()`` method, which will call the creation
1378        function, cache the new values, and return them::
1379
1380            values = generate_something.refresh("k1", "k2", "k3")
1381
1382        ...and a ``get()`` method, which will return values
1383        based on the given arguments::
1384
1385            values = generate_something.get("k1", "k2", "k3")
1386
1387        .. versionadded:: 0.5.3 Added ``get()`` method to decorated
1388           function.
1389
1390        Parameters passed to :meth:`.CacheRegion.cache_multi_on_arguments`
1391        have the same meaning as those passed to
1392        :meth:`.CacheRegion.cache_on_arguments`.
1393
1394        :param namespace: optional string argument which will be
1395         established as part of each cache key.
1396
1397        :param expiration_time: if not None, will override the normal
1398         expiration time.  May be passed as an integer or a
1399         callable.
1400
1401        :param should_cache_fn: passed to
1402         :meth:`.CacheRegion.get_or_create_multi`. This function is given a
1403         value as returned by the creator, and only if it returns True will
1404         that value be placed in the cache.
1405
1406        :param asdict: if ``True``, the decorated function should return
1407         its result as a dictionary of keys->values, and the final result
1408         of calling the decorated function will also be a dictionary.
1409         If left at its default value of ``False``, the decorated function
1410         should return its result as a list of values, and the final
1411         result of calling the decorated function will also be a list.
1412
1413         When ``asdict==True`` if the dictionary returned by the decorated
1414         function is missing keys, those keys will not be cached.
1415
1416        :param to_str: callable, will be called on each function argument
1417         in order to convert to a string.  Defaults to ``str()``.  If the
1418         function accepts non-ascii unicode arguments on Python 2.x, the
1419         ``unicode()`` builtin can be substituted, but note this will
1420         produce unicode cache keys which may require key mangling before
1421         reaching the cache.
1422
1423        .. versionadded:: 0.5.0
1424
1425        :param function_multi_key_generator: a function that will produce a
1426         list of keys. This function will supersede the one configured on the
1427         :class:`.CacheRegion` itself.
1428
1429         .. versionadded:: 0.5.5
1430
1431        .. seealso::
1432
1433            :meth:`.CacheRegion.cache_on_arguments`
1434
1435            :meth:`.CacheRegion.get_or_create_multi`
1436
1437        """
1438        expiration_time_is_callable = compat.callable(expiration_time)
1439
1440        if function_multi_key_generator is None:
1441            function_multi_key_generator = self.function_multi_key_generator
1442
1443        def get_or_create_for_user_func(key_generator, user_func, *arg, **kw):
1444            cache_keys = arg
1445            keys = key_generator(*arg, **kw)
1446            key_lookup = dict(zip(keys, cache_keys))
1447
1448            @wraps(user_func)
1449            def creator(*keys_to_create):
1450                return user_func(*[key_lookup[k] for k in keys_to_create])
1451
1452            timeout = expiration_time() if expiration_time_is_callable \
1453                else expiration_time
1454
1455            if asdict:
1456                def dict_create(*keys):
1457                    d_values = creator(*keys)
1458                    return [
1459                        d_values.get(key_lookup[k], NO_VALUE)
1460                        for k in keys]
1461
1462                def wrap_cache_fn(value):
1463                    if value is NO_VALUE:
1464                        return False
1465                    elif not should_cache_fn:
1466                        return True
1467                    else:
1468                        return should_cache_fn(value)
1469
1470                result = self.get_or_create_multi(
1471                    keys, dict_create, timeout, wrap_cache_fn)
1472                result = dict(
1473                    (k, v) for k, v in zip(cache_keys, result)
1474                    if v is not NO_VALUE)
1475            else:
1476                result = self.get_or_create_multi(
1477                    keys, creator, timeout,
1478                    should_cache_fn)
1479
1480            return result
1481
1482        def cache_decorator(user_func):
1483            key_generator = function_multi_key_generator(
1484                namespace, user_func,
1485                to_str=to_str)
1486
1487            def invalidate(*arg):
1488                keys = key_generator(*arg)
1489                self.delete_multi(keys)
1490
1491            def set_(mapping):
1492                keys = list(mapping)
1493                gen_keys = key_generator(*keys)
1494                self.set_multi(dict(
1495                    (gen_key, mapping[key])
1496                    for gen_key, key
1497                    in zip(gen_keys, keys))
1498                )
1499
1500            def get(*arg):
1501                keys = key_generator(*arg)
1502                return self.get_multi(keys)
1503
1504            def refresh(*arg):
1505                keys = key_generator(*arg)
1506                values = user_func(*arg)
1507                if asdict:
1508                    self.set_multi(
1509                        dict(zip(keys, [values[a] for a in arg]))
1510                    )
1511                    return values
1512                else:
1513                    self.set_multi(
1514                        dict(zip(keys, values))
1515                    )
1516                    return values
1517
1518            user_func.set = set_
1519            user_func.invalidate = invalidate
1520            user_func.refresh = refresh
1521            user_func.get = get
1522
1523            # Use `decorate` to preserve the signature of :param:`user_func`.
1524
1525            return decorate(user_func, partial(get_or_create_for_user_func, key_generator))
1526
1527        return cache_decorator
1528
1529
1530
1531
1532def make_region(*arg, **kw):
1533    """Instantiate a new :class:`.CacheRegion`.
1534
1535    Currently, :func:`.make_region` is a passthrough
1536    to :class:`.CacheRegion`.  See that class for
1537    constructor arguments.
1538
1539    """
1540    return CacheRegion(*arg, **kw)
1541