utils.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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 celery.datastructures import ConfigurationView
  12. from celery.five import items
  13. from celery.platforms import pyimplementation
  14. from celery.utils.text import pretty
  15. from celery.utils.imports import qualname
  16. from .defaults import find
  17. #: Format used to generate bugreport information.
  18. BUGREPORT_INFO = """
  19. software -> celery:{celery_v} kombu:{kombu_v} py:{py_v}
  20. billiard:{billiard_v} {driver_v}
  21. platform -> system:{system} arch:{arch} imp:{py_i}
  22. loader -> {loader}
  23. settings -> transport:{transport} results:{results}
  24. {human_settings}
  25. """
  26. HIDDEN_SETTINGS = re.compile(
  27. 'API|TOKEN|KEY|SECRET|PASS|PROFANITIES_LIST|SIGNATURE|DATABASE',
  28. re.IGNORECASE,
  29. )
  30. def appstr(app):
  31. """String used in __repr__ etc, to id app instances."""
  32. return '{0}:0x{1:x}'.format(app.main or '__main__', id(app))
  33. class Settings(ConfigurationView):
  34. """Celery settings object."""
  35. @property
  36. def CELERY_RESULT_BACKEND(self):
  37. return self.first('CELERY_RESULT_BACKEND', 'CELERY_BACKEND')
  38. @property
  39. def BROKER_TRANSPORT(self):
  40. return self.first('BROKER_TRANSPORT',
  41. 'BROKER_BACKEND', 'CARROT_BACKEND')
  42. @property
  43. def BROKER_BACKEND(self):
  44. """Deprecated compat alias to :attr:`BROKER_TRANSPORT`."""
  45. return self.BROKER_TRANSPORT
  46. @property
  47. def BROKER_HOST(self):
  48. return (os.environ.get('CELERY_BROKER_URL') or
  49. self.first('BROKER_URL', 'BROKER_HOST'))
  50. @property
  51. def CELERY_TIMEZONE(self):
  52. # this way we also support django's time zone.
  53. return self.first('CELERY_TIMEZONE', 'TIME_ZONE')
  54. def without_defaults(self):
  55. """Returns the current configuration, but without defaults."""
  56. # the last stash is the default settings, so just skip that
  57. return Settings({}, self._order[:-1])
  58. def find_option(self, name, namespace='celery'):
  59. """Search for option by name.
  60. Will return ``(namespace, option_name, Option)`` tuple, e.g.::
  61. >>> celery.conf.find_option('disable_rate_limits')
  62. ('CELERY', 'DISABLE_RATE_LIMITS',
  63. <Option: type->bool default->False>))
  64. :param name: Name of option, cannot be partial.
  65. :keyword namespace: Preferred namespace (``CELERY`` by default).
  66. """
  67. return find(name, namespace)
  68. def find_value_for_key(self, name, namespace='celery'):
  69. """Shortcut to ``get_by_parts(*find_option(name)[:-1])``"""
  70. return self.get_by_parts(*self.find_option(name, namespace)[:-1])
  71. def get_by_parts(self, *parts):
  72. """Returns the current value for setting specified as a path.
  73. Example::
  74. >>> celery.conf.get_by_parts('CELERY', 'DISABLE_RATE_LIMITS')
  75. False
  76. """
  77. return self['_'.join(part for part in parts if part)]
  78. def humanize(self):
  79. """Returns a human readable string showing changes to the
  80. configuration."""
  81. return '\n'.join(
  82. '{0}: {1}'.format(key, pretty(value, width=50))
  83. for key, value in items(filter_hidden_settings(dict(
  84. (k, v) for k, v in items(self.without_defaults())
  85. if k.isupper() and not k.startswith('_')))))
  86. class AppPickler(object):
  87. """Old application pickler/unpickler (< 3.1)."""
  88. def __call__(self, cls, *args):
  89. kwargs = self.build_kwargs(*args)
  90. app = self.construct(cls, **kwargs)
  91. self.prepare(app, **kwargs)
  92. return app
  93. def prepare(self, app, **kwargs):
  94. app.conf.update(kwargs['changes'])
  95. def build_kwargs(self, *args):
  96. return self.build_standard_kwargs(*args)
  97. def build_standard_kwargs(self, main, changes, loader, backend, amqp,
  98. events, log, control, accept_magic_kwargs,
  99. config_source=None):
  100. return dict(main=main, loader=loader, backend=backend, amqp=amqp,
  101. changes=changes, events=events, log=log, control=control,
  102. set_as_current=False,
  103. accept_magic_kwargs=accept_magic_kwargs,
  104. config_source=config_source)
  105. def construct(self, cls, **kwargs):
  106. return cls(**kwargs)
  107. def _unpickle_app(cls, pickler, *args):
  108. """Rebuild app for versions 2.5+"""
  109. return pickler()(cls, *args)
  110. def _unpickle_app_v2(cls, kwargs):
  111. """Rebuild app for versions 3.1+"""
  112. kwargs['set_as_current'] = False
  113. return cls(**kwargs)
  114. def filter_hidden_settings(conf):
  115. def maybe_censor(key, value):
  116. return '********' if HIDDEN_SETTINGS.search(key) else value
  117. return dict((k, maybe_censor(k, v)) for k, v in items(conf))
  118. def bugreport(app):
  119. """Returns a string containing information useful in bug reports."""
  120. import billiard
  121. import celery
  122. import kombu
  123. try:
  124. conn = app.connection()
  125. driver_v = '{0}:{1}'.format(conn.transport.driver_name,
  126. conn.transport.driver_version())
  127. transport = conn.transport_cls
  128. except Exception:
  129. transport = driver_v = ''
  130. return BUGREPORT_INFO.format(
  131. system=_platform.system(),
  132. arch=', '.join(x for x in _platform.architecture() if x),
  133. py_i=pyimplementation(),
  134. celery_v=celery.VERSION_BANNER,
  135. kombu_v=kombu.__version__,
  136. billiard_v=billiard.__version__,
  137. py_v=_platform.python_version(),
  138. driver_v=driver_v,
  139. transport=transport,
  140. results=app.conf.CELERY_RESULT_BACKEND or 'disabled',
  141. human_settings=app.conf.humanize(),
  142. loader=qualname(app.loader.__class__),
  143. )