test_base.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. from __future__ import absolute_import, unicode_literals
  2. import os
  3. import pytest
  4. from case import Mock, mock, patch
  5. from celery.bin.base import (
  6. Command,
  7. Option,
  8. Extensions,
  9. HelpFormatter,
  10. )
  11. from celery.five import bytes_if_py2
  12. from celery.utils.objects import Bunch
  13. class MyApp(object):
  14. user_options = {'preload': None}
  15. APP = MyApp() # <-- Used by test_with_custom_app
  16. class MockCommand(Command):
  17. mock_args = ('arg1', 'arg2', 'arg3')
  18. def parse_options(self, prog_name, arguments, command=None):
  19. options = Bunch(foo='bar', prog_name=prog_name)
  20. return options, self.mock_args
  21. def run(self, *args, **kwargs):
  22. return args, kwargs
  23. class test_Extensions:
  24. def test_load(self):
  25. with patch('pkg_resources.iter_entry_points') as iterep:
  26. with patch('celery.utils.imports.symbol_by_name') as symbyname:
  27. ep = Mock()
  28. ep.name = 'ep'
  29. ep.module_name = 'foo'
  30. ep.attrs = ['bar', 'baz']
  31. iterep.return_value = [ep]
  32. cls = symbyname.return_value = Mock()
  33. register = Mock()
  34. e = Extensions('unit', register)
  35. e.load()
  36. symbyname.assert_called_with('foo:bar')
  37. register.assert_called_with(cls, name='ep')
  38. with patch('celery.utils.imports.symbol_by_name') as symbyname:
  39. symbyname.side_effect = SyntaxError()
  40. with patch('warnings.warn') as warn:
  41. e.load()
  42. warn.assert_called()
  43. with patch('celery.utils.imports.symbol_by_name') as symbyname:
  44. symbyname.side_effect = KeyError('foo')
  45. with pytest.raises(KeyError):
  46. e.load()
  47. class test_HelpFormatter:
  48. def test_format_epilog(self):
  49. f = HelpFormatter()
  50. assert f.format_epilog('hello')
  51. assert not f.format_epilog('')
  52. def test_format_description(self):
  53. f = HelpFormatter()
  54. assert f.format_description('hello')
  55. class test_Command:
  56. def test_get_options(self):
  57. cmd = Command()
  58. cmd.option_list = (1, 2, 3)
  59. assert cmd.get_options() == (1, 2, 3)
  60. def test_custom_description(self):
  61. class C(Command):
  62. description = 'foo'
  63. c = C()
  64. assert c.description == 'foo'
  65. def test_register_callbacks(self):
  66. c = Command(on_error=8, on_usage_error=9)
  67. assert c.on_error == 8
  68. assert c.on_usage_error == 9
  69. def test_run_raises_UsageError(self):
  70. cb = Mock()
  71. c = Command(on_usage_error=cb)
  72. c.verify_args = Mock()
  73. c.run = Mock()
  74. exc = c.run.side_effect = c.UsageError('foo', status=3)
  75. assert c() == exc.status
  76. cb.assert_called_with(exc)
  77. c.verify_args.assert_called_with(())
  78. def test_default_on_usage_error(self):
  79. cmd = Command()
  80. cmd.handle_error = Mock()
  81. exc = Exception()
  82. cmd.on_usage_error(exc)
  83. cmd.handle_error.assert_called_with(exc)
  84. def test_verify_args_missing(self):
  85. c = Command()
  86. def run(a, b, c):
  87. pass
  88. c.run = run
  89. with pytest.raises(c.UsageError):
  90. c.verify_args((1,))
  91. c.verify_args((1, 2, 3))
  92. def test_run_interface(self):
  93. with pytest.raises(NotImplementedError):
  94. Command().run()
  95. @patch('sys.stdout')
  96. def test_early_version(self, stdout):
  97. cmd = Command()
  98. with pytest.raises(SystemExit):
  99. cmd.early_version(['--version'])
  100. def test_execute_from_commandline(self, app):
  101. cmd = MockCommand(app=app)
  102. args1, kwargs1 = cmd.execute_from_commandline() # sys.argv
  103. assert args1 == cmd.mock_args
  104. assert kwargs1['foo'] == 'bar'
  105. assert kwargs1.get('prog_name')
  106. args2, kwargs2 = cmd.execute_from_commandline(['foo']) # pass list
  107. assert args2 == cmd.mock_args
  108. assert kwargs2['foo'] == 'bar'
  109. assert kwargs2['prog_name'] == 'foo'
  110. def test_with_bogus_args(self, app):
  111. with mock.stdouts() as (_, stderr):
  112. cmd = MockCommand(app=app)
  113. cmd.supports_args = False
  114. with pytest.raises(SystemExit):
  115. cmd.execute_from_commandline(argv=['--bogus'])
  116. assert stderr.getvalue()
  117. assert 'Unrecognized' in stderr.getvalue()
  118. def test_with_custom_config_module(self, app):
  119. prev = os.environ.pop('CELERY_CONFIG_MODULE', None)
  120. try:
  121. cmd = MockCommand(app=app)
  122. cmd.setup_app_from_commandline(['--config=foo.bar.baz'])
  123. assert os.environ.get('CELERY_CONFIG_MODULE') == 'foo.bar.baz'
  124. finally:
  125. if prev:
  126. os.environ['CELERY_CONFIG_MODULE'] = prev
  127. else:
  128. os.environ.pop('CELERY_CONFIG_MODULE', None)
  129. def test_with_custom_broker(self, app):
  130. prev = os.environ.pop('CELERY_BROKER_URL', None)
  131. try:
  132. cmd = MockCommand(app=app)
  133. cmd.setup_app_from_commandline(['--broker=xyzza://'])
  134. assert os.environ.get('CELERY_BROKER_URL') == 'xyzza://'
  135. finally:
  136. if prev:
  137. os.environ['CELERY_BROKER_URL'] = prev
  138. else:
  139. os.environ.pop('CELERY_BROKER_URL', None)
  140. def test_with_custom_app(self, app):
  141. cmd = MockCommand(app=app)
  142. appstr = '.'.join([__name__, 'APP'])
  143. cmd.setup_app_from_commandline(['--app=%s' % (appstr,),
  144. '--loglevel=INFO'])
  145. assert cmd.app is APP
  146. cmd.setup_app_from_commandline(['-A', appstr,
  147. '--loglevel=INFO'])
  148. assert cmd.app is APP
  149. def test_setup_app_sets_quiet(self, app):
  150. cmd = MockCommand(app=app)
  151. cmd.setup_app_from_commandline(['-q'])
  152. assert cmd.quiet
  153. cmd2 = MockCommand(app=app)
  154. cmd2.setup_app_from_commandline(['--quiet'])
  155. assert cmd2.quiet
  156. def test_setup_app_sets_chdir(self, app):
  157. with patch('os.chdir') as chdir:
  158. cmd = MockCommand(app=app)
  159. cmd.setup_app_from_commandline(['--workdir=/opt'])
  160. chdir.assert_called_with('/opt')
  161. def test_setup_app_sets_loader(self, app):
  162. prev = os.environ.get('CELERY_LOADER')
  163. try:
  164. cmd = MockCommand(app=app)
  165. cmd.setup_app_from_commandline(['--loader=X.Y:Z'])
  166. assert os.environ['CELERY_LOADER'] == 'X.Y:Z'
  167. finally:
  168. if prev is not None:
  169. os.environ['CELERY_LOADER'] = prev
  170. def test_setup_app_no_respect(self, app):
  171. cmd = MockCommand(app=app)
  172. cmd.respects_app_option = False
  173. with patch('celery.bin.base.Celery') as cp:
  174. cmd.setup_app_from_commandline(['--app=x.y:z'])
  175. cp.assert_called()
  176. def test_setup_app_custom_app(self, app):
  177. cmd = MockCommand(app=app)
  178. app = cmd.app = Mock()
  179. app.user_options = {'preload': None}
  180. cmd.setup_app_from_commandline([])
  181. assert cmd.app == app
  182. def test_find_app_suspects(self, app):
  183. cmd = MockCommand(app=app)
  184. assert cmd.find_app('t.unit.bin.proj.app')
  185. assert cmd.find_app('t.unit.bin.proj')
  186. assert cmd.find_app('t.unit.bin.proj:hello')
  187. assert cmd.find_app('t.unit.bin.proj.hello')
  188. assert cmd.find_app('t.unit.bin.proj.app:app')
  189. assert cmd.find_app('t.unit.bin.proj.app.app')
  190. with pytest.raises(AttributeError):
  191. cmd.find_app('t.unit.bin')
  192. with pytest.raises(AttributeError):
  193. cmd.find_app(__name__)
  194. def test_ask(self, app, patching):
  195. try:
  196. input = patching('celery.bin.base.input')
  197. except AttributeError:
  198. input = patching('builtins.input')
  199. cmd = MockCommand(app=app)
  200. input.return_value = 'yes'
  201. assert cmd.ask('q', ('yes', 'no'), 'no') == 'yes'
  202. input.return_value = 'nop'
  203. assert cmd.ask('q', ('yes', 'no'), 'no') == 'no'
  204. def test_host_format(self, app):
  205. cmd = MockCommand(app=app)
  206. with patch('celery.utils.nodenames.gethostname') as hn:
  207. hn.return_value = 'blacktron.example.com'
  208. assert cmd.host_format('') == ''
  209. assert (cmd.host_format('celery@%h') ==
  210. 'celery@blacktron.example.com')
  211. assert cmd.host_format('celery@%d') == 'celery@example.com'
  212. assert cmd.host_format('celery@%n') == 'celery@blacktron'
  213. def test_say_chat_quiet(self, app):
  214. cmd = MockCommand(app=app)
  215. cmd.quiet = True
  216. assert cmd.say_chat('<-', 'foo', 'foo') is None
  217. def test_say_chat_show_body(self, app):
  218. cmd = MockCommand(app=app)
  219. cmd.out = Mock()
  220. cmd.show_body = True
  221. cmd.say_chat('->', 'foo', 'body')
  222. cmd.out.assert_called_with('body')
  223. def test_say_chat_no_body(self, app):
  224. cmd = MockCommand(app=app)
  225. cmd.out = Mock()
  226. cmd.show_body = False
  227. cmd.say_chat('->', 'foo', 'body')
  228. @pytest.mark.usefixtures('depends_on_current_app')
  229. def test_with_cmdline_config(self, app):
  230. cmd = MockCommand(app=app)
  231. cmd.enable_config_from_cmdline = True
  232. cmd.namespace = 'worker'
  233. rest = cmd.setup_app_from_commandline(argv=[
  234. '--loglevel=INFO', '--',
  235. 'broker.url=amqp://broker.example.com',
  236. '.prefetch_multiplier=100'])
  237. assert cmd.app.conf.broker_url == 'amqp://broker.example.com'
  238. assert cmd.app.conf.worker_prefetch_multiplier == 100
  239. assert rest == ['--loglevel=INFO']
  240. cmd.app = None
  241. cmd.get_app = Mock(name='get_app')
  242. cmd.get_app.return_value = app
  243. app.user_options['preload'] = [
  244. Option('--foo', action='store_true'),
  245. ]
  246. cmd.setup_app_from_commandline(argv=[
  247. '--foo', '--loglevel=INFO', '--',
  248. 'broker.url=amqp://broker.example.com',
  249. '.prefetch_multiplier=100'])
  250. assert cmd.app is cmd.get_app()
  251. def test_preparse_options__required_short(self, app):
  252. cmd = MockCommand(app=app)
  253. with pytest.raises(ValueError):
  254. cmd.preparse_options(
  255. ['a', '-f'], [Option('-f', action='store')])
  256. def test_preparse_options__longopt_whitespace(self, app):
  257. cmd = MockCommand(app=app)
  258. cmd.preparse_options(
  259. ['a', '--foo', 'val'], [Option('--foo', action='store')])
  260. def test_preparse_options__shortopt_store_true(self, app):
  261. cmd = MockCommand(app=app)
  262. cmd.preparse_options(
  263. ['a', '--foo'], [Option('--foo', action='store_true')])
  264. def test_get_default_app(self, app, patching):
  265. patching('celery._state.get_current_app')
  266. cmd = MockCommand(app=app)
  267. from celery._state import get_current_app
  268. assert cmd._get_default_app() is get_current_app()
  269. def test_set_colored(self, app):
  270. cmd = MockCommand(app=app)
  271. cmd.colored = 'foo'
  272. assert cmd.colored == 'foo'
  273. def test_set_no_color(self, app):
  274. cmd = MockCommand(app=app)
  275. cmd.no_color = False
  276. _ = cmd.colored # noqa
  277. cmd.no_color = True
  278. assert not cmd.colored.enabled
  279. def test_find_app(self, app):
  280. cmd = MockCommand(app=app)
  281. with patch('celery.utils.imports.symbol_by_name') as sbn:
  282. from types import ModuleType
  283. x = ModuleType(bytes_if_py2('proj'))
  284. def on_sbn(*args, **kwargs):
  285. def after(*args, **kwargs):
  286. x.app = 'quick brown fox'
  287. x.__path__ = None
  288. return x
  289. sbn.side_effect = after
  290. return x
  291. sbn.side_effect = on_sbn
  292. x.__path__ = [True]
  293. assert cmd.find_app('proj') == 'quick brown fox'
  294. def test_parse_preload_options_shortopt(self):
  295. cmd = Command()
  296. cmd.preload_options = (Option('-s', action='store', dest='silent'),)
  297. acc = cmd.parse_preload_options(['-s', 'yes'])
  298. assert acc.get('silent') == 'yes'
  299. def test_parse_preload_options_with_equals_and_append(self):
  300. cmd = Command()
  301. opt = Option('--zoom', action='append', default=[])
  302. cmd.preload_options = (opt,)
  303. acc = cmd.parse_preload_options(['--zoom=1', '--zoom=2'])
  304. assert acc, {'zoom': ['1' == '2']}
  305. def test_parse_preload_options_without_equals_and_append(self):
  306. cmd = Command()
  307. opt = Option('--zoom', action='append', default=[])
  308. cmd.preload_options = (opt,)
  309. acc = cmd.parse_preload_options(['--zoom', '1', '--zoom', '2'])
  310. assert acc, {'zoom': ['1' == '2']}