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

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

Issue 1920403002: [content/test/gpu] Run pylint check of gpu tests in unittest instead of PRESUBMIT (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update path to LICENSE.txt of logilab/README.chromium Created 4 years, 7 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
OLDNEW
(Empty)
1 # copyright 2003-2013 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
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
30
31 # XXX rewrite so we can use the decorator syntax when keyarg has to be specified
32
33 class cached_decorator(object):
34 def __init__(self, cacheattr=None, keyarg=None):
35 self.cacheattr = cacheattr
36 self.keyarg = keyarg
37 def __call__(self, callableobj=None):
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.cache attr)
44 else:
45 cache = _MultiValuesCache(callableobj, self.cacheattr)
46 return cache.closure()
47
48 class _SingleValueCache(object):
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
57 def __call__(__me, self, *args):
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
65 def closure(self):
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
76 def clear(self, holder):
77 holder.__dict__.pop(self.cacheattr, None)
78
79
80 class _MultiValuesCache(_SingleValueCache):
81 def _get_cache(self, holder):
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
97 class _MultiValuesKeyArgCache(_MultiValuesCache):
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
121
122 class cachedproperty(object):
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
138 def __init__(self, wrapped):
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
147 def __doc__(self):
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
159
160 def get_cache_impl(obj, funcname):
161 cls = obj.__class__
162 member = getattr(cls, funcname)
163 if isinstance(member, property):
164 member = member.fget
165 return member.cache_obj
166
167 def clear_cache(obj, funcname):
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
177 def copy_cache(obj, funcname, cacheobj):
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
185
186 class wproperty(object):
187 """Simple descriptor expecting to take a modifier function as first argument
188 and looking for a _<function name> to retrieve the attribute.
189 """
190 def __init__(self, setfunc):
191 self.setfunc = setfunc
192 self.attrname = '_%s' % setfunc.__name__
193
194 def __set__(self, obj, value):
195 self.setfunc(obj, value)
196
197 def __get__(self, obj, cls):
198 assert obj is not None
199 return getattr(obj, self.attrname)
200
201
202 class classproperty(object):
203 """this is a simple property-like class but for class attributes.
204 """
205 def __init__(self, get):
206 self.get = get
207 def __get__(self, inst, cls):
208 return self.get(cls)
209
210
211 class iclassmethod(object):
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 '''
215 def __init__(self, func):
216 self.func = func
217 def __get__(self, instance, objtype):
218 if instance is None:
219 return method_type(self.func, objtype, objtype.__class__)
220 return method_type(self.func, instance, objtype)
221 def __set__(self, instance, value):
222 raise AttributeError("can't set attribute")
223
224
225 def timed(f):
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
251
252 def monkeypatch(klass, methodname=None):
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
OLDNEW
« no previous file with comments | « third_party/logilab/logilab/common/debugger.py ('k') | third_party/logilab/logilab/common/deprecation.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698