1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """ A few useful function/method decorators. """
19
20 from __future__ import print_function
21
22 __docformat__ = "restructuredtext en"
23
24 import sys
25 import types
26 from time import clock, time
27 from inspect import isgeneratorfunction, getargspec
28
29 from logilab.common.compat import method_type
34 - def __init__(self, cacheattr=None, keyarg=None):
35 self.cacheattr = cacheattr
36 self.keyarg = keyarg
38 assert not isgeneratorfunction(callableobj), \
39 'cannot cache generator function: %s' % callableobj
40 if len(getargspec(callableobj).args) == 1 or self.keyarg == 0:
41 cache = _SingleValueCache(callableobj, self.cacheattr)
42 elif self.keyarg:
43 cache = _MultiValuesKeyArgCache(callableobj, self.keyarg, self.cacheattr)
44 else:
45 cache = _MultiValuesCache(callableobj, self.cacheattr)
46 return cache.closure()
47
49 - def __init__(self, callableobj, cacheattr=None):
50 self.callable = callableobj
51 if cacheattr is None:
52 self.cacheattr = '_%s_cache_' % callableobj.__name__
53 else:
54 assert cacheattr != callableobj.__name__
55 self.cacheattr = cacheattr
56
58 try:
59 return self.__dict__[__me.cacheattr]
60 except KeyError:
61 value = __me.callable(self, *args)
62 setattr(self, __me.cacheattr, value)
63 return value
64
66 def wrapped(*args, **kwargs):
67 return self.__call__(*args, **kwargs)
68 wrapped.cache_obj = self
69 try:
70 wrapped.__doc__ = self.callable.__doc__
71 wrapped.__name__ = self.callable.__name__
72 except:
73 pass
74 return wrapped
75
77 holder.__dict__.pop(self.cacheattr, None)
78
82 try:
83 _cache = holder.__dict__[self.cacheattr]
84 except KeyError:
85 _cache = {}
86 setattr(holder, self.cacheattr, _cache)
87 return _cache
88
89 - def __call__(__me, self, *args, **kwargs):
90 _cache = __me._get_cache(self)
91 try:
92 return _cache[args]
93 except KeyError:
94 _cache[args] = __me.callable(self, *args)
95 return _cache[args]
96
98 - def __init__(self, callableobj, keyarg, cacheattr=None):
99 super(_MultiValuesKeyArgCache, self).__init__(callableobj, cacheattr)
100 self.keyarg = keyarg
101
102 - def __call__(__me, self, *args, **kwargs):
103 _cache = __me._get_cache(self)
104 key = args[__me.keyarg-1]
105 try:
106 return _cache[key]
107 except KeyError:
108 _cache[key] = __me.callable(self, *args, **kwargs)
109 return _cache[key]
110
111
112 -def cached(callableobj=None, keyarg=None, **kwargs):
113 """Simple decorator to cache result of method call."""
114 kwargs['keyarg'] = keyarg
115 decorator = cached_decorator(**kwargs)
116 if callableobj is None:
117 return decorator
118 else:
119 return decorator(callableobj)
120
123 """ Provides a cached property equivalent to the stacking of
124 @cached and @property, but more efficient.
125
126 After first usage, the <property_name> becomes part of the object's
127 __dict__. Doing:
128
129 del obj.<property_name> empties the cache.
130
131 Idea taken from the pyramid_ framework and the mercurial_ project.
132
133 .. _pyramid: http://pypi.python.org/pypi/pyramid
134 .. _mercurial: http://pypi.python.org/pypi/Mercurial
135 """
136 __slots__ = ('wrapped',)
137
139 try:
140 wrapped.__name__
141 except AttributeError:
142 raise TypeError('%s must have a __name__ attribute' %
143 wrapped)
144 self.wrapped = wrapped
145
146 @property
148 doc = getattr(self.wrapped, '__doc__', None)
149 return ('<wrapped by the cachedproperty decorator>%s'
150 % ('\n%s' % doc if doc else ''))
151
152 - def __get__(self, inst, objtype=None):
153 if inst is None:
154 return self
155 val = self.wrapped(inst)
156 setattr(inst, self.wrapped.__name__, val)
157 return val
158
161 cls = obj.__class__
162 member = getattr(cls, funcname)
163 if isinstance(member, property):
164 member = member.fget
165 return member.cache_obj
166
168 """Clear a cache handled by the :func:`cached` decorator. If 'x' class has
169 @cached on its method `foo`, type
170
171 >>> clear_cache(x, 'foo')
172
173 to purge this method's cache on the instance.
174 """
175 get_cache_impl(obj, funcname).clear(obj)
176
178 """Copy cache for <funcname> from cacheobj to obj."""
179 cacheattr = get_cache_impl(obj, funcname).cacheattr
180 try:
181 setattr(obj, cacheattr, cacheobj.__dict__[cacheattr])
182 except KeyError:
183 pass
184
187 """Simple descriptor expecting to take a modifier function as first argument
188 and looking for a _<function name> to retrieve the attribute.
189 """
191 self.setfunc = setfunc
192 self.attrname = '_%s' % setfunc.__name__
193
195 self.setfunc(obj, value)
196
198 assert obj is not None
199 return getattr(obj, self.attrname)
200
203 """this is a simple property-like class but for class attributes.
204 """
209
212 '''Descriptor for method which should be available as class method if called
213 on the class or instance method if called on an instance.
214 '''
217 - def __get__(self, instance, objtype):
221 - def __set__(self, instance, value):
222 raise AttributeError("can't set attribute")
223
226 def wrap(*args, **kwargs):
227 t = time()
228 c = clock()
229 res = f(*args, **kwargs)
230 print('%s clock: %.9f / time: %.9f' % (f.__name__,
231 clock() - c, time() - t))
232 return res
233 return wrap
234
235
236 -def locked(acquire, release):
237 """Decorator taking two methods to acquire/release a lock as argument,
238 returning a decorator function which will call the inner method after
239 having called acquire(self) et will call release(self) afterwards.
240 """
241 def decorator(f):
242 def wrapper(self, *args, **kwargs):
243 acquire(self)
244 try:
245 return f(self, *args, **kwargs)
246 finally:
247 release(self)
248 return wrapper
249 return decorator
250
253 """Decorator extending class with the decorated callable. This is basically
254 a syntactic sugar vs class assignment.
255
256 >>> class A:
257 ... pass
258 >>> @monkeypatch(A)
259 ... def meth(self):
260 ... return 12
261 ...
262 >>> a = A()
263 >>> a.meth()
264 12
265 >>> @monkeypatch(A, 'foo')
266 ... def meth(self):
267 ... return 12
268 ...
269 >>> a.foo()
270 12
271 """
272 def decorator(func):
273 try:
274 name = methodname or func.__name__
275 except AttributeError:
276 raise AttributeError('%s has no __name__ attribute: '
277 'you should provide an explicit `methodname`'
278 % func)
279 setattr(klass, name, func)
280 return func
281 return decorator
282