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