Package nltk :: Module decorators
[hide private]
[frames] | no frames]

Source Code for Module nltk.decorators

  1  """ 
  2  Decorator module by Michele Simionato <[email protected]> 
  3  Copyright Michele Simionato, distributed under the terms of the BSD License (see below). 
  4  http://www.phyast.pitt.edu/~micheles/python/documentation.html 
  5   
  6  Included in NLTK for its support of a nice memoization decorator. 
  7  """ 
  8  __docformat__ = 'restructuredtext en' 
  9   
 10  ## The basic trick is to generate the source code for the decorated function 
 11  ## with the right signature and to evaluate it. 
 12  ## Uncomment the statement 'print >> sys.stderr, func_src'  in _decorator 
 13  ## to understand what is going on. 
 14   
 15  __all__ = ["decorator", "new_wrapper", "getinfo"] 
 16   
 17  import inspect, sys 
 18   
 19  try: 
 20      set 
 21  except NameError: 
 22      from sets import Set as set 
 23   
24 -def getinfo(func):
25 """ 26 Returns an info dictionary containing: 27 - name (the name of the function : str) 28 - argnames (the names of the arguments : list) 29 - defaults (the values of the default arguments : tuple) 30 - signature (the signature : str) 31 - doc (the docstring : str) 32 - module (the module name : str) 33 - dict (the function __dict__ : str) 34 35 >>> def f(self, x=1, y=2, *args, **kw): pass 36 37 >>> info = getinfo(f) 38 39 >>> info["name"] 40 'f' 41 >>> info["argnames"] 42 ['self', 'x', 'y', 'args', 'kw'] 43 44 >>> info["defaults"] 45 (1, 2) 46 47 >>> info["signature"] 48 'self, x, y, *args, **kw' 49 """ 50 assert inspect.ismethod(func) or inspect.isfunction(func) 51 regargs, varargs, varkwargs, defaults = inspect.getargspec(func) 52 argnames = list(regargs) 53 if varargs: 54 argnames.append(varargs) 55 if varkwargs: 56 argnames.append(varkwargs) 57 signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults, 58 formatvalue=lambda value: "")[1:-1] 59 return dict(name=func.__name__, argnames=argnames, signature=signature, 60 defaults = func.func_defaults, doc=func.__doc__, 61 module=func.__module__, dict=func.__dict__, 62 globals=func.func_globals, closure=func.func_closure)
63 64 # akin to functools.update_wrapper
65 -def update_wrapper(wrapper, model, infodict=None):
66 infodict = infodict or getinfo(model) 67 try: 68 wrapper.__name__ = infodict['name'] 69 except: # Python version < 2.4 70 pass 71 wrapper.__doc__ = infodict['doc'] 72 wrapper.__module__ = infodict['module'] 73 wrapper.__dict__.update(infodict['dict']) 74 wrapper.func_defaults = infodict['defaults'] 75 wrapper.undecorated = model 76 return wrapper
77
78 -def new_wrapper(wrapper, model):
79 """ 80 An improvement over functools.update_wrapper. The wrapper is a generic 81 callable object. It works by generating a copy of the wrapper with the 82 right signature and by updating the copy, not the original. 83 Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module', 84 'dict', 'defaults'. 85 """ 86 if isinstance(model, dict): 87 infodict = model 88 else: # assume model is a function 89 infodict = getinfo(model) 90 assert not '_wrapper_' in infodict["argnames"], ( 91 '"_wrapper_" is a reserved argument name!') 92 src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict 93 funcopy = eval(src, dict(_wrapper_=wrapper)) 94 return update_wrapper(funcopy, model, infodict)
95 96 # helper used in decorator_factory
97 -def __call__(self, func):
98 return new_wrapper(lambda *a, **k : self.call(func, *a, **k), func)
99
100 -def decorator_factory(cls):
101 """ 102 Take a class with a ``.caller`` method and return a callable decorator 103 object. It works by adding a suitable __call__ method to the class; 104 it raises a TypeError if the class already has a nontrivial __call__ 105 method. 106 """ 107 attrs = set(dir(cls)) 108 if '__call__' in attrs: 109 raise TypeError('You cannot decorate a class with a nontrivial ' 110 '__call__ method') 111 if 'call' not in attrs: 112 raise TypeError('You cannot decorate a class without a ' 113 '.call method') 114 cls.__call__ = __call__ 115 return cls
116
117 -def decorator(caller):
118 """ 119 General purpose decorator factory: takes a caller function as 120 input and returns a decorator with the same attributes. 121 A caller function is any function like this:: 122 123 def caller(func, *args, **kw): 124 # do something 125 return func(*args, **kw) 126 127 Here is an example of usage: 128 129 >>> @decorator 130 ... def chatty(f, *args, **kw): 131 ... print "Calling %r" % f.__name__ 132 ... return f(*args, **kw) 133 134 >>> chatty.__name__ 135 'chatty' 136 137 >>> @chatty 138 ... def f(): pass 139 ... 140 >>> f() 141 Calling 'f' 142 143 decorator can also take in input a class with a .caller method; in this 144 case it converts the class into a factory of callable decorator objects. 145 See the documentation for an example. 146 """ 147 if inspect.isclass(caller): 148 return decorator_factory(caller) 149 def _decorator(func): # the real meat is here 150 infodict = getinfo(func) 151 argnames = infodict['argnames'] 152 assert not ('_call_' in argnames or '_func_' in argnames), ( 153 'You cannot use _call_ or _func_ as argument names!') 154 src = "lambda %(signature)s: _call_(_func_, %(signature)s)" % infodict 155 # import sys; print >> sys.stderr, src # for debugging purposes 156 dec_func = eval(src, dict(_func_=func, _call_=caller)) 157 return update_wrapper(dec_func, func, infodict)
158 return update_wrapper(_decorator, caller) 159
160 -def getattr_(obj, name, default_thunk):
161 "Similar to .setdefault in dictionaries." 162 try: 163 return getattr(obj, name) 164 except AttributeError: 165 default = default_thunk() 166 setattr(obj, name, default) 167 return default
168 169 @decorator
170 -def memoize(func, *args):
171 dic = getattr_(func, "memoize_dic", dict) 172 # memoize_dic is created at the first call 173 if args in dic: 174 return dic[args] 175 else: 176 result = func(*args) 177 dic[args] = result 178 return result
179 180 if __name__ == "__main__": 181 import doctest; doctest.testmod() 182 183 ########################## LEGALESE ############################### 184 185 ## Redistributions of source code must retain the above copyright 186 ## notice, this list of conditions and the following disclaimer. 187 ## Redistributions in bytecode form must reproduce the above copyright 188 ## notice, this list of conditions and the following disclaimer in 189 ## the documentation and/or other materials provided with the 190 ## distribution. 191 192 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 193 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 194 ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 195 ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 196 ## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 197 ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 198 ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 199 ## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 200 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 201 ## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 202 ## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 203 ## DAMAGE. 204