Back to index

plone3  3.1.7
request.py
Go to the documentation of this file.
00001 """
00002 Memoize decorator for methods.
00003 
00004 Stores values in an annotation of the request.
00005 """
00006 
00007 import inspect
00008 
00009 try:
00010     from zope.annotation.interfaces import IAnnotations
00011 except ImportError:
00012     from zope.app.annotation.interfaces import IAnnotations
00013 
00014 from plone.memoize import volatile
00015 
00016 _marker = object()
00017 class RequestMemo(object):
00018     
00019     key = 'plone.memoize_request'
00020 
00021     def __init__(self, arg=0):
00022         self.arg = arg
00023 
00024     def __call__(self, func):
00025         def memogetter(*args, **kwargs):
00026             request = None
00027             if isinstance(self.arg, int):
00028                 request = args[self.arg]
00029             else:
00030                 request = kwargs[self.arg]
00031 
00032             annotations = IAnnotations(request)
00033             cache = annotations.get(self.key, _marker)
00034         
00035             if cache is _marker:
00036                 cache = annotations[self.key] = dict()
00037         
00038             key = (func.__module__, func.__name__, 
00039                    args, frozenset(kwargs.items()))
00040             value = cache.get(key, _marker)
00041             if value is _marker:
00042                 value = cache[key] = func(*args, **kwargs)
00043             return value
00044         return memogetter
00045 
00046 def store_in_annotation_of(expr):
00047     def _store_in_annotation(fun, *args, **kwargs):
00048         # Use expr to find out the name of the request variable
00049         vars = {}
00050         for index, name in enumerate(inspect.getargspec(fun)[0]):
00051             if name in kwargs:
00052                 vars[name] = kwargs[name]
00053             else:
00054                 vars[name] = args[index]
00055         request = eval(expr, {}, vars)
00056         return IAnnotations(request)
00057     return _store_in_annotation
00058 
00059 def cache(get_key, get_request='request'):
00060     r"""
00061     This is a hypothetical function `increment` that'll store the
00062     cache value on `a.request`, where a is the only argument to the
00063     function:
00064     
00065       >>> def increment(a):
00066       ...     print 'Someone or something called me'
00067       ...     return a + 1
00068 
00069     Now we need to define this `a`.  For this, we'll inherit from
00070     `int` and add a `request` class variable.  Note that we also make
00071     our fake request `IAttributeAnnotatable`, because that's how the
00072     cache values are stored on the request:
00073 
00074       >>> from zope.publisher.browser import TestRequest
00075       >>> class A(int):
00076       ...     request = TestRequest()
00077       >>> from zope.interface import directlyProvides
00078       >>> try:
00079       ...     from zope.annotation.interfaces import IAttributeAnnotatable
00080       ... except ImportError:
00081       ...     from zope.app.annotation.interfaces import IAttributeAnnotatable
00082       >>> directlyProvides(A.request, IAttributeAnnotatable)
00083 
00084     In addition to this request, we'll also need to set up a cache key
00085     generator.  We'll use the integer value of the only argument for
00086     that:
00087 
00088       >>> get_key = lambda fun, a, *args: a
00089 
00090     Let's decorate our `increment` function now with the `cache`
00091     decorator.  We'll tell the decorator to use `args_hash` for
00092     generating the key. `get_request` will tell the decorator how to
00093     actually find the `request` in the variable scope of the function
00094     itself:
00095     
00096       >>> cached_increment = \
00097       ...     cache(get_key=get_key, get_request='a.request')(increment)
00098 
00099       >>> cached_increment(A(1))
00100       Someone or something called me
00101       2
00102       >>> cached_increment(A(1))
00103       2
00104       >>> IAnnotations(A.request)
00105       {'plone.memoize.request.increment:1': 2}
00106 
00107     If `request` is already part of the function's argument list, we
00108     don't need to specify any expression:
00109 
00110       >>> @cache(get_key=get_key)
00111       ... def increment_plus(a, request):
00112       ...     print 'Someone or something called me'
00113       ...     return a + 1
00114 
00115       >>> increment_plus(42, A.request)
00116       Someone or something called me
00117       43
00118       >>> increment_plus(42, A.request)
00119       43
00120       >>> IAnnotations(A.request)['plone.memoize.request.increment_plus:42']
00121       43
00122     """
00123 
00124     return volatile.cache(get_key,
00125                           get_cache=store_in_annotation_of(get_request))
00126 
00127 memoize_diy_request = RequestMemo
00128 
00129 __all__ = (memoize_diy_request, store_in_annotation_of, cache)