decorators.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. """This module implements decorators for implementing other decorators
  2. as well as some commonly used decorators.
  3. """
  4. import sys
  5. PY2 = sys.version_info[0] == 2
  6. if PY2:
  7. string_types = basestring,
  8. def exec_(_code_, _globs_=None, _locs_=None):
  9. """Execute code in a namespace."""
  10. if _globs_ is None:
  11. frame = sys._getframe(1)
  12. _globs_ = frame.f_globals
  13. if _locs_ is None:
  14. _locs_ = frame.f_locals
  15. del frame
  16. elif _locs_ is None:
  17. _locs_ = _globs_
  18. exec("""exec _code_ in _globs_, _locs_""")
  19. else:
  20. string_types = str,
  21. import builtins
  22. exec_ = getattr(builtins, "exec")
  23. del builtins
  24. from functools import partial
  25. from inspect import ismethod, isclass, formatargspec
  26. from collections import namedtuple
  27. from threading import Lock, RLock
  28. try:
  29. from inspect import signature
  30. except ImportError:
  31. pass
  32. from .wrappers import (FunctionWrapper, BoundFunctionWrapper, ObjectProxy,
  33. CallableObjectProxy)
  34. # Adapter wrapper for the wrapped function which will overlay certain
  35. # properties from the adapter function onto the wrapped function so that
  36. # functions such as inspect.getargspec(), inspect.getfullargspec(),
  37. # inspect.signature() and inspect.getsource() return the correct results
  38. # one would expect.
  39. class _AdapterFunctionCode(CallableObjectProxy):
  40. def __init__(self, wrapped_code, adapter_code):
  41. super(_AdapterFunctionCode, self).__init__(wrapped_code)
  42. self._self_adapter_code = adapter_code
  43. @property
  44. def co_argcount(self):
  45. return self._self_adapter_code.co_argcount
  46. @property
  47. def co_code(self):
  48. return self._self_adapter_code.co_code
  49. @property
  50. def co_flags(self):
  51. return self._self_adapter_code.co_flags
  52. @property
  53. def co_kwonlyargcount(self):
  54. return self._self_adapter_code.co_kwonlyargcount
  55. @property
  56. def co_varnames(self):
  57. return self._self_adapter_code.co_varnames
  58. class _AdapterFunctionSurrogate(CallableObjectProxy):
  59. def __init__(self, wrapped, adapter):
  60. super(_AdapterFunctionSurrogate, self).__init__(wrapped)
  61. self._self_adapter = adapter
  62. @property
  63. def __code__(self):
  64. return _AdapterFunctionCode(self.__wrapped__.__code__,
  65. self._self_adapter.__code__)
  66. @property
  67. def __defaults__(self):
  68. return self._self_adapter.__defaults__
  69. @property
  70. def __kwdefaults__(self):
  71. return self._self_adapter.__kwdefaults__
  72. @property
  73. def __signature__(self):
  74. if 'signature' not in globals():
  75. return self._self_adapter.__signature__
  76. else:
  77. return signature(self._self_adapter)
  78. if PY2:
  79. func_code = __code__
  80. func_defaults = __defaults__
  81. class _BoundAdapterWrapper(BoundFunctionWrapper):
  82. @property
  83. def __func__(self):
  84. return _AdapterFunctionSurrogate(self.__wrapped__.__func__,
  85. self._self_parent._self_adapter)
  86. @property
  87. def __signature__(self):
  88. if 'signature' not in globals():
  89. return self.__wrapped__.__signature__
  90. else:
  91. return signature(self._self_parent._self_adapter)
  92. if PY2:
  93. im_func = __func__
  94. class AdapterWrapper(FunctionWrapper):
  95. __bound_function_wrapper__ = _BoundAdapterWrapper
  96. def __init__(self, *args, **kwargs):
  97. adapter = kwargs.pop('adapter')
  98. super(AdapterWrapper, self).__init__(*args, **kwargs)
  99. self._self_surrogate = _AdapterFunctionSurrogate(
  100. self.__wrapped__, adapter)
  101. self._self_adapter = adapter
  102. @property
  103. def __code__(self):
  104. return self._self_surrogate.__code__
  105. @property
  106. def __defaults__(self):
  107. return self._self_surrogate.__defaults__
  108. @property
  109. def __kwdefaults__(self):
  110. return self._self_surrogate.__kwdefaults__
  111. if PY2:
  112. func_code = __code__
  113. func_defaults = __defaults__
  114. @property
  115. def __signature__(self):
  116. return self._self_surrogate.__signature__
  117. class AdapterFactory(object):
  118. def __call__(self, wrapped):
  119. raise NotImplementedError()
  120. class DelegatedAdapterFactory(AdapterFactory):
  121. def __init__(self, factory):
  122. super(DelegatedAdapterFactory, self).__init__()
  123. self.factory = factory
  124. def __call__(self, wrapped):
  125. return self.factory(wrapped)
  126. adapter_factory = DelegatedAdapterFactory
  127. # Decorator for creating other decorators. This decorator and the
  128. # wrappers which they use are designed to properly preserve any name
  129. # attributes, function signatures etc, in addition to the wrappers
  130. # themselves acting like a transparent proxy for the original wrapped
  131. # function so the wrapper is effectively indistinguishable from the
  132. # original wrapped function.
  133. def decorator(wrapper=None, enabled=None, adapter=None, proxy=FunctionWrapper):
  134. # The decorator should be supplied with a single positional argument
  135. # which is the wrapper function to be used to implement the
  136. # decorator. This may be preceded by a step whereby the keyword
  137. # arguments are supplied to customise the behaviour of the
  138. # decorator. The 'adapter' argument is used to optionally denote a
  139. # separate function which is notionally used by an adapter
  140. # decorator. In that case parts of the function '__code__' and
  141. # '__defaults__' attributes are used from the adapter function
  142. # rather than those of the wrapped function. This allows for the
  143. # argument specification from inspect.getfullargspec() and similar
  144. # functions to be overridden with a prototype for a different
  145. # function than what was wrapped. The 'enabled' argument provides a
  146. # way to enable/disable the use of the decorator. If the type of
  147. # 'enabled' is a boolean, then it is evaluated immediately and the
  148. # wrapper not even applied if it is False. If not a boolean, it will
  149. # be evaluated when the wrapper is called for an unbound wrapper,
  150. # and when binding occurs for a bound wrapper. When being evaluated,
  151. # if 'enabled' is callable it will be called to obtain the value to
  152. # be checked. If False, the wrapper will not be called and instead
  153. # the original wrapped function will be called directly instead.
  154. # The 'proxy' argument provides a way of passing a custom version of
  155. # the FunctionWrapper class used in decorating the function.
  156. if wrapper is not None:
  157. # Helper function for creating wrapper of the appropriate
  158. # time when we need it down below.
  159. def _build(wrapped, wrapper, enabled=None, adapter=None):
  160. if adapter:
  161. if isinstance(adapter, AdapterFactory):
  162. adapter = adapter(wrapped)
  163. if not callable(adapter):
  164. ns = {}
  165. # Check if the signature argument specification has
  166. # annotations. If it does then we need to remember
  167. # it but also drop it when attempting to manufacture
  168. # a standin adapter function. This is necessary else
  169. # it will try and look up any types referenced in
  170. # the annotations in the empty namespace we use,
  171. # which will fail.
  172. annotations = {}
  173. if not isinstance(adapter, string_types):
  174. if len(adapter) == 7:
  175. annotations = adapter[-1]
  176. adapter = adapter[:-1]
  177. adapter = formatargspec(*adapter)
  178. exec_('def adapter{}: pass'.format(adapter), ns, ns)
  179. adapter = ns['adapter']
  180. # Override the annotations for the manufactured
  181. # adapter function so they match the original
  182. # adapter signature argument specification.
  183. if annotations:
  184. adapter.__annotations__ = annotations
  185. return AdapterWrapper(wrapped=wrapped, wrapper=wrapper,
  186. enabled=enabled, adapter=adapter)
  187. return proxy(wrapped=wrapped, wrapper=wrapper, enabled=enabled)
  188. # The wrapper has been provided so return the final decorator.
  189. # The decorator is itself one of our function wrappers so we
  190. # can determine when it is applied to functions, instance methods
  191. # or class methods. This allows us to bind the instance or class
  192. # method so the appropriate self or cls attribute is supplied
  193. # when it is finally called.
  194. def _wrapper(wrapped, instance, args, kwargs):
  195. # We first check for the case where the decorator was applied
  196. # to a class type.
  197. #
  198. # @decorator
  199. # class mydecoratorclass(object):
  200. # def __init__(self, arg=None):
  201. # self.arg = arg
  202. # def __call__(self, wrapped, instance, args, kwargs):
  203. # return wrapped(*args, **kwargs)
  204. #
  205. # @mydecoratorclass(arg=1)
  206. # def function():
  207. # pass
  208. #
  209. # In this case an instance of the class is to be used as the
  210. # decorator wrapper function. If args was empty at this point,
  211. # then it means that there were optional keyword arguments
  212. # supplied to be used when creating an instance of the class
  213. # to be used as the wrapper function.
  214. if instance is None and isclass(wrapped) and not args:
  215. # We still need to be passed the target function to be
  216. # wrapped as yet, so we need to return a further function
  217. # to be able to capture it.
  218. def _capture(target_wrapped):
  219. # Now have the target function to be wrapped and need
  220. # to create an instance of the class which is to act
  221. # as the decorator wrapper function. Before we do that,
  222. # we need to first check that use of the decorator
  223. # hadn't been disabled by a simple boolean. If it was,
  224. # the target function to be wrapped is returned instead.
  225. _enabled = enabled
  226. if type(_enabled) is bool:
  227. if not _enabled:
  228. return target_wrapped
  229. _enabled = None
  230. # Now create an instance of the class which is to act
  231. # as the decorator wrapper function. Any arguments had
  232. # to be supplied as keyword only arguments so that is
  233. # all we pass when creating it.
  234. target_wrapper = wrapped(**kwargs)
  235. # Finally build the wrapper itself and return it.
  236. return _build(target_wrapped, target_wrapper,
  237. _enabled, adapter)
  238. return _capture
  239. # We should always have the target function to be wrapped at
  240. # this point as the first (and only) value in args.
  241. target_wrapped = args[0]
  242. # Need to now check that use of the decorator hadn't been
  243. # disabled by a simple boolean. If it was, then target
  244. # function to be wrapped is returned instead.
  245. _enabled = enabled
  246. if type(_enabled) is bool:
  247. if not _enabled:
  248. return target_wrapped
  249. _enabled = None
  250. # We now need to build the wrapper, but there are a couple of
  251. # different cases we need to consider.
  252. if instance is None:
  253. if isclass(wrapped):
  254. # In this case the decorator was applied to a class
  255. # type but optional keyword arguments were not supplied
  256. # for initialising an instance of the class to be used
  257. # as the decorator wrapper function.
  258. #
  259. # @decorator
  260. # class mydecoratorclass(object):
  261. # def __init__(self, arg=None):
  262. # self.arg = arg
  263. # def __call__(self, wrapped, instance,
  264. # args, kwargs):
  265. # return wrapped(*args, **kwargs)
  266. #
  267. # @mydecoratorclass
  268. # def function():
  269. # pass
  270. #
  271. # We still need to create an instance of the class to
  272. # be used as the decorator wrapper function, but no
  273. # arguments are pass.
  274. target_wrapper = wrapped()
  275. else:
  276. # In this case the decorator was applied to a normal
  277. # function, or possibly a static method of a class.
  278. #
  279. # @decorator
  280. # def mydecoratorfuntion(wrapped, instance,
  281. # args, kwargs):
  282. # return wrapped(*args, **kwargs)
  283. #
  284. # @mydecoratorfunction
  285. # def function():
  286. # pass
  287. #
  288. # That normal function becomes the decorator wrapper
  289. # function.
  290. target_wrapper = wrapper
  291. else:
  292. if isclass(instance):
  293. # In this case the decorator was applied to a class
  294. # method.
  295. #
  296. # class myclass(object):
  297. # @decorator
  298. # @classmethod
  299. # def decoratorclassmethod(cls, wrapped,
  300. # instance, args, kwargs):
  301. # return wrapped(*args, **kwargs)
  302. #
  303. # instance = myclass()
  304. #
  305. # @instance.decoratorclassmethod
  306. # def function():
  307. # pass
  308. #
  309. # This one is a bit strange because binding was actually
  310. # performed on the wrapper created by our decorator
  311. # factory. We need to apply that binding to the decorator
  312. # wrapper function which which the decorator factory
  313. # was applied to.
  314. target_wrapper = wrapper.__get__(None, instance)
  315. else:
  316. # In this case the decorator was applied to an instance
  317. # method.
  318. #
  319. # class myclass(object):
  320. # @decorator
  321. # def decoratorclassmethod(self, wrapped,
  322. # instance, args, kwargs):
  323. # return wrapped(*args, **kwargs)
  324. #
  325. # instance = myclass()
  326. #
  327. # @instance.decoratorclassmethod
  328. # def function():
  329. # pass
  330. #
  331. # This one is a bit strange because binding was actually
  332. # performed on the wrapper created by our decorator
  333. # factory. We need to apply that binding to the decorator
  334. # wrapper function which which the decorator factory
  335. # was applied to.
  336. target_wrapper = wrapper.__get__(instance, type(instance))
  337. # Finally build the wrapper itself and return it.
  338. return _build(target_wrapped, target_wrapper, _enabled, adapter)
  339. # We first return our magic function wrapper here so we can
  340. # determine in what context the decorator factory was used. In
  341. # other words, it is itself a universal decorator. The decorator
  342. # function is used as the adapter so that linters see a signature
  343. # corresponding to the decorator and not the wrapper it is being
  344. # applied to.
  345. return _build(wrapper, _wrapper, adapter=decorator)
  346. else:
  347. # The wrapper still has not been provided, so we are just
  348. # collecting the optional keyword arguments. Return the
  349. # decorator again wrapped in a partial using the collected
  350. # arguments.
  351. return partial(decorator, enabled=enabled, adapter=adapter,
  352. proxy=proxy)
  353. # Decorator for implementing thread synchronization. It can be used as a
  354. # decorator, in which case the synchronization context is determined by
  355. # what type of function is wrapped, or it can also be used as a context
  356. # manager, where the user needs to supply the correct synchronization
  357. # context. It is also possible to supply an object which appears to be a
  358. # synchronization primitive of some sort, by virtue of having release()
  359. # and acquire() methods. In that case that will be used directly as the
  360. # synchronization primitive without creating a separate lock against the
  361. # derived or supplied context.
  362. def synchronized(wrapped):
  363. # Determine if being passed an object which is a synchronization
  364. # primitive. We can't check by type for Lock, RLock, Semaphore etc,
  365. # as the means of creating them isn't the type. Therefore use the
  366. # existence of acquire() and release() methods. This is more
  367. # extensible anyway as it allows custom synchronization mechanisms.
  368. if hasattr(wrapped, 'acquire') and hasattr(wrapped, 'release'):
  369. # We remember what the original lock is and then return a new
  370. # decorator which accesses and locks it. When returning the new
  371. # decorator we wrap it with an object proxy so we can override
  372. # the context manager methods in case it is being used to wrap
  373. # synchronized statements with a 'with' statement.
  374. lock = wrapped
  375. @decorator
  376. def _synchronized(wrapped, instance, args, kwargs):
  377. # Execute the wrapped function while the original supplied
  378. # lock is held.
  379. with lock:
  380. return wrapped(*args, **kwargs)
  381. class _PartialDecorator(CallableObjectProxy):
  382. def __enter__(self):
  383. lock.acquire()
  384. return lock
  385. def __exit__(self, *args):
  386. lock.release()
  387. return _PartialDecorator(wrapped=_synchronized)
  388. # Following only apply when the lock is being created automatically
  389. # based on the context of what was supplied. In this case we supply
  390. # a final decorator, but need to use FunctionWrapper directly as we
  391. # want to derive from it to add context manager methods in case it is
  392. # being used to wrap synchronized statements with a 'with' statement.
  393. def _synchronized_lock(context):
  394. # Attempt to retrieve the lock for the specific context.
  395. lock = vars(context).get('_synchronized_lock', None)
  396. if lock is None:
  397. # There is no existing lock defined for the context we
  398. # are dealing with so we need to create one. This needs
  399. # to be done in a way to guarantee there is only one
  400. # created, even if multiple threads try and create it at
  401. # the same time. We can't always use the setdefault()
  402. # method on the __dict__ for the context. This is the
  403. # case where the context is a class, as __dict__ is
  404. # actually a dictproxy. What we therefore do is use a
  405. # meta lock on this wrapper itself, to control the
  406. # creation and assignment of the lock attribute against
  407. # the context.
  408. with synchronized._synchronized_meta_lock:
  409. # We need to check again for whether the lock we want
  410. # exists in case two threads were trying to create it
  411. # at the same time and were competing to create the
  412. # meta lock.
  413. lock = vars(context).get('_synchronized_lock', None)
  414. if lock is None:
  415. lock = RLock()
  416. setattr(context, '_synchronized_lock', lock)
  417. return lock
  418. def _synchronized_wrapper(wrapped, instance, args, kwargs):
  419. # Execute the wrapped function while the lock for the
  420. # desired context is held. If instance is None then the
  421. # wrapped function is used as the context.
  422. with _synchronized_lock(instance if instance is not None else wrapped):
  423. return wrapped(*args, **kwargs)
  424. class _FinalDecorator(FunctionWrapper):
  425. def __enter__(self):
  426. self._self_lock = _synchronized_lock(self.__wrapped__)
  427. self._self_lock.acquire()
  428. return self._self_lock
  429. def __exit__(self, *args):
  430. self._self_lock.release()
  431. return _FinalDecorator(wrapped=wrapped, wrapper=_synchronized_wrapper)
  432. synchronized._synchronized_meta_lock = Lock()