From e3ac73b6fbe2d996070b90833c23a3b336cd4ac6 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Tue, 14 Jul 2020 06:36:34 +0200 Subject: [PATCH] FIX: -A and --args should behave the same. (#6223) * FIX: -A and --args should behave the same. Closes #4558 The added test should fail like this, without this patch: AssertionError: assert 't.unit.bin.test_celery.APP' == 'worker' * Remove dead code. * Feel that this should be kept untouched. --- celery/bin/base.py | 12 ++++++------ celery/bin/celery.py | 10 +++++++--- t/unit/bin/test_base.py | 6 +++--- t/unit/bin/test_celery.py | 18 ++++++++++++++++++ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/celery/bin/base.py b/celery/bin/base.py index 08a0f67f24d..4f69a64f9ab 100644 --- a/celery/bin/base.py +++ b/celery/bin/base.py @@ -474,7 +474,7 @@ def prepare_parser(self, parser): return parser def setup_app_from_commandline(self, argv): - preload_options = self.parse_preload_options(argv) + preload_options, remaining_options = self.parse_preload_options(argv) quiet = preload_options.get('quiet') if quiet is not None: self.quiet = quiet @@ -510,18 +510,18 @@ def setup_app_from_commandline(self, argv): elif self.app is None: self.app = self.get_app(loader=loader) if self.enable_config_from_cmdline: - argv = self.process_cmdline_config(argv) + remaining_options = self.process_cmdline_config(remaining_options) else: self.app = Celery(fixups=[]) self._handle_user_preload_options(argv) - return argv + return remaining_options def _handle_user_preload_options(self, argv): user_preload = tuple(self.app.user_options['preload'] or ()) if user_preload: - user_options = self._parse_preload_options(argv, user_preload) + user_options, _ = self._parse_preload_options(argv, user_preload) signals.user_preload_options.send( sender=self, app=self.app, options=user_options, ) @@ -550,8 +550,8 @@ def _parse_preload_options(self, args, options): args = [arg for arg in args if arg not in ('-h', '--help')] parser = self.Parser() self.add_compat_options(parser, options) - namespace, _ = parser.parse_known_args(args) - return vars(namespace) + namespace, unknown_args = parser.parse_known_args(args) + return vars(namespace), unknown_args def add_append_opt(self, acc, opt, value): default = opt.default or [] diff --git a/celery/bin/celery.py b/celery/bin/celery.py index a715f6e479c..e1001777950 100644 --- a/celery/bin/celery.py +++ b/celery/bin/celery.py @@ -435,6 +435,13 @@ def on_usage_error(self, exc, command=None): ))) def _relocate_args_from_start(self, argv, index=0): + """Move options to the end of args. + + This rewrites: + -l debug worker -c 3 + to: + worker -c 3 -l debug + """ if argv: rest = [] while index < len(argv): @@ -466,9 +473,6 @@ def _relocate_args_from_start(self, argv, index=0): # we assume the first argument in argv[i:] is the command # name. return argv[index:] + rest - # if there are no more arguments then the last arg in rest' - # must be the command. - [rest.pop()] + rest return [] def prepare_prog_name(self, name): diff --git a/t/unit/bin/test_base.py b/t/unit/bin/test_base.py index f33d2b831f8..a4fcfb80239 100644 --- a/t/unit/bin/test_base.py +++ b/t/unit/bin/test_base.py @@ -353,7 +353,7 @@ class TestCommand(Command): def add_preload_arguments(self, parser): parser.add_argument('-s', action='store', dest='silent') cmd = TestCommand() - acc = cmd.parse_preload_options(['-s', 'yes']) + acc, _ = cmd.parse_preload_options(['-s', 'yes']) assert acc.get('silent') == 'yes' def test_parse_preload_options_with_equals_and_append(self): @@ -363,7 +363,7 @@ class TestCommand(Command): def add_preload_arguments(self, parser): parser.add_argument('--zoom', action='append', default=[]) cmd = Command() - acc = cmd.parse_preload_options(['--zoom=1', '--zoom=2']) + acc, _ = cmd.parse_preload_options(['--zoom=1', '--zoom=2']) assert acc, {'zoom': ['1' == '2']} @@ -371,6 +371,6 @@ def test_parse_preload_options_without_equals_and_append(self): cmd = Command() opt = Option('--zoom', action='append', default=[]) cmd.preload_options = (opt,) - acc = cmd.parse_preload_options(['--zoom', '1', '--zoom', '2']) + acc, _ = cmd.parse_preload_options(['--zoom', '1', '--zoom', '2']) assert acc, {'zoom': ['1' == '2']} diff --git a/t/unit/bin/test_celery.py b/t/unit/bin/test_celery.py index 33d5ad2acb1..ba6eaaa93db 100644 --- a/t/unit/bin/test_celery.py +++ b/t/unit/bin/test_celery.py @@ -16,6 +16,13 @@ from celery.platforms import EX_FAILURE, EX_OK, EX_USAGE +class MyApp(object): + user_options = {'preload': None} + + +APP = MyApp() # <-- Used by test_short_and_long_arguments_be_the_same + + class test__main__: def test_main(self): @@ -204,6 +211,17 @@ def test_handle_argv(self): x.handle_argv('celery', ['start', 'foo']) x.execute.assert_called_with('start', ['start', 'foo']) + def test_short_and_long_arguments_be_the_same(self): + for arg in "--app", "-A": + appstr = '.'.join([__name__, 'APP']) + x = CeleryCommand(app=self.app) + x.execute = Mock() + with pytest.raises(SystemExit): + x.execute_from_commandline(['celery', arg, appstr, 'worker']) + assert x.execute.called + assert x.execute.call_args[0] + assert x.execute.call_args[0][0] == "worker" + def test_execute(self): x = CeleryCommand(app=self.app) Help = x.commands['help'] = Mock()