--- /dev/null
+# -*- coding: utf-8 -*-
+#######################################################################################
+# Photo is a part of Plinn - http://plinn.org #
+# Copyright (C) 2008 BenoƮt PIN <benoit.pin@ensmp.fr> #
+# #
+# 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)