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

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: Fix unittests. 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
« no previous file with comments | « third_party/logilab/common/debugger.py ('k') | third_party/logilab/common/deprecation.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « third_party/logilab/common/debugger.py ('k') | third_party/logilab/common/deprecation.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698