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

Source Code for Module nltk.decorators

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