five.py 6.5 KB

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