five.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.five
  4. ~~~~~~~~~~~
  5. Compatibility implementations of features
  6. only available in newer Python versions.
  7. """
  8. from __future__ import absolute_import
  9. import operator
  10. import sys
  11. from importlib import import_module
  12. from types import ModuleType
  13. # extends amqp.five
  14. from amqp.five import * # noqa
  15. from amqp.five import __all__ as _all_five
  16. try:
  17. from functools import reduce
  18. except ImportError:
  19. pass
  20. try: # pragma: no cover
  21. from inspect import formatargspec, getfullargspec
  22. except ImportError: # Py2
  23. from collections import namedtuple
  24. from inspect import formatargspec, getargspec as _getargspec # noqa
  25. FullArgSpec = namedtuple('FullArgSpec', (
  26. 'args', 'varargs', 'varkw', 'defaults',
  27. 'kwonlyargs', 'kwonlydefaults', 'annotations',
  28. ))
  29. def getfullargspec(fun, _fill=(None, ) * 3): # noqa
  30. s = _getargspec(fun)
  31. return FullArgSpec(*s + _fill)
  32. __all__ = [
  33. 'class_property', 'reclassmethod', 'create_module', 'recreate_module',
  34. ]
  35. __all__ += _all_five
  36. # ############# Module Generation ##########################
  37. # Utilities to dynamically
  38. # recreate modules, either for lazy loading or
  39. # to create old modules at runtime instead of
  40. # having them litter the source tree.
  41. # import fails in python 2.5. fallback to reduce in stdlib
  42. MODULE_DEPRECATED = """
  43. The module %s is deprecated and will be removed in a future version.
  44. """
  45. DEFAULT_ATTRS = {'__file__', '__path__', '__doc__', '__all__'}
  46. # im_func is no longer available in Py3.
  47. # instead the unbound method itself can be used.
  48. if sys.version_info[0] == 3: # pragma: no cover
  49. def fun_of_method(method):
  50. return method
  51. else:
  52. def fun_of_method(method): # noqa
  53. return method.im_func
  54. def getappattr(path):
  55. """Gets attribute from the current_app recursively,
  56. e.g. getappattr('amqp.get_task_consumer')``."""
  57. from celery import current_app
  58. return current_app._rgetattr(path)
  59. def _compat_periodic_task_decorator(*args, **kwargs):
  60. from celery.task import periodic_task
  61. return periodic_task(*args, **kwargs)
  62. COMPAT_MODULES = {
  63. 'celery': {
  64. 'execute': {
  65. 'send_task': 'send_task',
  66. },
  67. 'decorators': {
  68. 'task': 'task',
  69. 'periodic_task': _compat_periodic_task_decorator,
  70. },
  71. 'log': {
  72. 'get_default_logger': 'log.get_default_logger',
  73. 'setup_logger': 'log.setup_logger',
  74. 'setup_logging_subsystem': 'log.setup_logging_subsystem',
  75. 'redirect_stdouts_to_logger': 'log.redirect_stdouts_to_logger',
  76. },
  77. 'messaging': {
  78. 'TaskConsumer': 'amqp.TaskConsumer',
  79. 'establish_connection': 'connection',
  80. 'get_consumer_set': 'amqp.TaskConsumer',
  81. },
  82. 'registry': {
  83. 'tasks': 'tasks',
  84. },
  85. },
  86. 'celery.task': {
  87. 'control': {
  88. 'broadcast': 'control.broadcast',
  89. 'rate_limit': 'control.rate_limit',
  90. 'time_limit': 'control.time_limit',
  91. 'ping': 'control.ping',
  92. 'revoke': 'control.revoke',
  93. 'discard_all': 'control.purge',
  94. 'inspect': 'control.inspect',
  95. },
  96. 'schedules': 'celery.schedules',
  97. 'chords': 'celery.canvas',
  98. }
  99. }
  100. class class_property(object):
  101. def __init__(self, getter=None, setter=None):
  102. if getter is not None and not isinstance(getter, classmethod):
  103. getter = classmethod(getter)
  104. if setter is not None and not isinstance(setter, classmethod):
  105. setter = classmethod(setter)
  106. self.__get = getter
  107. self.__set = setter
  108. info = getter.__get__(object) # just need the info attrs.
  109. self.__doc__ = info.__doc__
  110. self.__name__ = info.__name__
  111. self.__module__ = info.__module__
  112. def __get__(self, obj, type=None):
  113. if obj and type is None:
  114. type = obj.__class__
  115. return self.__get.__get__(obj, type)()
  116. def __set__(self, obj, value):
  117. if obj is None:
  118. return self
  119. return self.__set.__get__(obj)(value)
  120. def setter(self, setter):
  121. return self.__class__(self.__get, setter)
  122. def reclassmethod(method):
  123. return classmethod(fun_of_method(method))
  124. class LazyModule(ModuleType):
  125. _compat_modules = ()
  126. _all_by_module = {}
  127. _direct = {}
  128. _object_origins = {}
  129. def __getattr__(self, name):
  130. if name in self._object_origins:
  131. module = __import__(self._object_origins[name], None, None, [name])
  132. for item in self._all_by_module[module.__name__]:
  133. setattr(self, item, getattr(module, item))
  134. return getattr(module, name)
  135. elif name in self._direct: # pragma: no cover
  136. module = __import__(self._direct[name], None, None, [name])
  137. setattr(self, name, module)
  138. return module
  139. return ModuleType.__getattribute__(self, name)
  140. def __dir__(self):
  141. return list(set(self.__all__) | DEFAULT_ATTRS)
  142. def __reduce__(self):
  143. return import_module, (self.__name__,)
  144. def create_module(name, attrs, cls_attrs=None, pkg=None,
  145. base=LazyModule, prepare_attr=None):
  146. fqdn = '.'.join([pkg.__name__, name]) if pkg else name
  147. cls_attrs = {} if cls_attrs is None else cls_attrs
  148. pkg, _, modname = name.rpartition('.')
  149. cls_attrs['__module__'] = pkg
  150. attrs = {
  151. attr_name: (prepare_attr(attr) if prepare_attr else attr)
  152. for attr_name, attr in items(attrs)
  153. }
  154. module = sys.modules[fqdn] = type(modname, (base,), cls_attrs)(name)
  155. module.__dict__.update(attrs)
  156. return module
  157. def recreate_module(name, compat_modules=(), by_module={}, direct={},
  158. base=LazyModule, **attrs):
  159. old_module = sys.modules[name]
  160. origins = get_origins(by_module)
  161. compat_modules = COMPAT_MODULES.get(name, ())
  162. cattrs = dict(
  163. _compat_modules=compat_modules,
  164. _all_by_module=by_module, _direct=direct,
  165. _object_origins=origins,
  166. __all__=tuple(set(reduce(
  167. operator.add,
  168. [tuple(v) for v in [compat_modules, origins, direct, attrs]],
  169. ))),
  170. )
  171. new_module = create_module(name, attrs, cls_attrs=cattrs, base=base)
  172. new_module.__dict__.update({
  173. mod: get_compat_module(new_module, mod) for mod in compat_modules
  174. })
  175. return old_module, new_module
  176. def get_compat_module(pkg, name):
  177. from .local import Proxy
  178. def prepare(attr):
  179. if isinstance(attr, string_t):
  180. return Proxy(getappattr, (attr,))
  181. return attr
  182. attrs = COMPAT_MODULES[pkg.__name__][name]
  183. if isinstance(attrs, string_t):
  184. fqdn = '.'.join([pkg.__name__, name])
  185. module = sys.modules[fqdn] = import_module(attrs)
  186. return module
  187. attrs['__all__'] = list(attrs)
  188. return create_module(name, dict(attrs), pkg=pkg, prepare_attr=prepare)
  189. def get_origins(defs):
  190. origins = {}
  191. for module, attrs in items(defs):
  192. origins.update({attr: module for attr in attrs})
  193. return origins