ab41afd42fba8dfa9bc25dfc1096d9135fd4790b
1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Photo is a part of Plinn - http://plinn.org #
4 # Copyright (C) 2008 BenoƮt PIN <benoit.pin@ensmp.fr> #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the Free Software #
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
19 #######################################################################################
27 from BTrees
.OOBTree
import OOBTree
29 def memoizedmethod(*indexes
, **options
) :
30 """ Used as decorator, this function stores result
31 of method m inside self._methodResultsCache or
32 self._v__methodResultsCache if volatile.
33 This decorator may be used inside a class which provides
34 a mapping object named _methodResultsCache and / or
35 _v__methodResultsCache.
39 1 - simple metdhod memoization
42 def methodWithNoParameters(self): pass
44 2 - indexed memoisation:
45 Parameters names are passed to memoizedmethod are
46 evaluated to construct an indexed cache.
47 Names must be a subset of the memoized method signature.
49 @memoizedmethod('arg1', 'arg2')
50 def methodWithParameters(self, arg1, arg2=None): pass
52 volatile
= options
.get('volatile', False)
53 cacheVarName
= '_methodResultsCache'
55 cacheVarName
= '_v_%s' % cacheVarName
57 def makeMemoizedMethod(m
) :
58 methodname
= m
.__name
__
61 def memoizedMethod(self
) :
62 if not hasattr(self
, cacheVarName
) :
63 setattr(self
, cacheVarName
, OOBTree())
64 cache
= getattr(self
, cacheVarName
)
65 if cache
.has_key(methodname
) :
66 return cache
[methodname
]
69 cache
[methodname
] = res
72 memoizedMethod
.__name
__ = methodname
73 memoizedMethod
.__doc
__ = m
.__doc
__
77 args
, varargs
, varkw
, defaults
= inspect
.getargspec(m
)
81 mandatoryargs
= args
[1:-len(defaults
)]
82 optargs
= args
[-len(defaults
):]
83 defaultValues
= dict(zip([name
for name
in args
[-len(defaults
):]], [val
for val
in defaults
]))
86 for index
in indexes
:
88 indexPositions
.append((index
, args
.index(index
)))
90 raise ValueError("%r argument is not in signature of %r" % (index
, methodname
))
93 indexPositions
.sort(lambda a
, b
: cmp(a
[1], b
[1]))
95 indexPositions
= tuple(indexPositions
)
98 def memoizedMethod(self
, *args
, **kw
) :
99 # test if m if called by ZPublished
100 if len(args
) < len(mandatoryargs
) and hasattr(self
, 'REQUEST') :
103 get
= lambda name
: self
.REQUEST
[name
]
104 for name
in mandatoryargs
:
106 args
.append(get(name
))
108 exactOrAtLeast
= defaults
and 'exactly' or 'at least'
109 raise TypeError('%(methodname)s takes %(exactOrAtLeast)s %(mandatoryArgsLength)d argument (%(givenArgsLength)s given)' % \
110 { 'methodname': methodname
111 , 'exactOrAtLeast': exactOrAtLeast
112 , 'mandatoryArgsLength': len(mandatoryargs
)
113 , 'givenArgsLength': len(args
)})
115 for name
in optargs
:
116 get
= self
.REQUEST
.get
117 args
.append(get(name
, defaultValues
[name
]))
121 if not hasattr(self
, cacheVarName
) :
122 setattr(self
, cacheVarName
, OOBTree())
123 cache
= getattr(self
, cacheVarName
)
124 if not cache
.has_key(methodname
) :
125 cache
[methodname
] = OOBTree()
127 cache
= cache
[methodname
]
128 index
= aggregateIndex(indexPositions
, args
)
130 if cache
.has_key(index
) :
133 res
= m(self
, *args
, **kw
)
137 memoizedMethod
.__name
__ = methodname
138 memoizedMethod
.__doc
__ = m
.__doc
__
139 return memoizedMethod
141 return makeMemoizedMethod
143 def aggregateIndex(indexPositions
, args
):
145 Returns the index to be used when looking for or inserting
147 view_name is a string.
148 local_keys is a mapping or None.
153 for name
, pos
in indexPositions
:
155 agg_index
.append((name
, str(val
)))
157 return tuple(agg_index
)