test_app.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. from __future__ import absolute_import, unicode_literals
  2. import gc
  3. import itertools
  4. import os
  5. from copy import deepcopy
  6. from datetime import datetime, timedelta
  7. from pickle import dumps, loads
  8. import pytest
  9. from case import ContextMock, Mock, mock, patch
  10. from celery import app as _app
  11. from celery import Celery, _state, current_app, shared_task
  12. from celery.app import base as _appbase
  13. from celery.app import defaults
  14. from celery.exceptions import ImproperlyConfigured
  15. from celery.five import items, keys
  16. from celery.loaders.base import unconfigured
  17. from celery.platforms import pyimplementation
  18. from celery.utils.collections import DictAttribute
  19. from celery.utils.objects import Bunch
  20. from celery.utils.serialization import pickle
  21. from celery.utils.time import localize, timezone, to_utc
  22. from vine import promise
  23. THIS_IS_A_KEY = 'this is a value'
  24. class ObjectConfig(object):
  25. FOO = 1
  26. BAR = 2
  27. object_config = ObjectConfig()
  28. dict_config = {'FOO': 10, 'BAR': 20}
  29. class ObjectConfig2(object):
  30. LEAVE_FOR_WORK = True
  31. MOMENT_TO_STOP = True
  32. CALL_ME_BACK = 123456789
  33. WANT_ME_TO = False
  34. UNDERSTAND_ME = True
  35. class test_module:
  36. def test_default_app(self):
  37. assert _app.default_app == _state.default_app
  38. def test_bugreport(self, app):
  39. assert _app.bugreport(app=app)
  40. class test_task_join_will_block:
  41. def test_task_join_will_block(self, patching):
  42. patching('celery._state._task_join_will_block', 0)
  43. assert _state._task_join_will_block == 0
  44. _state._set_task_join_will_block(True)
  45. assert _state._task_join_will_block is True
  46. # fixture 'app' sets this, so need to use orig_ function
  47. # set there by that fixture.
  48. res = _state.orig_task_join_will_block()
  49. assert res is True
  50. class test_App:
  51. def setup(self):
  52. self.app.add_defaults(deepcopy(self.CELERY_TEST_CONFIG))
  53. def test_now(self):
  54. timezone_setting_value = 'US/Eastern'
  55. tz_utc = timezone.get_timezone('UTC')
  56. tz_us_eastern = timezone.get_timezone(timezone_setting_value)
  57. now = to_utc(datetime.utcnow())
  58. app_now = self.app.now()
  59. assert app_now.tzinfo is tz_utc
  60. assert app_now - now <= timedelta(seconds=1)
  61. # Check that timezone conversion is applied from configuration
  62. self.app.conf.enable_utc = False
  63. self.app.conf.timezone = timezone_setting_value
  64. # timezone is a cached property
  65. del self.app.timezone
  66. app_now = self.app.now()
  67. assert app_now.tzinfo.zone == tz_us_eastern.zone
  68. diff = to_utc(datetime.utcnow()) - localize(app_now, tz_utc)
  69. assert diff <= timedelta(seconds=1)
  70. # Verify that timezone setting overrides enable_utc=on setting
  71. self.app.conf.enable_utc = True
  72. del self.app.timezone
  73. app_now = self.app.now()
  74. assert self.app.timezone == tz_us_eastern
  75. assert app_now.tzinfo.zone == tz_us_eastern.zone
  76. @patch('celery.app.base.set_default_app')
  77. def test_set_default(self, set_default_app):
  78. self.app.set_default()
  79. set_default_app.assert_called_with(self.app)
  80. @patch('celery.security.setup_security')
  81. def test_setup_security(self, setup_security):
  82. self.app.setup_security(
  83. {'json'}, 'key', 'cert', 'store', 'digest', 'serializer')
  84. setup_security.assert_called_with(
  85. {'json'}, 'key', 'cert', 'store', 'digest', 'serializer',
  86. app=self.app)
  87. def test_task_autofinalize_disabled(self):
  88. with self.Celery('xyzibari', autofinalize=False) as app:
  89. @app.task
  90. def ttafd():
  91. return 42
  92. with pytest.raises(RuntimeError):
  93. ttafd()
  94. with self.Celery('xyzibari', autofinalize=False) as app:
  95. @app.task
  96. def ttafd2():
  97. return 42
  98. app.finalize()
  99. assert ttafd2() == 42
  100. def test_registry_autofinalize_disabled(self):
  101. with self.Celery('xyzibari', autofinalize=False) as app:
  102. with pytest.raises(RuntimeError):
  103. app.tasks['celery.chain']
  104. app.finalize()
  105. assert app.tasks['celery.chain']
  106. def test_task(self):
  107. with self.Celery('foozibari') as app:
  108. def fun():
  109. pass
  110. fun.__module__ = '__main__'
  111. task = app.task(fun)
  112. assert task.name == app.main + '.fun'
  113. def test_task_too_many_args(self):
  114. with pytest.raises(TypeError):
  115. self.app.task(Mock(name='fun'), True)
  116. with pytest.raises(TypeError):
  117. self.app.task(Mock(name='fun'), True, 1, 2)
  118. def test_with_config_source(self):
  119. with self.Celery(config_source=ObjectConfig) as app:
  120. assert app.conf.FOO == 1
  121. assert app.conf.BAR == 2
  122. @pytest.mark.usefixtures('depends_on_current_app')
  123. def test_task_windows_execv(self):
  124. prev, _appbase.USING_EXECV = _appbase.USING_EXECV, True
  125. try:
  126. @self.app.task(shared=False)
  127. def foo():
  128. pass
  129. assert foo._get_current_object() # is proxy
  130. finally:
  131. _appbase.USING_EXECV = prev
  132. assert not _appbase.USING_EXECV
  133. def test_task_takes_no_args(self):
  134. with pytest.raises(TypeError):
  135. @self.app.task(1)
  136. def foo():
  137. pass
  138. def test_add_defaults(self):
  139. assert not self.app.configured
  140. _conf = {'foo': 300}
  141. def conf():
  142. return _conf
  143. self.app.add_defaults(conf)
  144. assert conf in self.app._pending_defaults
  145. assert not self.app.configured
  146. assert self.app.conf.foo == 300
  147. assert self.app.configured
  148. assert not self.app._pending_defaults
  149. # defaults not pickled
  150. appr = loads(dumps(self.app))
  151. with pytest.raises(AttributeError):
  152. appr.conf.foo
  153. # add more defaults after configured
  154. conf2 = {'foo': 'BAR'}
  155. self.app.add_defaults(conf2)
  156. assert self.app.conf.foo == 'BAR'
  157. assert _conf in self.app.conf.defaults
  158. assert conf2 in self.app.conf.defaults
  159. def test_connection_or_acquire(self):
  160. with self.app.connection_or_acquire(block=True):
  161. assert self.app.pool._dirty
  162. with self.app.connection_or_acquire(pool=False):
  163. assert not self.app.pool._dirty
  164. def test_using_v1_reduce(self):
  165. self.app._using_v1_reduce = True
  166. assert loads(dumps(self.app))
  167. def test_autodiscover_tasks_force(self):
  168. self.app.loader.autodiscover_tasks = Mock()
  169. self.app.autodiscover_tasks(['proj.A', 'proj.B'], force=True)
  170. self.app.loader.autodiscover_tasks.assert_called_with(
  171. ['proj.A', 'proj.B'], 'tasks',
  172. )
  173. self.app.loader.autodiscover_tasks = Mock()
  174. def lazy_list():
  175. return ['proj.A', 'proj.B']
  176. self.app.autodiscover_tasks(
  177. lazy_list,
  178. related_name='george',
  179. force=True,
  180. )
  181. self.app.loader.autodiscover_tasks.assert_called_with(
  182. ['proj.A', 'proj.B'], 'george',
  183. )
  184. def test_autodiscover_tasks_lazy(self):
  185. with patch('celery.signals.import_modules') as import_modules:
  186. def lazy_list():
  187. return [1, 2, 3]
  188. self.app.autodiscover_tasks(lazy_list)
  189. import_modules.connect.assert_called()
  190. prom = import_modules.connect.call_args[0][0]
  191. assert isinstance(prom, promise)
  192. assert prom.fun == self.app._autodiscover_tasks
  193. assert prom.args[0](), [1, 2 == 3]
  194. def test_autodiscover_tasks__no_packages(self):
  195. fixup1 = Mock(name='fixup')
  196. fixup2 = Mock(name='fixup')
  197. self.app._autodiscover_tasks_from_names = Mock(name='auto')
  198. self.app._fixups = [fixup1, fixup2]
  199. fixup1.autodiscover_tasks.return_value = ['A', 'B', 'C']
  200. fixup2.autodiscover_tasks.return_value = ['D', 'E', 'F']
  201. self.app.autodiscover_tasks(force=True)
  202. self.app._autodiscover_tasks_from_names.assert_called_with(
  203. ['A', 'B', 'C', 'D', 'E', 'F'], related_name='tasks',
  204. )
  205. def test_with_broker(self, patching):
  206. patching.setenv('CELERY_BROKER_URL', '')
  207. with self.Celery(broker='foo://baribaz') as app:
  208. assert app.conf.broker_url == 'foo://baribaz'
  209. def test_pending_configuration__setattr(self):
  210. with self.Celery(broker='foo://bar') as app:
  211. app.conf.task_default_delivery_mode = 44
  212. app.conf.worker_agent = 'foo:Bar'
  213. assert not app.configured
  214. assert app.conf.worker_agent == 'foo:Bar'
  215. assert app.conf.broker_url == 'foo://bar'
  216. assert app._preconf['worker_agent'] == 'foo:Bar'
  217. assert app.configured
  218. reapp = pickle.loads(pickle.dumps(app))
  219. assert reapp._preconf['worker_agent'] == 'foo:Bar'
  220. assert not reapp.configured
  221. assert reapp.conf.worker_agent == 'foo:Bar'
  222. assert reapp.configured
  223. assert reapp.conf.broker_url == 'foo://bar'
  224. assert reapp._preconf['worker_agent'] == 'foo:Bar'
  225. def test_pending_configuration__update(self):
  226. with self.Celery(broker='foo://bar') as app:
  227. app.conf.update(
  228. task_default_delivery_mode=44,
  229. worker_agent='foo:Bar',
  230. )
  231. assert not app.configured
  232. assert app.conf.worker_agent == 'foo:Bar'
  233. assert app.conf.broker_url == 'foo://bar'
  234. assert app._preconf['worker_agent'] == 'foo:Bar'
  235. def test_pending_configuration__compat_settings(self):
  236. with self.Celery(broker='foo://bar', backend='foo') as app:
  237. app.conf.update(
  238. CELERY_ALWAYS_EAGER=4,
  239. CELERY_DEFAULT_DELIVERY_MODE=63,
  240. CELERYD_AGENT='foo:Barz',
  241. )
  242. assert app.conf.task_always_eager == 4
  243. assert app.conf.task_default_delivery_mode == 63
  244. assert app.conf.worker_agent == 'foo:Barz'
  245. assert app.conf.broker_url == 'foo://bar'
  246. assert app.conf.result_backend == 'foo'
  247. def test_pending_configuration__compat_settings_mixing(self):
  248. with self.Celery(broker='foo://bar', backend='foo') as app:
  249. app.conf.update(
  250. CELERY_ALWAYS_EAGER=4,
  251. CELERY_DEFAULT_DELIVERY_MODE=63,
  252. CELERYD_AGENT='foo:Barz',
  253. worker_consumer='foo:Fooz',
  254. )
  255. with pytest.raises(ImproperlyConfigured):
  256. assert app.conf.task_always_eager == 4
  257. def test_pending_configuration__django_settings(self):
  258. with self.Celery(broker='foo://bar', backend='foo') as app:
  259. app.config_from_object(DictAttribute(Bunch(
  260. CELERY_TASK_ALWAYS_EAGER=4,
  261. CELERY_TASK_DEFAULT_DELIVERY_MODE=63,
  262. CELERY_WORKER_AGENT='foo:Barz',
  263. CELERY_RESULT_SERIALIZER='pickle',
  264. )), namespace='CELERY')
  265. assert app.conf.result_serializer == 'pickle'
  266. assert app.conf.CELERY_RESULT_SERIALIZER == 'pickle'
  267. assert app.conf.task_always_eager == 4
  268. assert app.conf.task_default_delivery_mode == 63
  269. assert app.conf.worker_agent == 'foo:Barz'
  270. assert app.conf.broker_url == 'foo://bar'
  271. assert app.conf.result_backend == 'foo'
  272. def test_pending_configuration__compat_settings_mixing_new(self):
  273. with self.Celery(broker='foo://bar', backend='foo') as app:
  274. app.conf.update(
  275. task_always_eager=4,
  276. task_default_delivery_mode=63,
  277. worker_agent='foo:Barz',
  278. CELERYD_CONSUMER='foo:Fooz',
  279. CELERYD_AUTOSCALER='foo:Xuzzy',
  280. )
  281. with pytest.raises(ImproperlyConfigured):
  282. assert app.conf.worker_consumer == 'foo:Fooz'
  283. def test_pending_configuration__compat_settings_mixing_alt(self):
  284. with self.Celery(broker='foo://bar', backend='foo') as app:
  285. app.conf.update(
  286. task_always_eager=4,
  287. task_default_delivery_mode=63,
  288. worker_agent='foo:Barz',
  289. CELERYD_CONSUMER='foo:Fooz',
  290. worker_consumer='foo:Fooz',
  291. CELERYD_AUTOSCALER='foo:Xuzzy',
  292. worker_autoscaler='foo:Xuzzy'
  293. )
  294. def test_pending_configuration__setdefault(self):
  295. with self.Celery(broker='foo://bar') as app:
  296. assert not app.configured
  297. app.conf.setdefault('worker_agent', 'foo:Bar')
  298. assert not app.configured
  299. def test_pending_configuration__iter(self):
  300. with self.Celery(broker='foo://bar') as app:
  301. app.conf.worker_agent = 'foo:Bar'
  302. assert not app.configured
  303. assert list(keys(app.conf))
  304. assert app.configured
  305. assert 'worker_agent' in app.conf
  306. assert dict(app.conf)
  307. def test_pending_configuration__raises_ImproperlyConfigured(self):
  308. with self.Celery(set_as_current=False) as app:
  309. app.conf.worker_agent = 'foo://bar'
  310. app.conf.task_default_delivery_mode = 44
  311. app.conf.CELERY_ALWAYS_EAGER = 5
  312. with pytest.raises(ImproperlyConfigured):
  313. app.finalize()
  314. with self.Celery() as app:
  315. assert not self.app.conf.task_always_eager
  316. def test_repr(self):
  317. assert repr(self.app)
  318. def test_custom_task_registry(self):
  319. with self.Celery(tasks=self.app.tasks) as app2:
  320. assert app2.tasks is self.app.tasks
  321. def test_include_argument(self):
  322. with self.Celery(include=('foo', 'bar.foo')) as app:
  323. assert app.conf.include, ('foo' == 'bar.foo')
  324. def test_set_as_current(self):
  325. current = _state._tls.current_app
  326. try:
  327. app = self.Celery(set_as_current=True)
  328. assert _state._tls.current_app is app
  329. finally:
  330. _state._tls.current_app = current
  331. def test_current_task(self):
  332. @self.app.task
  333. def foo(shared=False):
  334. pass
  335. _state._task_stack.push(foo)
  336. try:
  337. assert self.app.current_task.name == foo.name
  338. finally:
  339. _state._task_stack.pop()
  340. def test_task_not_shared(self):
  341. with patch('celery.app.base.connect_on_app_finalize') as sh:
  342. @self.app.task(shared=False)
  343. def foo():
  344. pass
  345. sh.assert_not_called()
  346. def test_task_compat_with_filter(self):
  347. with self.Celery() as app:
  348. check = Mock()
  349. def filter(task):
  350. check(task)
  351. return task
  352. @app.task(filter=filter, shared=False)
  353. def foo():
  354. pass
  355. check.assert_called_with(foo)
  356. def test_task_with_filter(self):
  357. with self.Celery() as app:
  358. check = Mock()
  359. def filter(task):
  360. check(task)
  361. return task
  362. assert not _appbase.USING_EXECV
  363. @app.task(filter=filter, shared=False)
  364. def foo():
  365. pass
  366. check.assert_called_with(foo)
  367. def test_task_sets_main_name_MP_MAIN_FILE(self):
  368. from celery.utils import imports as _imports
  369. _imports.MP_MAIN_FILE = __file__
  370. try:
  371. with self.Celery('xuzzy') as app:
  372. @app.task
  373. def foo():
  374. pass
  375. assert foo.name == 'xuzzy.foo'
  376. finally:
  377. _imports.MP_MAIN_FILE = None
  378. def test_annotate_decorator(self):
  379. from celery.app.task import Task
  380. class adX(Task):
  381. def run(self, y, z, x):
  382. return y, z, x
  383. check = Mock()
  384. def deco(fun):
  385. def _inner(*args, **kwargs):
  386. check(*args, **kwargs)
  387. return fun(*args, **kwargs)
  388. return _inner
  389. self.app.conf.task_annotations = {
  390. adX.name: {'@__call__': deco}
  391. }
  392. adX.bind(self.app)
  393. assert adX.app is self.app
  394. i = adX()
  395. i(2, 4, x=3)
  396. check.assert_called_with(i, 2, 4, x=3)
  397. i.annotate()
  398. i.annotate()
  399. def test_apply_async_has__self__(self):
  400. @self.app.task(__self__='hello', shared=False)
  401. def aawsX(x, y):
  402. pass
  403. with pytest.raises(TypeError):
  404. aawsX.apply_async(())
  405. with pytest.raises(TypeError):
  406. aawsX.apply_async((2,))
  407. with patch('celery.app.amqp.AMQP.create_task_message') as create:
  408. with patch('celery.app.amqp.AMQP.send_task_message') as send:
  409. create.return_value = Mock(), Mock(), Mock(), Mock()
  410. aawsX.apply_async((4, 5))
  411. args = create.call_args[0][2]
  412. assert args, ('hello', 4 == 5)
  413. send.assert_called()
  414. def test_apply_async_adds_children(self):
  415. from celery._state import _task_stack
  416. @self.app.task(bind=True, shared=False)
  417. def a3cX1(self):
  418. pass
  419. @self.app.task(bind=True, shared=False)
  420. def a3cX2(self):
  421. pass
  422. _task_stack.push(a3cX1)
  423. try:
  424. a3cX1.push_request(called_directly=False)
  425. try:
  426. res = a3cX2.apply_async(add_to_parent=True)
  427. assert res in a3cX1.request.children
  428. finally:
  429. a3cX1.pop_request()
  430. finally:
  431. _task_stack.pop()
  432. def test_pickle_app(self):
  433. changes = {'THE_FOO_BAR': 'bars',
  434. 'THE_MII_MAR': 'jars'}
  435. self.app.conf.update(changes)
  436. saved = pickle.dumps(self.app)
  437. assert len(saved) < 2048
  438. restored = pickle.loads(saved)
  439. for key, value in items(changes):
  440. assert restored.conf[key] == value
  441. def test_worker_main(self):
  442. from celery.bin import worker as worker_bin
  443. class worker(worker_bin.worker):
  444. def execute_from_commandline(self, argv):
  445. return argv
  446. prev, worker_bin.worker = worker_bin.worker, worker
  447. try:
  448. ret = self.app.worker_main(argv=['--version'])
  449. assert ret == ['--version']
  450. finally:
  451. worker_bin.worker = prev
  452. def test_config_from_envvar(self):
  453. os.environ['CELERYTEST_CONFIG_OBJECT'] = 't.unit.app.test_app'
  454. self.app.config_from_envvar('CELERYTEST_CONFIG_OBJECT')
  455. assert self.app.conf.THIS_IS_A_KEY == 'this is a value'
  456. def assert_config2(self):
  457. assert self.app.conf.LEAVE_FOR_WORK
  458. assert self.app.conf.MOMENT_TO_STOP
  459. assert self.app.conf.CALL_ME_BACK == 123456789
  460. assert not self.app.conf.WANT_ME_TO
  461. assert self.app.conf.UNDERSTAND_ME
  462. def test_config_from_object__lazy(self):
  463. conf = ObjectConfig2()
  464. self.app.config_from_object(conf)
  465. assert self.app.loader._conf is unconfigured
  466. assert self.app._config_source is conf
  467. self.assert_config2()
  468. def test_config_from_object__force(self):
  469. self.app.config_from_object(ObjectConfig2(), force=True)
  470. assert self.app.loader._conf
  471. self.assert_config2()
  472. def test_config_from_object__compat(self):
  473. class Config(object):
  474. CELERY_ALWAYS_EAGER = 44
  475. CELERY_DEFAULT_DELIVERY_MODE = 30
  476. CELERY_TASK_PUBLISH_RETRY = False
  477. self.app.config_from_object(Config)
  478. assert self.app.conf.task_always_eager == 44
  479. assert self.app.conf.CELERY_ALWAYS_EAGER == 44
  480. assert not self.app.conf.task_publish_retry
  481. assert self.app.conf.task_default_routing_key == 'testcelery'
  482. def test_config_from_object__supports_old_names(self):
  483. class Config(object):
  484. task_always_eager = 45
  485. task_default_delivery_mode = 301
  486. self.app.config_from_object(Config())
  487. assert self.app.conf.CELERY_ALWAYS_EAGER == 45
  488. assert self.app.conf.task_always_eager == 45
  489. assert self.app.conf.CELERY_DEFAULT_DELIVERY_MODE == 301
  490. assert self.app.conf.task_default_delivery_mode == 301
  491. assert self.app.conf.task_default_routing_key == 'testcelery'
  492. def test_config_from_object__namespace_uppercase(self):
  493. class Config(object):
  494. CELERY_TASK_ALWAYS_EAGER = 44
  495. CELERY_TASK_DEFAULT_DELIVERY_MODE = 301
  496. self.app.config_from_object(Config(), namespace='CELERY')
  497. assert self.app.conf.task_always_eager == 44
  498. def test_config_from_object__namespace_lowercase(self):
  499. class Config(object):
  500. celery_task_always_eager = 44
  501. celery_task_default_delivery_mode = 301
  502. self.app.config_from_object(Config(), namespace='celery')
  503. assert self.app.conf.task_always_eager == 44
  504. def test_config_from_object__mixing_new_and_old(self):
  505. class Config(object):
  506. task_always_eager = 44
  507. worker_agent = 'foo:Agent'
  508. worker_consumer = 'foo:Consumer'
  509. beat_schedule = '/foo/schedule'
  510. CELERY_DEFAULT_DELIVERY_MODE = 301
  511. with pytest.raises(ImproperlyConfigured) as exc:
  512. self.app.config_from_object(Config(), force=True)
  513. assert exc.args[0].startswith('CELERY_DEFAULT_DELIVERY_MODE')
  514. assert 'task_default_delivery_mode' in exc.args[0]
  515. def test_config_from_object__mixing_old_and_new(self):
  516. class Config(object):
  517. CELERY_ALWAYS_EAGER = 46
  518. CELERYD_AGENT = 'foo:Agent'
  519. CELERYD_CONSUMER = 'foo:Consumer'
  520. CELERYBEAT_SCHEDULE = '/foo/schedule'
  521. task_default_delivery_mode = 301
  522. with pytest.raises(ImproperlyConfigured) as exc:
  523. self.app.config_from_object(Config(), force=True)
  524. assert exc.args[0].startswith('task_default_delivery_mode')
  525. assert 'CELERY_DEFAULT_DELIVERY_MODE' in exc.args[0]
  526. def test_config_from_cmdline(self):
  527. cmdline = ['task_always_eager=no',
  528. 'result_backend=/dev/null',
  529. 'worker_prefetch_multiplier=368',
  530. '.foobarstring=(string)300',
  531. '.foobarint=(int)300',
  532. 'database_engine_options=(dict){"foo": "bar"}']
  533. self.app.config_from_cmdline(cmdline, namespace='worker')
  534. assert not self.app.conf.task_always_eager
  535. assert self.app.conf.result_backend == '/dev/null'
  536. assert self.app.conf.worker_prefetch_multiplier == 368
  537. assert self.app.conf.worker_foobarstring == '300'
  538. assert self.app.conf.worker_foobarint == 300
  539. assert self.app.conf.database_engine_options == {'foo': 'bar'}
  540. def test_setting__broker_transport_options(self):
  541. _args = {'foo': 'bar', 'spam': 'baz'}
  542. self.app.config_from_object(Bunch())
  543. assert self.app.conf.broker_transport_options == \
  544. {'polling_interval': 0.1}
  545. self.app.config_from_object(Bunch(broker_transport_options=_args))
  546. assert self.app.conf.broker_transport_options == _args
  547. def test_Windows_log_color_disabled(self):
  548. self.app.IS_WINDOWS = True
  549. assert not self.app.log.supports_color(True)
  550. def test_WorkController(self):
  551. x = self.app.WorkController
  552. assert x.app is self.app
  553. def test_Worker(self):
  554. x = self.app.Worker
  555. assert x.app is self.app
  556. @pytest.mark.usefixtures('depends_on_current_app')
  557. def test_AsyncResult(self):
  558. x = self.app.AsyncResult('1')
  559. assert x.app is self.app
  560. r = loads(dumps(x))
  561. # not set as current, so ends up as default app after reduce
  562. assert r.app is current_app._get_current_object()
  563. def test_get_active_apps(self):
  564. assert list(_state._get_active_apps())
  565. app1 = self.Celery()
  566. appid = id(app1)
  567. assert app1 in _state._get_active_apps()
  568. app1.close()
  569. del(app1)
  570. gc.collect()
  571. # weakref removed from list when app goes out of scope.
  572. with pytest.raises(StopIteration):
  573. next(app for app in _state._get_active_apps() if id(app) == appid)
  574. def test_config_from_envvar_more(self, key='CELERY_HARNESS_CFG1'):
  575. assert not self.app.config_from_envvar(
  576. 'HDSAJIHWIQHEWQU', force=True, silent=True)
  577. with pytest.raises(ImproperlyConfigured):
  578. self.app.config_from_envvar(
  579. 'HDSAJIHWIQHEWQU', force=True, silent=False,
  580. )
  581. os.environ[key] = __name__ + '.object_config'
  582. assert self.app.config_from_envvar(key, force=True)
  583. assert self.app.conf['FOO'] == 1
  584. assert self.app.conf['BAR'] == 2
  585. os.environ[key] = 'unknown_asdwqe.asdwqewqe'
  586. with pytest.raises(ImportError):
  587. self.app.config_from_envvar(key, silent=False)
  588. assert not self.app.config_from_envvar(key, force=True, silent=True)
  589. os.environ[key] = __name__ + '.dict_config'
  590. assert self.app.config_from_envvar(key, force=True)
  591. assert self.app.conf['FOO'] == 10
  592. assert self.app.conf['BAR'] == 20
  593. @patch('celery.bin.celery.CeleryCommand.execute_from_commandline')
  594. def test_start(self, execute):
  595. self.app.start()
  596. execute.assert_called()
  597. @pytest.mark.parametrize('url,expected_fields', [
  598. ('pyamqp://', {
  599. 'hostname': 'localhost',
  600. 'userid': 'guest',
  601. 'password': 'guest',
  602. 'virtual_host': '/',
  603. }),
  604. ('pyamqp://:1978/foo', {
  605. 'port': 1978,
  606. 'virtual_host': 'foo',
  607. }),
  608. ('pyamqp:////value', {
  609. 'virtual_host': '/value',
  610. })
  611. ])
  612. def test_amqp_get_broker_info(self, url, expected_fields):
  613. info = self.app.connection(url).info()
  614. for key, expected_value in items(expected_fields):
  615. assert info[key] == expected_value
  616. def test_amqp_failover_strategy_selection(self):
  617. # Test passing in a string and make sure the string
  618. # gets there untouched
  619. self.app.conf.broker_failover_strategy = 'foo-bar'
  620. assert self.app.connection('amqp:////value') \
  621. .failover_strategy == 'foo-bar'
  622. # Try passing in None
  623. self.app.conf.broker_failover_strategy = None
  624. assert self.app.connection('amqp:////value') \
  625. .failover_strategy == itertools.cycle
  626. # Test passing in a method
  627. def my_failover_strategy(it):
  628. yield True
  629. self.app.conf.broker_failover_strategy = my_failover_strategy
  630. assert self.app.connection('amqp:////value') \
  631. .failover_strategy == my_failover_strategy
  632. def test_amqp_heartbeat_settings(self):
  633. # Test default broker_heartbeat value
  634. assert self.app.connection('amqp:////value') \
  635. .heartbeat == 0
  636. # Test passing heartbeat through app configuration
  637. self.app.conf.broker_heartbeat = 60
  638. assert self.app.connection('amqp:////value') \
  639. .heartbeat == 60
  640. # Test passing heartbeat as connection argument
  641. assert self.app.connection('amqp:////value', heartbeat=30) \
  642. .heartbeat == 30
  643. def test_after_fork(self):
  644. self.app._pool = Mock()
  645. self.app.on_after_fork = Mock(name='on_after_fork')
  646. self.app._after_fork()
  647. assert self.app._pool is None
  648. self.app.on_after_fork.send.assert_called_with(sender=self.app)
  649. self.app._after_fork()
  650. def test_global_after_fork(self):
  651. self.app._after_fork = Mock(name='_after_fork')
  652. _appbase._after_fork_cleanup_app(self.app)
  653. self.app._after_fork.assert_called_with()
  654. @patch('celery.app.base.logger')
  655. def test_after_fork_cleanup_app__raises(self, logger):
  656. self.app._after_fork = Mock(name='_after_fork')
  657. exc = self.app._after_fork.side_effect = KeyError()
  658. _appbase._after_fork_cleanup_app(self.app)
  659. logger.info.assert_called_with(
  660. 'after forker raised exception: %r', exc, exc_info=1)
  661. def test_ensure_after_fork__no_multiprocessing(self):
  662. prev, _appbase.register_after_fork = (
  663. _appbase.register_after_fork, None)
  664. try:
  665. self.app._after_fork_registered = False
  666. self.app._ensure_after_fork()
  667. assert self.app._after_fork_registered
  668. finally:
  669. _appbase.register_after_fork = prev
  670. def test_canvas(self):
  671. assert self.app._canvas.Signature
  672. def test_signature(self):
  673. sig = self.app.signature('foo', (1, 2))
  674. assert sig.app is self.app
  675. def test_timezone__none_set(self):
  676. self.app.conf.timezone = None
  677. tz = self.app.timezone
  678. assert tz == timezone.get_timezone('UTC')
  679. def test_uses_utc_timezone(self):
  680. self.app.conf.timezone = None
  681. assert self.app.uses_utc_timezone() is True
  682. self.app.conf.timezone = 'US/Eastern'
  683. assert self.app.uses_utc_timezone() is False
  684. self.app.conf.timezone = 'UTC'
  685. assert self.app.uses_utc_timezone() is True
  686. def test_compat_on_configure(self):
  687. _on_configure = Mock(name='on_configure')
  688. class CompatApp(Celery):
  689. def on_configure(self, *args, **kwargs):
  690. # on pypy3 if named on_configure the class function
  691. # will be called, instead of the mock defined above,
  692. # so we add the underscore.
  693. _on_configure(*args, **kwargs)
  694. with CompatApp(set_as_current=False) as app:
  695. app.loader = Mock()
  696. app.loader.conf = {}
  697. app._load_config()
  698. _on_configure.assert_called_with()
  699. def test_add_periodic_task(self):
  700. @self.app.task
  701. def add(x, y):
  702. pass
  703. assert not self.app.configured
  704. self.app.add_periodic_task(
  705. 10, self.app.signature('add', (2, 2)),
  706. name='add1', expires=3,
  707. )
  708. assert self.app._pending_periodic_tasks
  709. assert not self.app.configured
  710. sig2 = add.s(4, 4)
  711. assert self.app.configured
  712. self.app.add_periodic_task(20, sig2, name='add2', expires=4)
  713. assert 'add1' in self.app.conf.beat_schedule
  714. assert 'add2' in self.app.conf.beat_schedule
  715. def test_pool_no_multiprocessing(self):
  716. with mock.mask_modules('multiprocessing.util'):
  717. pool = self.app.pool
  718. assert pool is self.app._pool
  719. def test_bugreport(self):
  720. assert self.app.bugreport()
  721. def test_send_task__connection_provided(self):
  722. connection = Mock(name='connection')
  723. router = Mock(name='router')
  724. router.route.return_value = {}
  725. self.app.amqp = Mock(name='amqp')
  726. self.app.amqp.Producer.attach_mock(ContextMock(), 'return_value')
  727. self.app.send_task('foo', (1, 2), connection=connection, router=router)
  728. self.app.amqp.Producer.assert_called_with(
  729. connection, auto_declare=False)
  730. self.app.amqp.send_task_message.assert_called_with(
  731. self.app.amqp.Producer(), 'foo',
  732. self.app.amqp.create_task_message())
  733. def test_send_task_sent_event(self):
  734. class Dispatcher(object):
  735. sent = []
  736. def publish(self, type, fields, *args, **kwargs):
  737. self.sent.append((type, fields))
  738. conn = self.app.connection()
  739. chan = conn.channel()
  740. try:
  741. for e in ('foo_exchange', 'moo_exchange', 'bar_exchange'):
  742. chan.exchange_declare(e, 'direct', durable=True)
  743. chan.queue_declare(e, durable=True)
  744. chan.queue_bind(e, e, e)
  745. finally:
  746. chan.close()
  747. assert conn.transport_cls == 'memory'
  748. message = self.app.amqp.create_task_message(
  749. 'id', 'footask', (), {}, create_sent_event=True,
  750. )
  751. prod = self.app.amqp.Producer(conn)
  752. dispatcher = Dispatcher()
  753. self.app.amqp.send_task_message(
  754. prod, 'footask', message,
  755. exchange='moo_exchange', routing_key='moo_exchange',
  756. event_dispatcher=dispatcher,
  757. )
  758. assert dispatcher.sent
  759. assert dispatcher.sent[0][0] == 'task-sent'
  760. self.app.amqp.send_task_message(
  761. prod, 'footask', message, event_dispatcher=dispatcher,
  762. exchange='bar_exchange', routing_key='bar_exchange',
  763. )
  764. def test_select_queues(self):
  765. self.app.amqp = Mock(name='amqp')
  766. self.app.select_queues({'foo', 'bar'})
  767. self.app.amqp.queues.select.assert_called_with({'foo', 'bar'})
  768. def test_Beat(self):
  769. from celery.apps.beat import Beat
  770. beat = self.app.Beat()
  771. assert isinstance(beat, Beat)
  772. def test_registry_cls(self):
  773. class TaskRegistry(self.app.registry_cls):
  774. pass
  775. class CustomCelery(type(self.app)):
  776. registry_cls = TaskRegistry
  777. app = CustomCelery(set_as_current=False)
  778. assert isinstance(app.tasks, TaskRegistry)
  779. class test_defaults:
  780. def test_strtobool(self):
  781. for s in ('false', 'no', '0'):
  782. assert not defaults.strtobool(s)
  783. for s in ('true', 'yes', '1'):
  784. assert defaults.strtobool(s)
  785. with pytest.raises(TypeError):
  786. defaults.strtobool('unsure')
  787. class test_debugging_utils:
  788. def test_enable_disable_trace(self):
  789. try:
  790. _app.enable_trace()
  791. assert _state.app_or_default == _state._app_or_default_trace
  792. _app.disable_trace()
  793. assert _state.app_or_default == _state._app_or_default
  794. finally:
  795. _app.disable_trace()
  796. class test_pyimplementation:
  797. def test_platform_python_implementation(self):
  798. with mock.platform_pyimp(lambda: 'Xython'):
  799. assert pyimplementation() == 'Xython'
  800. def test_platform_jython(self):
  801. with mock.platform_pyimp():
  802. with mock.sys_platform('java 1.6.51'):
  803. assert 'Jython' in pyimplementation()
  804. def test_platform_pypy(self):
  805. with mock.platform_pyimp():
  806. with mock.sys_platform('darwin'):
  807. with mock.pypy_version((1, 4, 3)):
  808. assert 'PyPy' in pyimplementation()
  809. with mock.pypy_version((1, 4, 3, 'a4')):
  810. assert 'PyPy' in pyimplementation()
  811. def test_platform_fallback(self):
  812. with mock.platform_pyimp():
  813. with mock.sys_platform('darwin'):
  814. with mock.pypy_version():
  815. assert 'CPython' == pyimplementation()
  816. class test_shared_task:
  817. def test_registers_to_all_apps(self):
  818. with self.Celery('xproj', set_as_current=True) as xproj:
  819. xproj.finalize()
  820. @shared_task
  821. def foo():
  822. return 42
  823. @shared_task()
  824. def bar():
  825. return 84
  826. assert foo.app is xproj
  827. assert bar.app is xproj
  828. assert foo._get_current_object()
  829. with self.Celery('yproj', set_as_current=True) as yproj:
  830. assert foo.app is yproj
  831. assert bar.app is yproj
  832. @shared_task()
  833. def baz():
  834. return 168
  835. assert baz.app is yproj