five.py 6.7 KB

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