X-Git-Url: https://scm.cri.ensmp.fr/git/Photo.git/blobdiff_plain/b0a7e10b4f32cf74864bb53268ca4d3080f23bc0..6c41809185e322ce2d30e98234f71144f78f06c0:/Products/Photo/cache.py diff --git a/Products/Photo/cache.py b/Products/Photo/cache.py new file mode 100755 index 0000000..ab41afd --- /dev/null +++ b/Products/Photo/cache.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +####################################################################################### +# Photo is a part of Plinn - http://plinn.org # +# Copyright (C) 2008 Benoît PIN # +# # +# This program is free software; you can redistribute it and/or # +# modify it under the terms of the GNU General Public License # +# as published by the Free Software Foundation; either version 2 # +# of the License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # +####################################################################################### +""" Memoization utils + + + +""" + +import inspect +from BTrees.OOBTree import OOBTree + +def memoizedmethod(*indexes, **options) : + """ Used as decorator, this function stores result + of method m inside self._methodResultsCache or + self._v__methodResultsCache if volatile. + This decorator may be used inside a class which provides + a mapping object named _methodResultsCache and / or + _v__methodResultsCache. + + example : + + 1 - simple metdhod memoization + + @memoizedmethod() + def methodWithNoParameters(self): pass + + 2 - indexed memoisation: + Parameters names are passed to memoizedmethod are + evaluated to construct an indexed cache. + Names must be a subset of the memoized method signature. + + @memoizedmethod('arg1', 'arg2') + def methodWithParameters(self, arg1, arg2=None): pass + """ + volatile = options.get('volatile', False) + cacheVarName = '_methodResultsCache' + if volatile==True : + cacheVarName = '_v_%s' % cacheVarName + + def makeMemoizedMethod(m) : + methodname = m.__name__ + + if not indexes : + def memoizedMethod(self) : + if not hasattr(self, cacheVarName) : + setattr(self, cacheVarName, OOBTree()) + cache = getattr(self, cacheVarName) + if cache.has_key(methodname) : + return cache[methodname] + else : + res = m(self) + cache[methodname] = res + return res + + memoizedMethod.__name__ = methodname + memoizedMethod.__doc__ = m.__doc__ + return memoizedMethod + + else : + args, varargs, varkw, defaults = inspect.getargspec(m) + args = list(args) + if defaults is None : + defaults = [] + mandatoryargs = args[1:-len(defaults)] + optargs = args[-len(defaults):] + defaultValues = dict(zip([name for name in args[-len(defaults):]], [val for val in defaults])) + + indexPositions = [] + for index in indexes : + try : + indexPositions.append((index, args.index(index))) + except ValueError : + raise ValueError("%r argument is not in signature of %r" % (index, methodname)) + + if indexPositions : + indexPositions.sort(lambda a, b : cmp(a[1], b[1])) + + indexPositions = tuple(indexPositions) + + + def memoizedMethod(self, *args, **kw) : + # test if m if called by ZPublished + if len(args) < len(mandatoryargs) and hasattr(self, 'REQUEST') : + assert not kw + args = list(args) + get = lambda name : self.REQUEST[name] + for name in mandatoryargs : + try : + args.append(get(name)) + except KeyError : + exactOrAtLeast = defaults and 'exactly' or 'at least' + raise TypeError('%(methodname)s takes %(exactOrAtLeast)s %(mandatoryArgsLength)d argument (%(givenArgsLength)s given)' % \ + { 'methodname': methodname + , 'exactOrAtLeast': exactOrAtLeast + , 'mandatoryArgsLength': len(mandatoryargs) + , 'givenArgsLength': len(args)}) + + for name in optargs : + get = self.REQUEST.get + args.append(get(name, defaultValues[name])) + + args = tuple(args) + + if not hasattr(self, cacheVarName) : + setattr(self, cacheVarName, OOBTree()) + cache = getattr(self, cacheVarName) + if not cache.has_key(methodname) : + cache[methodname] = OOBTree() + + cache = cache[methodname] + index = aggregateIndex(indexPositions, args) + + if cache.has_key(index) : + return cache[index] + else : + res = m(self, *args, **kw) + cache[index] = res + return res + + memoizedMethod.__name__ = methodname + memoizedMethod.__doc__ = m.__doc__ + return memoizedMethod + + return makeMemoizedMethod + +def aggregateIndex(indexPositions, args): + ''' + Returns the index to be used when looking for or inserting + a cache entry. + view_name is a string. + local_keys is a mapping or None. + ''' + + agg_index = [] + + for name, pos in indexPositions : + val = args[pos-1] + agg_index.append((name, str(val))) + + return tuple(agg_index)