Zine

open source content publishing system


source: zine/cache.py @ 1279:088d2f519391

Revision 1279:088d2f519391, 5.5 KB checked in by Georg Brandl <georg@…>, 2 years ago (diff)

Update copyright notices.

Line 
1# -*- coding: utf-8 -*-
2"""
3    zine.cache
4    ~~~~~~~~~~
5
6    This module implements the Zine caching system.  This is essentially
7    a binding to memcached.
8
9
10    :copyright: (c) 2010 by the Zine Team, see AUTHORS for more details.
11    :license: BSD, see LICENSE for more details.
12"""
13import os
14
15from werkzeug.contrib.cache import NullCache, SimpleCache, FileSystemCache, \
16     MemcachedCache
17
18from zine.utils import local
19
20
21def get_cache(app):
22    """Return the cache for the application.  This is called during the
23    application setup by the application itself.  No need to call that
24    afterwards.
25    """
26    return systems[app.cfg['cache_system']](app)
27
28
29def get_cache_context(vary, eager_caching=False, request=None):
30    """Returns a tuple in the form ``(request, status)`` where request is a
31    request object and status a bool that is `True` if caching should be
32    performed.
33    """
34    request = request or local.request
35    return request, not (
36        # don't cache if we have the null cache.  in theory the null cache
37        # doesn't do anything anyways but if one tests for caching to
38        # disable some more expensive caculations in the function we can
39        # tell him to not perform anything if the cache won't hold the data
40        request.app.cache is NullCache or
41
42        # if this is an eager caching method and eager caching is disabled
43        # we don't do anything here
44        eager_caching and not request.app.cfg['enable_eager_caching'] or
45
46        # don't perform caching if "user" is in vary and the current
47        # active user is not an anonymous user
48        ('user' in vary and request.user.is_somebody) or
49
50        # also don't cache if `method` is in "vary" and the current
51        # request is not GET / HEAD
52        ('method' in vary and request.method not in ('GET', 'HEAD'))
53    )
54
55
56def result(cache_key, vary=(), eager_caching=False, timeout=None,
57           admix_arguments=True, skip_posargs=0):
58    """Cache the result of the function for a given timeout.  The `vary`
59    argument can be used to keep different caches or limit the cache.
60    Currently the following `vary` modifiers are available:
61
62    ``'user'``
63        cache only for anonymous users
64
65    ``'method'``
66        cache only if the current request is a GET or HEAD request.
67
68    if `admix_arguments` is set to `True` the arguments passed to the function
69    will be hashed and added to the cache key.  If you set `eager_caching` to
70    `True` this method won't do anything if eager caching is disabled.
71    """
72    def decorator(f):
73        def oncall(*args, **kwargs):
74            request, want_cache = get_cache_context(vary, eager_caching)
75
76            if want_cache:
77                key = cache_key
78                if admix_arguments:
79                    key += ':%d' % hash((args[skip_posargs:],
80                                        frozenset(kwargs.iteritems())))
81                result = request.app.cache.get(key)
82                if result is not None:
83                    return result
84            result = f(*args, **kwargs)
85            if want_cache:
86                request.app.cache.set(key, result, timeout)
87            return result
88
89        try:
90            oncall.__name__ = f.__name__
91            oncall.__doc__ = f.__doc__
92            oncall.__module__ = f.__module__
93        except AttributeError:
94            pass
95        return oncall
96    return decorator
97
98
99def response(vary=(), timeout=None, cache_key=None):
100    """Cache a complete view function for a number of seconds.  This is a
101    little bit different from `result` because it freezes the response
102    properly and sets etags.  The current request path is added to the cache
103    key to keep them cached properly.  If the response is not 200 no caching
104    is performed.
105
106    This method doesn't do anything if eager caching is disabled (by default).
107    """
108    from zine.application import Response
109    if not 'method' in vary:
110        vary = set(vary)
111        vary.add('method')
112    def decorator(f):
113        key = cache_key or 'view_func/%s.%s' % (f.__module__, f.__name__)
114        def oncall(request, *args, **kwargs):
115            use_cache = get_cache_context(vary, True, request)[1]
116            response = None
117            if use_cache:
118                cache_key = key + request.path.encode('utf-8')
119                response = request.app.cache.get(cache_key)
120
121            if response is None:
122                response = f(request, *args, **kwargs)
123
124            # make sure it's one of our request objects so that we
125            # have the `make_conditional` method on it.
126            Response.force_type(response)
127
128            if use_cache and response.status_code == 200:
129                response.freeze()
130                request.app.cache.set(key, response, timeout)
131                response.make_conditional(request)
132            return response
133        oncall.__name__ = f.__name__
134        oncall.__module__ = f.__module__
135        oncall.__doc__ = f.__doc__
136        return oncall
137    return decorator
138
139
140#: the cache system factories.
141systems = {
142    'null':         lambda app: NullCache(),
143    'simple':       lambda app: SimpleCache(app.cfg['cache_timeout']),
144    'memcached':    lambda app: MemcachedCache([x.strip() for x in
145                        app.cfg['memcached_servers']],
146                        app.cfg['cache_timeout']),
147    'filesystem':   lambda app: FileSystemCache(
148                        os.path.join(app.instance_folder,
149                                     app.cfg['filesystem_cache_path']), 500,
150                        app.cfg['cache_timeout'])
151}
Note: See TracBrowser for help on using the repository browser.