diff --git a/requirements.txt b/requirements.txt --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,7 @@ flask kombu psycopg2 +pyyaml vcversioner # test dependencies diff --git a/swh/scheduler/cli.py b/swh/scheduler/cli.py --- a/swh/scheduler/cli.py +++ b/swh/scheduler/cli.py @@ -51,14 +51,14 @@ def pretty_print_list(list, indent=0): """Pretty-print a list""" - return ''.join('%s%s\n' % (' ' * indent, item) for item in list) + return ''.join('%s%r\n' % (' ' * indent, item) for item in list) def pretty_print_dict(dict, indent=0): """Pretty-print a list""" - return ''.join('%s%s: %s\n' % + return ''.join('%s%s: %r\n' % (' ' * indent, click.style(key, bold=True), value) - for key, value in dict.items()) + for key, value in sorted(dict.items())) def pretty_print_run(run, indent=4): @@ -75,8 +75,8 @@ >>> task = { ... 'id': 1234, ... 'arguments': { - ... 'args': ['foo', 'bar'], - ... 'kwargs': {'key': 'value'}, + ... 'args': ['foo', 'bar', True], + ... 'kwargs': {'key': 'value', 'key2': 42}, ... }, ... 'current_interval': datetime.timedelta(hours=1), ... 'next_run': datetime.datetime(2019, 2, 21, 13, 52, 35, 407818), @@ -92,10 +92,12 @@ Type: test_task Policy: oneshot Args: - foo - bar + 'foo' + 'bar' + True Keyword args: - key: value + key: 'value' + key2: 42 >>> print(click.unstyle(pretty_print_task(task, full=True))) Task 1234 @@ -106,10 +108,12 @@ Status: next_run_not_scheduled Priority:\x20 Args: - foo - bar + 'foo' + 'bar' + True Keyword args: - key: value + key: 'value' + key2: 42 """ next_run = arrow.get(task['next_run']) diff --git a/swh/scheduler/cli_utils.py b/swh/scheduler/cli_utils.py --- a/swh/scheduler/cli_utils.py +++ b/swh/scheduler/cli_utils.py @@ -3,19 +3,43 @@ # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information +import click +import yaml + + +def parse_argument(option): + try: + return yaml.load(option) + except Exception: + raise click.ClickException('Invalid argument: {}'.format(option)) + def parse_options(options): - """Parses options from a CLI and turns it into Python args and kwargs. + """Parses options from a CLI as YAML and turns it into Python + args and kwargs. >>> parse_options([]) ([], {}) >>> parse_options(['foo', 'bar']) (['foo', 'bar'], {}) - >>> parse_options(['foo=bar']) + >>> parse_options(['[foo, bar]']) + ([['foo', 'bar']], {}) + >>> parse_options(['"foo"', '"bar"']) + (['foo', 'bar'], {}) + >>> parse_options(['foo="bar"']) ([], {'foo': 'bar'}) - >>> parse_options(['foo', 'bar=baz']) + >>> parse_options(['"foo"', 'bar="baz"']) (['foo'], {'bar': 'baz'}) + >>> parse_options(['42', 'bar=False']) + ([42], {'bar': False}) + >>> parse_options(['42', 'bar=false']) + ([42], {'bar': False}) + >>> parse_options(['42', '"foo']) + Traceback (most recent call last): + ... + click.exceptions.ClickException: Invalid argument: "foo """ - args = [x for x in options if '=' not in x] - kw = dict(x.split('=', 1) for x in options if '=' in x) + kw_pairs = [x.split('=', 1) for x in options if '=' in x] + args = [parse_argument(x) for x in options if '=' not in x] + kw = {k: parse_argument(v) for (k, v) in kw_pairs} return (args, kw) diff --git a/swh/scheduler/tests/test_cli.py b/swh/scheduler/tests/test_cli.py --- a/swh/scheduler/tests/test_cli.py +++ b/swh/scheduler/tests/test_cli.py @@ -61,7 +61,7 @@ Args: \['arg1', 'arg2'\] Keyword args: - key: value + key: 'value' Task 2 Next run: just now \(.*\) @@ -71,7 +71,7 @@ Args: \['arg3', 'arg4'\] Keyword args: - key: value + key: 'value' '''.lstrip() assert result.exit_code == 0, result.output @@ -99,10 +99,10 @@ Type: swh-test-ping Policy: oneshot Args: - arg1 - arg2 + 'arg1' + 'arg2' Keyword args: - key: value + key: 'value' '''.lstrip() assert result.exit_code == 0, result.output @@ -124,10 +124,10 @@ Type: swh-test-ping Policy: recurring Args: - arg1 - arg2 + 'arg1' + 'arg2' Keyword args: - key: value + key: 'value' '''.lstrip() assert result.exit_code == 0, result.output @@ -167,7 +167,7 @@ Policy: oneshot Args: Keyword args: - key: value + key: 'value' '''.lstrip() assert result.exit_code == 0, result.output