Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.py 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.app.utils
  4. ~~~~~~~~~~~~~~~~
  5. App utilities: Compat settings, bugreport tool, pickling apps.
  6. """
  7. from __future__ import absolute_import
  8. import os
  9. import platform as _platform
  10. import re
  11. from collections import Mapping
  12. from types import ModuleType
  13. from kombu.utils.url import maybe_sanitize_url
  14. from celery.datastructures import ConfigurationView
  15. from celery.five import items, string_t, values
  16. from celery.platforms import pyimplementation
  17. from celery.utils.text import pretty
  18. from celery.utils.imports import import_from_cwd, symbol_by_name, qualname
  19. from .defaults import find
  20. __all__ = ['Settings', 'appstr', 'bugreport',
  21. 'filter_hidden_settings', 'find_app']
  22. #: Format used to generate bugreport information.
  23. BUGREPORT_INFO = """
  24. software -> celery:{celery_v} kombu:{kombu_v} py:{py_v}
  25. billiard:{billiard_v} {driver_v}
  26. platform -> system:{system} arch:{arch} imp:{py_i}
  27. loader -> {loader}
  28. settings -> transport:{transport} results:{results}
  29. {human_settings}
  30. """
  31. HIDDEN_SETTINGS = re.compile(
  32. 'API|TOKEN|KEY|SECRET|PASS|PROFANITIES_LIST|SIGNATURE|DATABASE',
  33. re.IGNORECASE,
  34. )
  35. def appstr(app):
  36. """String used in __repr__ etc, to id app instances."""
  37. return '{0}:0x{1:x}'.format(app.main or '__main__', id(app))
  38. class Settings(ConfigurationView):
  39. """Celery settings object.
  40. .. seealso:
  41. :ref:`configuration` for a full list of configuration keys.
  42. """
  43. @property
  44. def CELERY_RESULT_BACKEND(self):
  45. return self.first('CELERY_RESULT_BACKEND', 'CELERY_BACKEND')
  46. @property
  47. def BROKER_TRANSPORT(self):
  48. return self.first('BROKER_TRANSPORT',
  49. 'BROKER_BACKEND', 'CARROT_BACKEND')
  50. @property
  51. def BROKER_BACKEND(self):
  52. """Deprecated compat alias to :attr:`BROKER_TRANSPORT`."""
  53. return self.BROKER_TRANSPORT
  54. @property
  55. def BROKER_URL(self):
  56. return (os.environ.get('CELERY_BROKER_URL') or
  57. self.first('BROKER_URL', 'BROKER_HOST'))
  58. @property
  59. def CELERY_TIMEZONE(self):
  60. # this way we also support django's time zone.
  61. return self.first('CELERY_TIMEZONE', 'TIME_ZONE')
  62. def without_defaults(self):
  63. """Return the current configuration, but without defaults."""
  64. # the last stash is the default settings, so just skip that
  65. return Settings({}, self._order[:-1])
  66. def value_set_for(self, key):
  67. return key in self.without_defaults()
  68. def find_option(self, name, namespace='celery'):
  69. """Search for option by name.
  70. Will return ``(namespace, key, type)`` tuple, e.g.::
  71. >>> from proj.celery import app
  72. >>> app.conf.find_option('disable_rate_limits')
  73. ('CELERY', 'DISABLE_RATE_LIMITS',
  74. <Option: type->bool default->False>))
  75. :param name: Name of option, cannot be partial.
  76. :keyword namespace: Preferred namespace (``CELERY`` by default).
  77. """
  78. return find(name, namespace)
  79. def find_value_for_key(self, name, namespace='celery'):
  80. """Shortcut to ``get_by_parts(*find_option(name)[:-1])``"""
  81. return self.get_by_parts(*self.find_option(name, namespace)[:-1])
  82. def get_by_parts(self, *parts):
  83. """Return the current value for setting specified as a path.
  84. Example::
  85. >>> from proj.celery import app
  86. >>> app.conf.get_by_parts('CELERY', 'DISABLE_RATE_LIMITS')
  87. False
  88. """
  89. return self['_'.join(part for part in parts if part)]
  90. def table(self, with_defaults=False, censored=True):
  91. filt = filter_hidden_settings if censored else lambda v: v
  92. return filt(dict(
  93. (k, v) for k, v in items(
  94. self if with_defaults else self.without_defaults())
  95. if k.isupper() and not k.startswith('_')
  96. ))
  97. def humanize(self, with_defaults=False, censored=True):
  98. """Return a human readable string showing changes to the
  99. configuration."""
  100. return '\n'.join(
  101. '{0}: {1}'.format(key, pretty(value, width=50))
  102. for key, value in items(self.table(with_defaults, censored)))
  103. class AppPickler(object):
  104. """Old application pickler/unpickler (< 3.1)."""
  105. def __call__(self, cls, *args):
  106. kwargs = self.build_kwargs(*args)
  107. app = self.construct(cls, **kwargs)
  108. self.prepare(app, **kwargs)
  109. return app
  110. def prepare(self, app, **kwargs):
  111. app.conf.update(kwargs['changes'])
  112. def build_kwargs(self, *args):
  113. return self.build_standard_kwargs(*args)
  114. def build_standard_kwargs(self, main, changes, loader, backend, amqp,
  115. events, log, control, accept_magic_kwargs,
  116. config_source=None):
  117. return dict(main=main, loader=loader, backend=backend, amqp=amqp,
  118. changes=changes, events=events, log=log, control=control,
  119. set_as_current=False,
  120. accept_magic_kwargs=accept_magic_kwargs,
  121. config_source=config_source)
  122. def construct(self, cls, **kwargs):
  123. return cls(**kwargs)
  124. def _unpickle_app(cls, pickler, *args):
  125. """Rebuild app for versions 2.5+"""
  126. return pickler()(cls, *args)
  127. def _unpickle_app_v2(cls, kwargs):
  128. """Rebuild app for versions 3.1+"""
  129. kwargs['set_as_current'] = False
  130. return cls(**kwargs)
  131. def filter_hidden_settings(conf):
  132. def maybe_censor(key, value, mask='*' * 8):
  133. if isinstance(value, Mapping):
  134. return filter_hidden_settings(value)
  135. if isinstance(key, string_t):
  136. if HIDDEN_SETTINGS.search(key):
  137. return mask
  138. elif 'BROKER_URL' in key.upper():
  139. from kombu import Connection
  140. return Connection(value).as_uri(mask=mask)
  141. elif key.upper() in ('CELERY_RESULT_BACKEND', 'CELERY_BACKEND'):
  142. return maybe_sanitize_url(value, mask=mask)
  143. return value
  144. return dict((k, maybe_censor(k, v)) for k, v in items(conf))
  145. def bugreport(app):
  146. """Return a string containing information useful in bug reports."""
  147. import billiard
  148. import celery
  149. import kombu
  150. try:
  151. conn = app.connection()
  152. driver_v = '{0}:{1}'.format(conn.transport.driver_name,
  153. conn.transport.driver_version())
  154. transport = conn.transport_cls
  155. except Exception:
  156. transport = driver_v = ''
  157. return BUGREPORT_INFO.format(
  158. system=_platform.system(),
  159. arch=', '.join(x for x in _platform.architecture() if x),
  160. py_i=pyimplementation(),
  161. celery_v=celery.VERSION_BANNER,
  162. kombu_v=kombu.__version__,
  163. billiard_v=billiard.__version__,
  164. py_v=_platform.python_version(),
  165. driver_v=driver_v,
  166. transport=transport,
  167. results=maybe_sanitize_url(
  168. app.conf.CELERY_RESULT_BACKEND or 'disabled'),
  169. human_settings=app.conf.humanize(),
  170. loader=qualname(app.loader.__class__),
  171. )
  172. def find_app(app, symbol_by_name=symbol_by_name, imp=import_from_cwd):
  173. from .base import Celery
  174. try:
  175. sym = symbol_by_name(app, imp=imp)
  176. except AttributeError:
  177. # last part was not an attribute, but a module
  178. sym = imp(app)
  179. if isinstance(sym, ModuleType) and ':' not in app:
  180. try:
  181. found = sym.app
  182. if isinstance(found, ModuleType):
  183. raise AttributeError()
  184. except AttributeError:
  185. try:
  186. found = sym.celery
  187. if isinstance(found, ModuleType):
  188. raise AttributeError()
  189. except AttributeError:
  190. if getattr(sym, '__path__', None):
  191. try:
  192. return find_app(
  193. '{0}.celery'.format(app),
  194. symbol_by_name=symbol_by_name, imp=imp,
  195. )
  196. except ImportError:
  197. pass
  198. for suspect in values(vars(sym)):
  199. if isinstance(suspect, Celery):
  200. return suspect
  201. raise
  202. else:
  203. return found
  204. else:
  205. return found
  206. return sym