Package logilab :: Package common :: Module decorators
[frames] | no frames]

Source Code for Module logilab.common.decorators

  1  # copyright 2003-2010 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 under 
  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) any 
  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 types 
 22  from time import clock, time 
 23  import sys, re 
 24   
 25  # XXX rewrite so we can use the decorator syntax when keyarg has to be specified 
 26   
27 -def _is_generator_function(callableobj):
28 return callableobj.func_code.co_flags & 0x20
29
30 -def cached(callableobj, keyarg=None):
31 """Simple decorator to cache result of method call.""" 32 assert not _is_generator_function(callableobj), 'cannot cache generator function: %s' % callableobj 33 if callableobj.func_code.co_argcount == 1 or keyarg == 0: 34 35 def cache_wrapper1(self, *args): 36 cache = '_%s_cache_' % callableobj.__name__ 37 #print 'cache1?', cache 38 try: 39 return self.__dict__[cache] 40 except KeyError: 41 #print 'miss' 42 value = callableobj(self, *args) 43 setattr(self, cache, value) 44 return value
45 try: 46 cache_wrapper1.__doc__ = callableobj.__doc__ 47 cache_wrapper1.func_name = callableobj.func_name 48 except: 49 pass 50 return cache_wrapper1 51 52 elif keyarg: 53 54 def cache_wrapper2(self, *args, **kwargs): 55 cache = '_%s_cache_' % callableobj.__name__ 56 key = args[keyarg-1] 57 #print 'cache2?', cache, self, key 58 try: 59 _cache = self.__dict__[cache] 60 except KeyError: 61 #print 'init' 62 _cache = {} 63 setattr(self, cache, _cache) 64 try: 65 return _cache[key] 66 except KeyError: 67 #print 'miss', self, cache, key 68 _cache[key] = callableobj(self, *args, **kwargs) 69 return _cache[key] 70 try: 71 cache_wrapper2.__doc__ = callableobj.__doc__ 72 cache_wrapper2.func_name = callableobj.func_name 73 except: 74 pass 75 return cache_wrapper2 76 77 def cache_wrapper3(self, *args): 78 cache = '_%s_cache_' % callableobj.__name__ 79 #print 'cache3?', cache, self, args 80 try: 81 _cache = self.__dict__[cache] 82 except KeyError: 83 #print 'init' 84 _cache = {} 85 setattr(self, cache, _cache) 86 try: 87 return _cache[args] 88 except KeyError: 89 #print 'miss' 90 _cache[args] = callableobj(self, *args) 91 return _cache[args] 92 try: 93 cache_wrapper3.__doc__ = callableobj.__doc__ 94 cache_wrapper3.func_name = callableobj.func_name 95 except: 96 pass 97 return cache_wrapper3 98
99 -def clear_cache(obj, funcname):
100 """Function to clear a cache handled by the cached decorator.""" 101 try: 102 del obj.__dict__['_%s_cache_' % funcname] 103 except KeyError: 104 pass
105
106 -def copy_cache(obj, funcname, cacheobj):
107 """Copy cache for <funcname> from cacheobj to obj.""" 108 cache = '_%s_cache_' % funcname 109 try: 110 setattr(obj, cache, cacheobj.__dict__[cache]) 111 except KeyError: 112 pass
113
114 -class wproperty(object):
115 """Simple descriptor expecting to take a modifier function as first argument 116 and looking for a _<function name> to retrieve the attribute. 117 """
118 - def __init__(self, setfunc):
119 self.setfunc = setfunc 120 self.attrname = '_%s' % setfunc.__name__
121
122 - def __set__(self, obj, value):
123 self.setfunc(obj, value)
124
125 - def __get__(self, obj, cls):
126 assert obj is not None 127 return getattr(obj, self.attrname)
128 129
130 -class classproperty(object):
131 """this is a simple property-like class but for class attributes. 132 """
133 - def __init__(self, get):
134 self.get = get
135 - def __get__(self, inst, cls):
136 return self.get(cls)
137 138
139 -class iclassmethod(object):
140 '''Descriptor for method which should be available as class method if called 141 on the class or instance method if called on an instance. 142 '''
143 - def __init__(self, func):
144 self.func = func
145 - def __get__(self, instance, objtype):
146 if instance is None: 147 return types.MethodType(self.func, objtype, objtype.__class__) 148 return types.MethodType(self.func, instance, objtype)
149 - def __set__(self, instance, value):
150 raise AttributeError("can't set attribute")
151 152
153 -def timed(f):
154 def wrap(*args, **kwargs): 155 t = time() 156 c = clock() 157 res = f(*args, **kwargs) 158 print '%s clock: %.9f / time: %.9f' % (f.__name__, 159 clock() - c, time() - t) 160 return res
161 return wrap 162 163
164 -def locked(acquire, release):
165 """Decorator taking two methods to acquire/release a lock as argument, 166 returning a decorator function which will call the inner method after 167 having called acquire(self) et will call release(self) afterwards. 168 """ 169 def decorator(f): 170 def wrapper(self, *args, **kwargs): 171 acquire(self) 172 try: 173 return f(self, *args, **kwargs) 174 finally: 175 release(self)
176 return wrapper 177 return decorator 178 179
180 -def monkeypatch(klass, methodname=None):
181 """Decorator extending class with the decorated function 182 >>> class A: 183 ... pass 184 >>> @monkeypatch(A) 185 ... def meth(self): 186 ... return 12 187 ... 188 >>> a = A() 189 >>> a.meth() 190 12 191 >>> @monkeypatch(A, 'foo') 192 ... def meth(self): 193 ... return 12 194 ... 195 >>> a.foo() 196 12 197 """ 198 def decorator(func): 199 setattr(klass, methodname or func.__name__, func) 200 return func
201 return decorator 202