Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(378)

Side by Side Diff: third_party/logilab/common/decorators.py

Issue 10447014: Add pylint to depot_tools. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Run pylint in subprocess rather than importing. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 #
4 # This file is part of logilab-common.
5 #
6 # logilab-common is free software: you can redistribute it and/or modify it unde r
7 # the terms of the GNU Lesser General Public License as published by the Free
8 # Software Foundation, either version 2.1 of the License, or (at your option) an y
9 # later version.
10 #
11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 # details.
15 #
16 # You should have received a copy of the GNU Lesser General Public License along
17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>.
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
25
26 # XXX rewrite so we can use the decorator syntax when keyarg has to be specified
27
28 def _is_generator_function(callableobj):
29 return callableobj.func_code.co_flags & 0x20
30
31 class cached_decorator(object):
32 def __init__(self, cacheattr=None, keyarg=None):
33 self.cacheattr = cacheattr
34 self.keyarg = keyarg
35 def __call__(self, callableobj=None):
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.cache attr)
42 else:
43 cache = _MultiValuesCache(callableobj, self.cacheattr)
44 return cache.closure()
45
46 class _SingleValueCache(object):
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
55 def __call__(__me, self, *args):
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
63 def closure(self):
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
75 def clear(self, holder):
76 holder.__dict__.pop(self.cacheattr, None)
77
78
79 class _MultiValuesCache(_SingleValueCache):
80 def _get_cache(self, holder):
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
96 class _MultiValuesKeyArgCache(_MultiValuesCache):
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
120
121 class cachedproperty(object):
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
137 def __init__(self, wrapped):
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
146 def __doc__(self):
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
158
159 def get_cache_impl(obj, funcname):
160 cls = obj.__class__
161 member = getattr(cls, funcname)
162 if isinstance(member, property):
163 member = member.fget
164 return member.cache_obj
165
166 def clear_cache(obj, funcname):
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
176 def copy_cache(obj, funcname, cacheobj):
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
184
185 class wproperty(object):
186 """Simple descriptor expecting to take a modifier function as first argument
187 and looking for a _<function name> to retrieve the attribute.
188 """
189 def __init__(self, setfunc):
190 self.setfunc = setfunc
191 self.attrname = '_%s' % setfunc.__name__
192
193 def __set__(self, obj, value):
194 self.setfunc(obj, value)
195
196 def __get__(self, obj, cls):
197 assert obj is not None
198 return getattr(obj, self.attrname)
199
200
201 class classproperty(object):
202 """this is a simple property-like class but for class attributes.
203 """
204 def __init__(self, get):
205 self.get = get
206 def __get__(self, inst, cls):
207 return self.get(cls)
208
209
210 class iclassmethod(object):
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 '''
214 def __init__(self, func):
215 self.func = func
216 def __get__(self, instance, objtype):
217 if instance is None:
218 return method_type(self.func, objtype, objtype.__class__)
219 return method_type(self.func, instance, objtype)
220 def __set__(self, instance, value):
221 raise AttributeError("can't set attribute")
222
223
224 def timed(f):
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
250
251 def monkeypatch(klass, methodname=None):
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 # likely a property
280 # this is quite borderline but usage already in the wild ...
281 setattr(klass, name, func)
282 return func
283 return decorator
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698