decorator.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. # ######################### LICENSE ############################ #
  2. # Copyright (c) 2005-2016, Michele Simionato
  3. # All rights reserved.
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are
  6. # met:
  7. # Redistributions of source code must retain the above copyright
  8. # notice, this list of conditions and the following disclaimer.
  9. # Redistributions in bytecode form must reproduce the above copyright
  10. # notice, this list of conditions and the following disclaimer in
  11. # the documentation and/or other materials provided with the
  12. # distribution.
  13. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  14. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  15. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  16. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  17. # HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  18. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  19. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  20. # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  21. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  22. # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  23. # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  24. # DAMAGE.
  25. """
  26. Decorator module, see http://pypi.python.org/pypi/decorator
  27. for the documentation.
  28. """
  29. from __future__ import print_function
  30. import collections
  31. import inspect
  32. import itertools
  33. import operator
  34. import re
  35. import sys
  36. __version__ = '4.0.10'
  37. if sys.version_info >= (3,):
  38. from inspect import getfullargspec
  39. def get_init(cls):
  40. return cls.__init__
  41. else:
  42. class getfullargspec(object):
  43. "A quick and dirty replacement for getfullargspec for Python 2.X"
  44. def __init__(self, f):
  45. self.args, self.varargs, self.varkw, self.defaults = \
  46. inspect.getargspec(f)
  47. self.kwonlyargs = []
  48. self.kwonlydefaults = None
  49. def __iter__(self):
  50. yield self.args
  51. yield self.varargs
  52. yield self.varkw
  53. yield self.defaults
  54. getargspec = inspect.getargspec
  55. def get_init(cls):
  56. return cls.__init__.__func__
  57. # getargspec has been deprecated in Python 3.5
  58. ArgSpec = collections.namedtuple(
  59. 'ArgSpec', 'args varargs varkw defaults')
  60. def getargspec(f):
  61. """A replacement for inspect.getargspec"""
  62. spec = getfullargspec(f)
  63. return ArgSpec(spec.args, spec.varargs, spec.varkw, spec.defaults)
  64. DEF = re.compile(r'\s*def\s*([_\w][_\w\d]*)\s*\(')
  65. # basic functionality
  66. class FunctionMaker(object):
  67. """
  68. An object with the ability to create functions with a given signature.
  69. It has attributes name, doc, module, signature, defaults, dict and
  70. methods update and make.
  71. """
  72. # Atomic get-and-increment provided by the GIL
  73. _compile_count = itertools.count()
  74. def __init__(self, func=None, name=None, signature=None,
  75. defaults=None, doc=None, module=None, funcdict=None):
  76. self.shortsignature = signature
  77. if func:
  78. # func can be a class or a callable, but not an instance method
  79. self.name = func.__name__
  80. if self.name == '<lambda>': # small hack for lambda functions
  81. self.name = '_lambda_'
  82. self.doc = func.__doc__
  83. self.module = func.__module__
  84. if inspect.isfunction(func):
  85. argspec = getfullargspec(func)
  86. self.annotations = getattr(func, '__annotations__', {})
  87. for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs',
  88. 'kwonlydefaults'):
  89. setattr(self, a, getattr(argspec, a))
  90. for i, arg in enumerate(self.args):
  91. setattr(self, 'arg%d' % i, arg)
  92. if sys.version_info < (3,): # easy way
  93. self.shortsignature = self.signature = (
  94. inspect.formatargspec(
  95. formatvalue=lambda val: "", *argspec)[1:-1])
  96. else: # Python 3 way
  97. allargs = list(self.args)
  98. allshortargs = list(self.args)
  99. if self.varargs:
  100. allargs.append('*' + self.varargs)
  101. allshortargs.append('*' + self.varargs)
  102. elif self.kwonlyargs:
  103. allargs.append('*') # single star syntax
  104. for a in self.kwonlyargs:
  105. allargs.append('%s=None' % a)
  106. allshortargs.append('%s=%s' % (a, a))
  107. if self.varkw:
  108. allargs.append('**' + self.varkw)
  109. allshortargs.append('**' + self.varkw)
  110. self.signature = ', '.join(allargs)
  111. self.shortsignature = ', '.join(allshortargs)
  112. self.dict = func.__dict__.copy()
  113. # func=None happens when decorating a caller
  114. if name:
  115. self.name = name
  116. if signature is not None:
  117. self.signature = signature
  118. if defaults:
  119. self.defaults = defaults
  120. if doc:
  121. self.doc = doc
  122. if module:
  123. self.module = module
  124. if funcdict:
  125. self.dict = funcdict
  126. # check existence required attributes
  127. assert hasattr(self, 'name')
  128. if not hasattr(self, 'signature'):
  129. raise TypeError('You are decorating a non function: %s' % func)
  130. def update(self, func, **kw):
  131. "Update the signature of func with the data in self"
  132. func.__name__ = self.name
  133. func.__doc__ = getattr(self, 'doc', None)
  134. func.__dict__ = getattr(self, 'dict', {})
  135. func.__defaults__ = getattr(self, 'defaults', ())
  136. func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None)
  137. func.__annotations__ = getattr(self, 'annotations', None)
  138. try:
  139. frame = sys._getframe(3)
  140. except AttributeError: # for IronPython and similar implementations
  141. callermodule = '?'
  142. else:
  143. callermodule = frame.f_globals.get('__name__', '?')
  144. func.__module__ = getattr(self, 'module', callermodule)
  145. func.__dict__.update(kw)
  146. def make(self, src_templ, evaldict=None, addsource=False, **attrs):
  147. "Make a new function from a given template and update the signature"
  148. src = src_templ % vars(self) # expand name and signature
  149. evaldict = evaldict or {}
  150. mo = DEF.match(src)
  151. if mo is None:
  152. raise SyntaxError('not a valid function template\n%s' % src)
  153. name = mo.group(1) # extract the function name
  154. names = set([name] + [arg.strip(' *') for arg in
  155. self.shortsignature.split(',')])
  156. for n in names:
  157. if n in ('_func_', '_call_'):
  158. raise NameError('%s is overridden in\n%s' % (n, src))
  159. if not src.endswith('\n'): # add a newline for old Pythons
  160. src += '\n'
  161. # Ensure each generated function has a unique filename for profilers
  162. # (such as cProfile) that depend on the tuple of (<filename>,
  163. # <definition line>, <function name>) being unique.
  164. filename = '<decorator-gen-%d>' % (next(self._compile_count),)
  165. try:
  166. code = compile(src, filename, 'single')
  167. exec(code, evaldict)
  168. except:
  169. print('Error in generated code:', file=sys.stderr)
  170. print(src, file=sys.stderr)
  171. raise
  172. func = evaldict[name]
  173. if addsource:
  174. attrs['__source__'] = src
  175. self.update(func, **attrs)
  176. return func
  177. @classmethod
  178. def create(cls, obj, body, evaldict, defaults=None,
  179. doc=None, module=None, addsource=True, **attrs):
  180. """
  181. Create a function from the strings name, signature and body.
  182. evaldict is the evaluation dictionary. If addsource is true an
  183. attribute __source__ is added to the result. The attributes attrs
  184. are added, if any.
  185. """
  186. if isinstance(obj, str): # "name(signature)"
  187. name, rest = obj.strip().split('(', 1)
  188. signature = rest[:-1] # strip a right parens
  189. func = None
  190. else: # a function
  191. name = None
  192. signature = None
  193. func = obj
  194. self = cls(func, name, signature, defaults, doc, module)
  195. ibody = '\n'.join(' ' + line for line in body.splitlines())
  196. return self.make('def %(name)s(%(signature)s):\n' + ibody,
  197. evaldict, addsource, **attrs)
  198. def decorate(func, caller):
  199. """
  200. decorate(func, caller) decorates a function using a caller.
  201. """
  202. evaldict = dict(_call_=caller, _func_=func)
  203. fun = FunctionMaker.create(
  204. func, "return _call_(_func_, %(shortsignature)s)",
  205. evaldict, __wrapped__=func)
  206. if hasattr(func, '__qualname__'):
  207. fun.__qualname__ = func.__qualname__
  208. return fun
  209. def decorator(caller, _func=None):
  210. """decorator(caller) converts a caller function into a decorator"""
  211. if _func is not None: # return a decorated function
  212. # this is obsolete behavior; you should use decorate instead
  213. return decorate(_func, caller)
  214. # else return a decorator function
  215. if inspect.isclass(caller):
  216. name = caller.__name__.lower()
  217. doc = 'decorator(%s) converts functions/generators into ' \
  218. 'factories of %s objects' % (caller.__name__, caller.__name__)
  219. elif inspect.isfunction(caller):
  220. if caller.__name__ == '<lambda>':
  221. name = '_lambda_'
  222. else:
  223. name = caller.__name__
  224. doc = caller.__doc__
  225. else: # assume caller is an object with a __call__ method
  226. name = caller.__class__.__name__.lower()
  227. doc = caller.__call__.__doc__
  228. evaldict = dict(_call_=caller, _decorate_=decorate)
  229. return FunctionMaker.create(
  230. '%s(func)' % name, 'return _decorate_(func, _call_)',
  231. evaldict, doc=doc, module=caller.__module__,
  232. __wrapped__=caller)
  233. # ####################### contextmanager ####################### #
  234. try: # Python >= 3.2
  235. from contextlib import _GeneratorContextManager
  236. except ImportError: # Python >= 2.5
  237. from contextlib import GeneratorContextManager as _GeneratorContextManager
  238. class ContextManager(_GeneratorContextManager):
  239. def __call__(self, func):
  240. """Context manager decorator"""
  241. return FunctionMaker.create(
  242. func, "with _self_: return _func_(%(shortsignature)s)",
  243. dict(_self_=self, _func_=func), __wrapped__=func)
  244. init = getfullargspec(_GeneratorContextManager.__init__)
  245. n_args = len(init.args)
  246. if n_args == 2 and not init.varargs: # (self, genobj) Python 2.7
  247. def __init__(self, g, *a, **k):
  248. return _GeneratorContextManager.__init__(self, g(*a, **k))
  249. ContextManager.__init__ = __init__
  250. elif n_args == 2 and init.varargs: # (self, gen, *a, **k) Python 3.4
  251. pass
  252. elif n_args == 4: # (self, gen, args, kwds) Python 3.5
  253. def __init__(self, g, *a, **k):
  254. return _GeneratorContextManager.__init__(self, g, a, k)
  255. ContextManager.__init__ = __init__
  256. contextmanager = decorator(ContextManager)
  257. # ############################ dispatch_on ############################ #
  258. def append(a, vancestors):
  259. """
  260. Append ``a`` to the list of the virtual ancestors, unless it is already
  261. included.
  262. """
  263. add = True
  264. for j, va in enumerate(vancestors):
  265. if issubclass(va, a):
  266. add = False
  267. break
  268. if issubclass(a, va):
  269. vancestors[j] = a
  270. add = False
  271. if add:
  272. vancestors.append(a)
  273. # inspired from simplegeneric by P.J. Eby and functools.singledispatch
  274. def dispatch_on(*dispatch_args):
  275. """
  276. Factory of decorators turning a function into a generic function
  277. dispatching on the given arguments.
  278. """
  279. assert dispatch_args, 'No dispatch args passed'
  280. dispatch_str = '(%s,)' % ', '.join(dispatch_args)
  281. def check(arguments, wrong=operator.ne, msg=''):
  282. """Make sure one passes the expected number of arguments"""
  283. if wrong(len(arguments), len(dispatch_args)):
  284. raise TypeError('Expected %d arguments, got %d%s' %
  285. (len(dispatch_args), len(arguments), msg))
  286. def gen_func_dec(func):
  287. """Decorator turning a function into a generic function"""
  288. # first check the dispatch arguments
  289. argset = set(getfullargspec(func).args)
  290. if not set(dispatch_args) <= argset:
  291. raise NameError('Unknown dispatch arguments %s' % dispatch_str)
  292. typemap = {}
  293. def vancestors(*types):
  294. """
  295. Get a list of sets of virtual ancestors for the given types
  296. """
  297. check(types)
  298. ras = [[] for _ in range(len(dispatch_args))]
  299. for types_ in typemap:
  300. for t, type_, ra in zip(types, types_, ras):
  301. if issubclass(t, type_) and type_ not in t.__mro__:
  302. append(type_, ra)
  303. return [set(ra) for ra in ras]
  304. def ancestors(*types):
  305. """
  306. Get a list of virtual MROs, one for each type
  307. """
  308. check(types)
  309. lists = []
  310. for t, vas in zip(types, vancestors(*types)):
  311. n_vas = len(vas)
  312. if n_vas > 1:
  313. raise RuntimeError(
  314. 'Ambiguous dispatch for %s: %s' % (t, vas))
  315. elif n_vas == 1:
  316. va, = vas
  317. mro = type('t', (t, va), {}).__mro__[1:]
  318. else:
  319. mro = t.__mro__
  320. lists.append(mro[:-1]) # discard t and object
  321. return lists
  322. def register(*types):
  323. """
  324. Decorator to register an implementation for the given types
  325. """
  326. check(types)
  327. def dec(f):
  328. check(getfullargspec(f).args, operator.lt, ' in ' + f.__name__)
  329. typemap[types] = f
  330. return f
  331. return dec
  332. def dispatch_info(*types):
  333. """
  334. An utility to introspect the dispatch algorithm
  335. """
  336. check(types)
  337. lst = []
  338. for anc in itertools.product(*ancestors(*types)):
  339. lst.append(tuple(a.__name__ for a in anc))
  340. return lst
  341. def _dispatch(dispatch_args, *args, **kw):
  342. types = tuple(type(arg) for arg in dispatch_args)
  343. try: # fast path
  344. f = typemap[types]
  345. except KeyError:
  346. pass
  347. else:
  348. return f(*args, **kw)
  349. combinations = itertools.product(*ancestors(*types))
  350. next(combinations) # the first one has been already tried
  351. for types_ in combinations:
  352. f = typemap.get(types_)
  353. if f is not None:
  354. return f(*args, **kw)
  355. # else call the default implementation
  356. return func(*args, **kw)
  357. return FunctionMaker.create(
  358. func, 'return _f_(%s, %%(shortsignature)s)' % dispatch_str,
  359. dict(_f_=_dispatch), register=register, default=func,
  360. typemap=typemap, vancestors=vancestors, ancestors=ancestors,
  361. dispatch_info=dispatch_info, __wrapped__=func)
  362. gen_func_dec.__name__ = 'dispatch_on' + dispatch_str
  363. return gen_func_dec