base.py 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.app.base
  4. ~~~~~~~~~~~~~~~
  5. Actual App instance implementation.
  6. """
  7. from __future__ import absolute_import
  8. import os
  9. import threading
  10. import warnings
  11. from collections import defaultdict, deque
  12. from operator import attrgetter
  13. from functools import wraps
  14. from amqp import starpromise
  15. try:
  16. from billiard.util import register_after_fork
  17. except ImportError:
  18. register_after_fork = None
  19. from kombu.clocks import LamportClock
  20. from kombu.common import oid_from
  21. from kombu.utils import cached_property, uuid
  22. from celery import platforms
  23. from celery import signals
  24. from celery._state import (
  25. _task_stack, get_current_app, _set_current_app, set_default_app,
  26. _register_app, get_current_worker_task, connect_on_app_finalize,
  27. _announce_app_finalized,
  28. )
  29. from celery.datastructures import AttributeDictMixin
  30. from celery.exceptions import AlwaysEagerIgnored, ImproperlyConfigured
  31. from celery.five import UserDict, values
  32. from celery.loaders import get_loader_cls
  33. from celery.local import PromiseProxy, maybe_evaluate
  34. from celery.utils import abstract
  35. from celery.utils import gen_task_name
  36. from celery.utils.dispatch import Signal
  37. from celery.utils.functional import first, maybe_list, head_from_fun
  38. from celery.utils.imports import instantiate, symbol_by_name
  39. from celery.utils.objects import FallbackContext, mro_lookup
  40. from .annotations import prepare as prepare_annotations
  41. from .defaults import find_deprecated_settings
  42. from .registry import TaskRegistry
  43. from .utils import (
  44. AppPickler, Settings,
  45. bugreport, _unpickle_app, _unpickle_app_v2, appstr, detect_settings,
  46. )
  47. # Load all builtin tasks
  48. from . import builtins # noqa
  49. __all__ = ['Celery']
  50. _EXECV = os.environ.get('FORKED_BY_MULTIPROCESSING')
  51. BUILTIN_FIXUPS = {
  52. 'celery.fixups.django:fixup',
  53. }
  54. ERR_ENVVAR_NOT_SET = """\
  55. The environment variable {0!r} is not set,
  56. and as such the configuration could not be loaded.
  57. Please set this variable and make it point to
  58. a configuration module."""
  59. _after_fork_registered = False
  60. def app_has_custom(app, attr):
  61. return mro_lookup(app.__class__, attr, stop=(Celery, object),
  62. monkey_patched=[__name__])
  63. def _unpickle_appattr(reverse_name, args):
  64. """Given an attribute name and a list of args, gets
  65. the attribute from the current app and calls it."""
  66. return get_current_app()._rgetattr(reverse_name)(*args)
  67. def _global_after_fork(obj):
  68. # Previously every app would call:
  69. # `register_after_fork(app, app._after_fork)`
  70. # but this created a leak as `register_after_fork` stores concrete object
  71. # references and once registered an object cannot be removed without
  72. # touching and iterating over the private afterfork registry list.
  73. #
  74. # See Issue #1949
  75. from celery import _state
  76. from multiprocessing import util as mputil
  77. for app in _state._apps:
  78. try:
  79. app._after_fork(obj)
  80. except Exception as exc:
  81. if mputil._logger:
  82. mputil._logger.info(
  83. 'after forker raised exception: %r', exc, exc_info=1)
  84. def _ensure_after_fork():
  85. global _after_fork_registered
  86. _after_fork_registered = True
  87. if register_after_fork is not None:
  88. register_after_fork(_global_after_fork, _global_after_fork)
  89. class PendingConfiguration(UserDict, AttributeDictMixin):
  90. callback = None
  91. data = None
  92. def __init__(self, conf, callback):
  93. object.__setattr__(self, 'data', conf)
  94. object.__setattr__(self, 'callback', callback)
  95. def __getitem__(self, key):
  96. return self.callback(key)
  97. class Celery(object):
  98. """Celery application.
  99. :param main: Name of the main module if running as `__main__`.
  100. This is used as a prefix for task names.
  101. :keyword broker: URL of the default broker used.
  102. :keyword loader: The loader class, or the name of the loader class to use.
  103. Default is :class:`celery.loaders.app.AppLoader`.
  104. :keyword backend: The result store backend class, or the name of the
  105. backend class to use. Default is the value of the
  106. :setting:`result_backend` setting.
  107. :keyword amqp: AMQP object or class name.
  108. :keyword events: Events object or class name.
  109. :keyword log: Log object or class name.
  110. :keyword control: Control object or class name.
  111. :keyword set_as_current: Make this the global current app.
  112. :keyword tasks: A task registry or the name of a registry class.
  113. :keyword include: List of modules every worker should import.
  114. :keyword fixups: List of fixup plug-ins (see e.g.
  115. :mod:`celery.fixups.django`).
  116. :keyword autofinalize: If set to False a :exc:`RuntimeError`
  117. will be raised if the task registry or tasks are used before
  118. the app is finalized.
  119. """
  120. #: This is deprecated, use :meth:`reduce_keys` instead
  121. Pickler = AppPickler
  122. SYSTEM = platforms.SYSTEM
  123. IS_OSX, IS_WINDOWS = platforms.IS_OSX, platforms.IS_WINDOWS
  124. #: Name of the `__main__` module. Required for standalone scripts.
  125. #:
  126. #: If set this will be used instead of `__main__` when automatically
  127. #: generating task names.
  128. main = None
  129. #: Custom options for command-line programs.
  130. #: See :ref:`extending-commandoptions`
  131. user_options = None
  132. #: Custom bootsteps to extend and modify the worker.
  133. #: See :ref:`extending-bootsteps`.
  134. steps = None
  135. amqp_cls = 'celery.app.amqp:AMQP'
  136. backend_cls = None
  137. events_cls = 'celery.events:Events'
  138. loader_cls = 'celery.loaders.app:AppLoader'
  139. log_cls = 'celery.app.log:Logging'
  140. control_cls = 'celery.app.control:Control'
  141. task_cls = 'celery.app.task:Task'
  142. registry_cls = TaskRegistry
  143. _fixups = None
  144. _pool = None
  145. _conf = None
  146. builtin_fixups = BUILTIN_FIXUPS
  147. #: Signal sent when app is loading configuration.
  148. on_configure = None
  149. #: Signal sent after app has prepared the configuration.
  150. on_after_configure = None
  151. #: Signal sent after app has been finalized.
  152. on_after_finalize = None
  153. def __init__(self, main=None, loader=None, backend=None,
  154. amqp=None, events=None, log=None, control=None,
  155. set_as_current=True, tasks=None, broker=None, include=None,
  156. changes=None, config_source=None, fixups=None, task_cls=None,
  157. autofinalize=True, namespace=None, **kwargs):
  158. self.clock = LamportClock()
  159. self.main = main
  160. self.amqp_cls = amqp or self.amqp_cls
  161. self.events_cls = events or self.events_cls
  162. self.loader_cls = loader or self.loader_cls
  163. self.log_cls = log or self.log_cls
  164. self.control_cls = control or self.control_cls
  165. self.task_cls = task_cls or self.task_cls
  166. self.set_as_current = set_as_current
  167. self.registry_cls = symbol_by_name(self.registry_cls)
  168. self.user_options = defaultdict(set)
  169. self.steps = defaultdict(set)
  170. self.autofinalize = autofinalize
  171. self.namespace = namespace
  172. self.configured = False
  173. self._config_source = config_source
  174. self._pending_defaults = deque()
  175. self._pending_periodic_tasks = deque()
  176. self.finalized = False
  177. self._finalize_mutex = threading.Lock()
  178. self._pending = deque()
  179. self._tasks = tasks
  180. if not isinstance(self._tasks, TaskRegistry):
  181. self._tasks = TaskRegistry(self._tasks or {})
  182. # If the class defines a custom __reduce_args__ we need to use
  183. # the old way of pickling apps, which is pickling a list of
  184. # args instead of the new way that pickles a dict of keywords.
  185. self._using_v1_reduce = app_has_custom(self, '__reduce_args__')
  186. # these options are moved to the config to
  187. # simplify pickling of the app object.
  188. self._preconf = changes or {}
  189. self._preconf_set_by_auto = set()
  190. self.__autoset('broker_url', broker)
  191. self.__autoset('result_backend', backend)
  192. self.__autoset('include', include)
  193. self._conf = Settings(
  194. PendingConfiguration(
  195. self._preconf, self._get_from_conf_and_finalize),
  196. prefix=self.namespace,
  197. )
  198. # - Apply fixups.
  199. self.fixups = set(self.builtin_fixups) if fixups is None else fixups
  200. # ...store fixup instances in _fixups to keep weakrefs alive.
  201. self._fixups = [symbol_by_name(fixup)(self) for fixup in self.fixups]
  202. if self.set_as_current:
  203. self.set_current()
  204. # Signals
  205. if self.on_configure is None:
  206. # used to be a method pre 4.0
  207. self.on_configure = Signal()
  208. self.on_after_configure = Signal()
  209. self.on_after_finalize = Signal()
  210. self.on_init()
  211. _register_app(self)
  212. def __autoset(self, key, value):
  213. if value:
  214. self._preconf[key] = value
  215. self._preconf_set_by_auto.add(key)
  216. def set_current(self):
  217. """Makes this the current app for this thread."""
  218. _set_current_app(self)
  219. def set_default(self):
  220. """Makes this the default app for all threads."""
  221. set_default_app(self)
  222. def __enter__(self):
  223. return self
  224. def __exit__(self, *exc_info):
  225. self.close()
  226. def close(self):
  227. """Close any open pool connections and do any other steps necessary
  228. to clean up after the application.
  229. Only necessary for dynamically created apps for which you can
  230. use the with statement instead::
  231. with Celery(set_as_current=False) as app:
  232. with app.connection() as conn:
  233. pass
  234. """
  235. self._maybe_close_pool()
  236. def on_init(self):
  237. """Optional callback called at init."""
  238. pass
  239. def start(self, argv=None):
  240. """Run :program:`celery` using `argv`.
  241. Uses :data:`sys.argv` if `argv` is not specified.
  242. """
  243. return instantiate(
  244. 'celery.bin.celery:CeleryCommand',
  245. app=self).execute_from_commandline(argv)
  246. def worker_main(self, argv=None):
  247. """Run :program:`celery worker` using `argv`.
  248. Uses :data:`sys.argv` if `argv` is not specified.
  249. """
  250. return instantiate(
  251. 'celery.bin.worker:worker',
  252. app=self).execute_from_commandline(argv)
  253. def task(self, *args, **opts):
  254. """Decorator to create a task class out of any callable.
  255. Examples:
  256. .. code-block:: python
  257. @app.task
  258. def refresh_feed(url):
  259. return …
  260. with setting extra options:
  261. .. code-block:: python
  262. @app.task(exchange="feeds")
  263. def refresh_feed(url):
  264. return …
  265. .. admonition:: App Binding
  266. For custom apps the task decorator will return a proxy
  267. object, so that the act of creating the task is not performed
  268. until the task is used or the task registry is accessed.
  269. If you are depending on binding to be deferred, then you must
  270. not access any attributes on the returned object until the
  271. application is fully set up (finalized).
  272. """
  273. if _EXECV and opts.get('lazy', True):
  274. # When using execv the task in the original module will point to a
  275. # different app, so doing things like 'add.request' will point to
  276. # a different task instance. This makes sure it will always use
  277. # the task instance from the current app.
  278. # Really need a better solution for this :(
  279. from . import shared_task
  280. return shared_task(*args, lazy=False, **opts)
  281. def inner_create_task_cls(shared=True, filter=None, lazy=True, **opts):
  282. _filt = filter # stupid 2to3
  283. def _create_task_cls(fun):
  284. if shared:
  285. def cons(app):
  286. return app._task_from_fun(fun, **opts)
  287. cons.__name__ = fun.__name__
  288. connect_on_app_finalize(cons)
  289. if not lazy or self.finalized:
  290. ret = self._task_from_fun(fun, **opts)
  291. else:
  292. # return a proxy object that evaluates on first use
  293. ret = PromiseProxy(self._task_from_fun, (fun,), opts,
  294. __doc__=fun.__doc__)
  295. self._pending.append(ret)
  296. if _filt:
  297. return _filt(ret)
  298. return ret
  299. return _create_task_cls
  300. if len(args) == 1:
  301. if callable(args[0]):
  302. return inner_create_task_cls(**opts)(*args)
  303. raise TypeError('argument 1 to @task() must be a callable')
  304. if args:
  305. raise TypeError(
  306. '@task() takes exactly 1 argument ({0} given)'.format(
  307. sum([len(args), len(opts)])))
  308. return inner_create_task_cls(**opts)
  309. def _task_from_fun(self, fun, name=None, base=None, bind=False, **options):
  310. if not self.finalized and not self.autofinalize:
  311. raise RuntimeError('Contract breach: app not finalized')
  312. name = name or self.gen_task_name(fun.__name__, fun.__module__)
  313. base = base or self.Task
  314. if name not in self._tasks:
  315. run = fun if bind else staticmethod(fun)
  316. task = type(fun.__name__, (base,), dict({
  317. 'app': self,
  318. 'name': name,
  319. 'run': run,
  320. '_decorated': True,
  321. '__doc__': fun.__doc__,
  322. '__module__': fun.__module__,
  323. '__header__': staticmethod(head_from_fun(fun, bound=bind)),
  324. '__wrapped__': run}, **options))()
  325. self._tasks[task.name] = task
  326. task.bind(self) # connects task to this app
  327. autoretry_for = tuple(options.get('autoretry_for', ()))
  328. retry_kwargs = options.get('retry_kwargs', {})
  329. if autoretry_for and not hasattr(task, '_orig_run'):
  330. @wraps(task.run)
  331. def run(*args, **kwargs):
  332. try:
  333. return task._orig_run(*args, **kwargs)
  334. except autoretry_for as exc:
  335. raise task.retry(exc=exc, **retry_kwargs)
  336. task._orig_run, task.run = task.run, run
  337. else:
  338. task = self._tasks[name]
  339. return task
  340. def gen_task_name(self, name, module):
  341. return gen_task_name(self, name, module)
  342. def finalize(self, auto=False):
  343. """Finalizes the app by loading built-in tasks,
  344. and evaluating pending task decorators."""
  345. with self._finalize_mutex:
  346. if not self.finalized:
  347. if auto and not self.autofinalize:
  348. raise RuntimeError('Contract breach: app not finalized')
  349. self.finalized = True
  350. _announce_app_finalized(self)
  351. pending = self._pending
  352. while pending:
  353. maybe_evaluate(pending.popleft())
  354. for task in values(self._tasks):
  355. task.bind(self)
  356. self.on_after_finalize.send(sender=self)
  357. def add_defaults(self, fun):
  358. """Add default configuration from dict ``d``.
  359. If the argument is a callable function then it will be regarded
  360. as a promise, and it won't be loaded until the configuration is
  361. actually needed.
  362. This method can be compared to::
  363. >>> celery.conf.update(d)
  364. with a difference that 1) no copy will be made and 2) the dict will
  365. not be transferred when the worker spawns child processes, so
  366. it's important that the same configuration happens at import time
  367. when pickle restores the object on the other side.
  368. """
  369. if not callable(fun):
  370. d, fun = fun, lambda: d
  371. if self.configured:
  372. return self._conf.add_defaults(fun())
  373. self._pending_defaults.append(fun)
  374. def config_from_object(self, obj,
  375. silent=False, force=False, namespace=None):
  376. """Reads configuration from object, where object is either
  377. an object or the name of a module to import.
  378. :keyword silent: If true then import errors will be ignored.
  379. :keyword force: Force reading configuration immediately.
  380. By default the configuration will be read only when required.
  381. .. code-block:: pycon
  382. >>> celery.config_from_object("myapp.celeryconfig")
  383. >>> from myapp import celeryconfig
  384. >>> celery.config_from_object(celeryconfig)
  385. """
  386. self._config_source = obj
  387. self.namespace = namespace or self.namespace
  388. if force or self.configured:
  389. self._conf = None
  390. if self.loader.config_from_object(obj, silent=silent):
  391. return self.conf
  392. def config_from_envvar(self, variable_name, silent=False, force=False):
  393. """Read configuration from environment variable.
  394. The value of the environment variable must be the name
  395. of a module to import.
  396. .. code-block:: pycon
  397. >>> os.environ["CELERY_CONFIG_MODULE"] = "myapp.celeryconfig"
  398. >>> celery.config_from_envvar("CELERY_CONFIG_MODULE")
  399. """
  400. module_name = os.environ.get(variable_name)
  401. if not module_name:
  402. if silent:
  403. return False
  404. raise ImproperlyConfigured(
  405. ERR_ENVVAR_NOT_SET.format(variable_name))
  406. return self.config_from_object(module_name, silent=silent, force=force)
  407. def config_from_cmdline(self, argv, namespace='celery'):
  408. self._conf.update(
  409. self.loader.cmdline_config_parser(argv, namespace)
  410. )
  411. def setup_security(self, allowed_serializers=None, key=None, cert=None,
  412. store=None, digest='sha1', serializer='json'):
  413. """Setup the message-signing serializer.
  414. This will affect all application instances (a global operation).
  415. Disables untrusted serializers and if configured to use the ``auth``
  416. serializer will register the auth serializer with the provided settings
  417. into the Kombu serializer registry.
  418. :keyword allowed_serializers: List of serializer names, or
  419. content_types that should be exempt from being disabled.
  420. :keyword key: Name of private key file to use.
  421. Defaults to the :setting:`security_key` setting.
  422. :keyword cert: Name of certificate file to use.
  423. Defaults to the :setting:`security_certificate` setting.
  424. :keyword store: Directory containing certificates.
  425. Defaults to the :setting:`security_cert_store` setting.
  426. :keyword digest: Digest algorithm used when signing messages.
  427. Default is ``sha1``.
  428. :keyword serializer: Serializer used to encode messages after
  429. they have been signed. See :setting:`task_serializer` for
  430. the serializers supported.
  431. Default is ``json``.
  432. """
  433. from celery.security import setup_security
  434. return setup_security(allowed_serializers, key, cert,
  435. store, digest, serializer, app=self)
  436. def autodiscover_tasks(self, packages=None,
  437. related_name='tasks', force=False):
  438. """Try to autodiscover and import modules with a specific name (by
  439. default 'tasks').
  440. If the name is empty, this will be delegated to fixups (e.g. Django).
  441. For example if you have an (imagined) directory tree like this::
  442. foo/__init__.py
  443. tasks.py
  444. models.py
  445. bar/__init__.py
  446. tasks.py
  447. models.py
  448. baz/__init__.py
  449. models.py
  450. Then calling ``app.autodiscover_tasks(['foo', bar', 'baz'])`` will
  451. result in the modules ``foo.tasks`` and ``bar.tasks`` being imported.
  452. :param packages: List of packages to search.
  453. This argument may also be a callable, in which case the
  454. value returned is used (for lazy evaluation).
  455. :keyword related_name: The name of the module to find. Defaults
  456. to "tasks", which means it look for "module.tasks" for every
  457. module in ``packages``.
  458. :keyword force: By default this call is lazy so that the actual
  459. autodiscovery will not happen until an application imports the
  460. default modules. Forcing will cause the autodiscovery to happen
  461. immediately.
  462. """
  463. if force:
  464. return self._autodiscover_tasks(packages, related_name)
  465. signals.import_modules.connect(starpromise(
  466. self._autodiscover_tasks, packages, related_name,
  467. ), weak=False, sender=self)
  468. def _autodiscover_tasks(self, packages, related_name, **kwargs):
  469. if packages:
  470. return self._autodiscover_tasks_from_names(packages, related_name)
  471. return self._autodiscover_tasks_from_fixups(related_name)
  472. def _autodiscover_tasks_from_names(self, packages, related_name):
  473. # packages argument can be lazy
  474. return self.loader.autodiscover_tasks(
  475. packages() if callable(packages) else packages, related_name,
  476. )
  477. def _autodiscover_tasks_from_fixups(self, related_name):
  478. return self._autodiscover_tasks_from_names([
  479. pkg for fixup in self._fixups
  480. for pkg in fixup.autodiscover_tasks()
  481. if hasattr(fixup, 'autodiscover_tasks')
  482. ], related_name=related_name)
  483. def send_task(self, name, args=None, kwargs=None, countdown=None,
  484. eta=None, task_id=None, producer=None, connection=None,
  485. router=None, result_cls=None, expires=None,
  486. publisher=None, link=None, link_error=None,
  487. add_to_parent=True, group_id=None, retries=0, chord=None,
  488. reply_to=None, time_limit=None, soft_time_limit=None,
  489. root_id=None, parent_id=None, route_name=None,
  490. shadow=None, **options):
  491. """Send task by name.
  492. :param name: Name of task to call (e.g. `"tasks.add"`).
  493. :keyword result_cls: Specify custom result class. Default is
  494. using :meth:`AsyncResult`.
  495. Otherwise supports the same arguments as :meth:`@-Task.apply_async`.
  496. """
  497. amqp = self.amqp
  498. task_id = task_id or uuid()
  499. producer = producer or publisher # XXX compat
  500. router = router or amqp.router
  501. conf = self.conf
  502. if conf.task_always_eager: # pragma: no cover
  503. warnings.warn(AlwaysEagerIgnored(
  504. 'task_always_eager has no effect on send_task',
  505. ), stacklevel=2)
  506. options = router.route(options, route_name or name, args, kwargs)
  507. message = amqp.create_task_message(
  508. task_id, name, args, kwargs, countdown, eta, group_id,
  509. expires, retries, chord,
  510. maybe_list(link), maybe_list(link_error),
  511. reply_to or self.oid, time_limit, soft_time_limit,
  512. self.conf.task_send_sent_event,
  513. root_id, parent_id, shadow,
  514. )
  515. if connection:
  516. producer = amqp.Producer(connection)
  517. with self.producer_or_acquire(producer) as P:
  518. self.backend.on_task_call(P, task_id)
  519. amqp.send_task_message(P, name, message, **options)
  520. result = (result_cls or self.AsyncResult)(task_id)
  521. if add_to_parent:
  522. parent = get_current_worker_task()
  523. if parent:
  524. parent.add_trail(result)
  525. return result
  526. def connection(self, hostname=None, userid=None, password=None,
  527. virtual_host=None, port=None, ssl=None,
  528. connect_timeout=None, transport=None,
  529. transport_options=None, heartbeat=None,
  530. login_method=None, failover_strategy=None, **kwargs):
  531. """Establish a connection to the message broker.
  532. :param url: Either the URL or the hostname of the broker to use.
  533. :keyword hostname: URL, Hostname/IP-address of the broker.
  534. If an URL is used, then the other argument below will
  535. be taken from the URL instead.
  536. :keyword userid: Username to authenticate as.
  537. :keyword password: Password to authenticate with
  538. :keyword virtual_host: Virtual host to use (domain).
  539. :keyword port: Port to connect to.
  540. :keyword ssl: Defaults to the :setting:`broker_use_ssl` setting.
  541. :keyword transport: defaults to the :setting:`broker_transport`
  542. setting.
  543. :returns :class:`kombu.Connection`:
  544. """
  545. conf = self.conf
  546. return self.amqp.Connection(
  547. hostname or conf.broker_url,
  548. userid or conf.broker_user,
  549. password or conf.broker_password,
  550. virtual_host or conf.broker_vhost,
  551. port or conf.broker_port,
  552. transport=transport or conf.broker_transport,
  553. ssl=self.either('broker_use_ssl', ssl),
  554. heartbeat=heartbeat,
  555. login_method=login_method or conf.broker_login_method,
  556. failover_strategy=(
  557. failover_strategy or conf.broker_failover_strategy
  558. ),
  559. transport_options=dict(
  560. conf.broker_transport_options, **transport_options or {}
  561. ),
  562. connect_timeout=self.either(
  563. 'broker_connection_timeout', connect_timeout
  564. ),
  565. )
  566. broker_connection = connection
  567. def _acquire_connection(self, pool=True):
  568. """Helper for :meth:`connection_or_acquire`."""
  569. if pool:
  570. return self.pool.acquire(block=True)
  571. return self.connection()
  572. def connection_or_acquire(self, connection=None, pool=True, *_, **__):
  573. """For use within a with-statement to get a connection from the pool
  574. if one is not already provided.
  575. :keyword connection: If not provided, then a connection will be
  576. acquired from the connection pool.
  577. """
  578. return FallbackContext(connection, self._acquire_connection, pool=pool)
  579. default_connection = connection_or_acquire # XXX compat
  580. def producer_or_acquire(self, producer=None):
  581. """For use within a with-statement to get a producer from the pool
  582. if one is not already provided
  583. :keyword producer: If not provided, then a producer will be
  584. acquired from the producer pool.
  585. """
  586. return FallbackContext(
  587. producer, self.amqp.producer_pool.acquire, block=True,
  588. )
  589. default_producer = producer_or_acquire # XXX compat
  590. def prepare_config(self, c):
  591. """Prepare configuration before it is merged with the defaults."""
  592. return find_deprecated_settings(c)
  593. def now(self):
  594. """Return the current time and date as a
  595. :class:`~datetime.datetime` object."""
  596. return self.loader.now(utc=self.conf.enable_utc)
  597. def mail_admins(self, subject, body, fail_silently=False):
  598. """Sends an email to the admins in the :setting:`admins` setting."""
  599. conf = self.conf
  600. if conf.admins:
  601. to = [admin_email for _, admin_email in conf.admins]
  602. return self.loader.mail_admins(
  603. subject, body, fail_silently, to=to,
  604. sender=conf.server_email,
  605. host=conf.email_host,
  606. port=conf.email_port,
  607. user=conf.email_host_user,
  608. password=conf.email_host_password,
  609. timeout=conf.email_timeout,
  610. use_ssl=conf.email_use_ssl,
  611. use_tls=conf.email_use_tls,
  612. charset=conf.email_charset,
  613. )
  614. def select_queues(self, queues=None):
  615. """Select a subset of queues, where queues must be a list of queue
  616. names to keep."""
  617. return self.amqp.queues.select(queues)
  618. def either(self, default_key, *values):
  619. """Fallback to the value of a configuration key if none of the
  620. `*values` are true."""
  621. return first(None, [
  622. first(None, values), starpromise(self.conf.get, default_key),
  623. ])
  624. def bugreport(self):
  625. """Return a string with information useful for the Celery core
  626. developers when reporting a bug."""
  627. return bugreport(self)
  628. def _get_backend(self):
  629. from celery.backends import get_backend_by_url
  630. backend, url = get_backend_by_url(
  631. self.backend_cls or self.conf.result_backend,
  632. self.loader)
  633. return backend(app=self, url=url)
  634. def _load_config(self):
  635. if isinstance(self.on_configure, Signal):
  636. self.on_configure.send(sender=self)
  637. else:
  638. # used to be a method pre 4.0
  639. self.on_configure()
  640. if self._config_source:
  641. self.loader.config_from_object(self._config_source)
  642. self.configured = True
  643. settings = detect_settings(
  644. self.prepare_config(self.loader.conf), self._preconf,
  645. ignore_keys=self._preconf_set_by_auto, prefix=self.namespace,
  646. )
  647. if self._conf is not None:
  648. # replace in place, as someone may have referenced app.conf,
  649. # done some changes, accessed a key, and then try to make more
  650. # changes to the reference and not the finalized value.
  651. self._conf.swap_with(settings)
  652. else:
  653. self._conf = settings
  654. # load lazy config dict initializers.
  655. pending_def = self._pending_defaults
  656. while pending_def:
  657. self._conf.add_defaults(maybe_evaluate(pending_def.popleft()()))
  658. # load lazy periodic tasks
  659. pending_beat = self._pending_periodic_tasks
  660. while pending_beat:
  661. self._add_periodic_task(*pending_beat.popleft())
  662. self.on_after_configure.send(sender=self, source=self._conf)
  663. return self._conf
  664. def _after_fork(self, obj_):
  665. self._maybe_close_pool()
  666. def _maybe_close_pool(self):
  667. if self._pool:
  668. self._pool.force_close_all()
  669. self._pool = None
  670. amqp = self.__dict__.get('amqp')
  671. if amqp is not None and amqp._producer_pool is not None:
  672. amqp._producer_pool.force_close_all()
  673. amqp._producer_pool = None
  674. def signature(self, *args, **kwargs):
  675. """Return a new :class:`~celery.canvas.Signature` bound to this app.
  676. See :meth:`~celery.signature`
  677. """
  678. kwargs['app'] = self
  679. return self.canvas.signature(*args, **kwargs)
  680. def add_periodic_task(self, schedule, sig,
  681. args=(), kwargs=(), name=None, **opts):
  682. key, entry = self._sig_to_periodic_task_entry(
  683. schedule, sig, args, kwargs, name, **opts)
  684. if self.configured:
  685. self._add_periodic_task(key, entry)
  686. else:
  687. self._pending_periodic_tasks.append((key, entry))
  688. return key
  689. def _sig_to_periodic_task_entry(self, schedule, sig,
  690. args=(), kwargs={}, name=None, **opts):
  691. sig = (sig.clone(args, kwargs)
  692. if isinstance(sig, abstract.CallableSignature)
  693. else self.signature(sig.name, args, kwargs))
  694. return name or repr(sig), {
  695. 'schedule': schedule,
  696. 'task': sig.name,
  697. 'args': sig.args,
  698. 'kwargs': sig.kwargs,
  699. 'options': dict(sig.options, **opts),
  700. }
  701. def _add_periodic_task(self, key, entry):
  702. self._conf.beat_schedule[key] = entry
  703. def create_task_cls(self):
  704. """Creates a base task class using default configuration
  705. taken from this app."""
  706. return self.subclass_with_self(
  707. self.task_cls, name='Task', attribute='_app',
  708. keep_reduce=True, abstract=True,
  709. )
  710. def subclass_with_self(self, Class, name=None, attribute='app',
  711. reverse=None, keep_reduce=False, **kw):
  712. """Subclass an app-compatible class by setting its app attribute
  713. to be this app instance.
  714. App-compatible means that the class has a class attribute that
  715. provides the default app it should use, e.g.
  716. ``class Foo: app = None``.
  717. :param Class: The app-compatible class to subclass.
  718. :keyword name: Custom name for the target class.
  719. :keyword attribute: Name of the attribute holding the app,
  720. default is 'app'.
  721. """
  722. Class = symbol_by_name(Class)
  723. reverse = reverse if reverse else Class.__name__
  724. def __reduce__(self):
  725. return _unpickle_appattr, (reverse, self.__reduce_args__())
  726. attrs = dict({attribute: self}, __module__=Class.__module__,
  727. __doc__=Class.__doc__, **kw)
  728. if not keep_reduce:
  729. attrs['__reduce__'] = __reduce__
  730. return type(name or Class.__name__, (Class,), attrs)
  731. def _rgetattr(self, path):
  732. return attrgetter(path)(self)
  733. def __repr__(self):
  734. return '<{0} {1}>'.format(type(self).__name__, appstr(self))
  735. def __reduce__(self):
  736. if self._using_v1_reduce:
  737. return self.__reduce_v1__()
  738. return (_unpickle_app_v2, (self.__class__, self.__reduce_keys__()))
  739. def __reduce_v1__(self):
  740. # Reduce only pickles the configuration changes,
  741. # so the default configuration doesn't have to be passed
  742. # between processes.
  743. return (
  744. _unpickle_app,
  745. (self.__class__, self.Pickler) + self.__reduce_args__(),
  746. )
  747. def __reduce_keys__(self):
  748. """Return keyword arguments used to reconstruct the object
  749. when unpickling."""
  750. return {
  751. 'main': self.main,
  752. 'changes':
  753. self._conf.changes if self.configured else self._preconf,
  754. 'loader': self.loader_cls,
  755. 'backend': self.backend_cls,
  756. 'amqp': self.amqp_cls,
  757. 'events': self.events_cls,
  758. 'log': self.log_cls,
  759. 'control': self.control_cls,
  760. 'fixups': self.fixups,
  761. 'config_source': self._config_source,
  762. 'task_cls': self.task_cls,
  763. 'namespace': self.namespace,
  764. }
  765. def __reduce_args__(self):
  766. """Deprecated method, please use :meth:`__reduce_keys__` instead."""
  767. return (self.main, self._conf.changes if self.configured else {},
  768. self.loader_cls, self.backend_cls, self.amqp_cls,
  769. self.events_cls, self.log_cls, self.control_cls,
  770. False, self._config_source)
  771. @cached_property
  772. def Worker(self):
  773. """Worker application. See :class:`~@Worker`."""
  774. return self.subclass_with_self('celery.apps.worker:Worker')
  775. @cached_property
  776. def WorkController(self, **kwargs):
  777. """Embeddable worker. See :class:`~@WorkController`."""
  778. return self.subclass_with_self('celery.worker:WorkController')
  779. @cached_property
  780. def Beat(self, **kwargs):
  781. """Celerybeat scheduler application.
  782. See :class:`~@Beat`.
  783. """
  784. return self.subclass_with_self('celery.apps.beat:Beat')
  785. @cached_property
  786. def Task(self):
  787. """Base task class for this app."""
  788. return self.create_task_cls()
  789. @cached_property
  790. def annotations(self):
  791. return prepare_annotations(self.conf.task_annotations)
  792. @cached_property
  793. def AsyncResult(self):
  794. """Create new result instance.
  795. See :class:`celery.result.AsyncResult`.
  796. """
  797. return self.subclass_with_self('celery.result:AsyncResult')
  798. @cached_property
  799. def ResultSet(self):
  800. return self.subclass_with_self('celery.result:ResultSet')
  801. @cached_property
  802. def GroupResult(self):
  803. """Create new group result instance.
  804. See :class:`celery.result.GroupResult`.
  805. """
  806. return self.subclass_with_self('celery.result:GroupResult')
  807. @property
  808. def pool(self):
  809. """Broker connection pool: :class:`~@pool`.
  810. This attribute is not related to the workers concurrency pool.
  811. """
  812. if self._pool is None:
  813. _ensure_after_fork()
  814. limit = self.conf.broker_pool_limit
  815. self._pool = self.connection().Pool(limit=limit)
  816. return self._pool
  817. @property
  818. def current_task(self):
  819. """The instance of the task that is being executed, or
  820. :const:`None`."""
  821. return _task_stack.top
  822. @cached_property
  823. def oid(self):
  824. return oid_from(self)
  825. @cached_property
  826. def amqp(self):
  827. """AMQP related functionality: :class:`~@amqp`."""
  828. return instantiate(self.amqp_cls, app=self)
  829. @cached_property
  830. def backend(self):
  831. """Current backend instance."""
  832. return self._get_backend()
  833. @property
  834. def conf(self):
  835. """Current configuration."""
  836. if self._conf is None:
  837. self._conf = self._load_config()
  838. return self._conf
  839. def _get_from_conf_and_finalize(self, key):
  840. conf = self._conf = self._load_config()
  841. return conf[key]
  842. @conf.setter
  843. def conf(self, d): # noqa
  844. self._conf = d
  845. @cached_property
  846. def control(self):
  847. """Remote control: :class:`~@control`."""
  848. return instantiate(self.control_cls, app=self)
  849. @cached_property
  850. def events(self):
  851. """Consuming and sending events: :class:`~@events`."""
  852. return instantiate(self.events_cls, app=self)
  853. @cached_property
  854. def loader(self):
  855. """Current loader instance."""
  856. return get_loader_cls(self.loader_cls)(app=self)
  857. @cached_property
  858. def log(self):
  859. """Logging: :class:`~@log`."""
  860. return instantiate(self.log_cls, app=self)
  861. @cached_property
  862. def canvas(self):
  863. from celery import canvas
  864. return canvas
  865. @cached_property
  866. def tasks(self):
  867. """Task registry.
  868. Accessing this attribute will also finalize the app.
  869. """
  870. self.finalize(auto=True)
  871. return self._tasks
  872. @cached_property
  873. def timezone(self):
  874. """Current timezone for this app.
  875. This is a cached property taking the time zone from the
  876. :setting:`timezone` setting.
  877. """
  878. from celery.utils.timeutils import timezone
  879. conf = self.conf
  880. tz = conf.timezone
  881. if not tz:
  882. return (timezone.get_timezone('UTC') if conf.enable_utc
  883. else timezone.local)
  884. return timezone.get_timezone(conf.timezone)
  885. App = Celery # compat