diff --git a/tackle/parser.py b/tackle/parser.py index bb722ae7b..0ea72b5ab 100644 --- a/tackle/parser.py +++ b/tackle/parser.py @@ -539,7 +539,7 @@ def evaluate_args( TODO: This needs to be re-thought. Right now we parse the inputs without regard for the types of the argument mapping. What could be better is if we know the types of the arg mapping ahead of time and then try to assemble the most logical mapping - afterwards. So if the mapping consists of a [str, list], then the if the first + afterwards. So if the mapping consists of a [str, list], then if the first args are strs then we can ignore the list part. Right now it would just join all the strings together if they are part of last arg mapping. TODO: Improve error handling. @@ -548,6 +548,9 @@ def evaluate_args( - Single types then into unions? - If type cannot be infered (ie Any) then do ast as literal """ + # Flag to inform if we are at end of args and need to pop the rest + pop_all: bool = False + hook_args: list = Hook.__fields__['args'].default for i, v in enumerate(args): # Iterate over the input args @@ -560,19 +563,23 @@ def evaluate_args( elif Hook.__fields__[hook_args[i]].type_ == str: # Was parsed on spaces so reconstructed. value = ' '.join(args[i:]) + pop_all = True elif Hook.__fields__[hook_args[i]].type_ in (bool, float, int): # TODO: Incomplete value = args[i] elif isinstance(Hook.__fields__[hook_args[i]], list): # If list then all the remaining items value = args[i:] + pop_all = True elif isinstance(v, str): # Make assumption the rest of the args can be reconstructed as above value = ' '.join(args[i:]) + pop_all = True elif isinstance(v, (bool, float, int)): # TODO: Incomplete if len(args[i:]) > 1: value = args[i:] + pop_all = True else: value = args[i] else: @@ -585,7 +592,10 @@ def evaluate_args( ) from None value = args[i] hook_dict[hook_args[i]] = value - args.pop(0) + if pop_all: + args.clear() + else: + args.pop(0) return else: # The hooks arguments are indexed @@ -899,13 +909,22 @@ def find_run_hook_method( elif arg == 'help': # Exit 0 run_help(context=context, hook=hook) - elif 'args' in hook.__fields__: - evaluate_args(args, arg_dict, Hook=hook, context=context) - else: + # elif 'args' not in hook.__fields__: + elif hook.__fields__['args'].default == []: # noqa + hook_name = hook.identifier.split('.')[-1] + if hook_name == '': + msg = "default hook" + else: + msg = f"hook='{hook_name}'" raise exceptions.UnknownInputArgumentException( - "Can't find the ", context=context + f"The {msg} was supplied the arg='{arg}' and does not take any " + "arguments. Exiting.", + context=context, ) from None + if 'args' in hook.__fields__: + evaluate_args(args, arg_dict, Hook=hook, context=context) + try: Hook = hook(**kwargs, **arg_dict) except ValidationError as e: diff --git a/tests/cli/fixtures/help.yaml b/tests/cli/fixtures/help.yaml deleted file mode 100644 index 5f155c606..000000000 --- a/tests/cli/fixtures/help.yaml +++ /dev/null @@ -1,38 +0,0 @@ -with_help<-: - help: Do stuff to things - things: - type: str - description: The things - render_by_default: true - exec<-: - p->: print {{things}} - - -with_help_methods<-: - help: Do stuff to things - things: - type: str - description: The things - render_by_default: true - exec<-: - p->: print {{things}} - - a_method_without_help<-: - foo->: print bar - - a_method_with_help<-: - help: Stuff n tings - foo->: print bar - -no_help<-: - no_show_str: foo - no_show_list: - - foo - - bar - - things: - type: str - description: The things - render_by_default: true - exec<-: - p->: print {{things}} diff --git a/tests/cli/fixtures/input.yaml b/tests/cli/fixtures/input.yaml new file mode 100644 index 000000000..08436e73b --- /dev/null +++ b/tests/cli/fixtures/input.yaml @@ -0,0 +1,3 @@ +<-: + foo: str + args: ['foo'] diff --git a/tests/cli/fixtures/tackle-help.yaml b/tests/cli/fixtures/tackle-help.yaml deleted file mode 100644 index 47b4b8685..000000000 --- a/tests/cli/fixtures/tackle-help.yaml +++ /dev/null @@ -1,2 +0,0 @@ -__import: - - robcxyz/tackle-make diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py index 21c1fbb52..92fafce60 100644 --- a/tests/cli/test_cli.py +++ b/tests/cli/test_cli.py @@ -49,11 +49,8 @@ def test_cli_parse_args_empty(mocker, change_curdir_fixtures): @pytest.mark.parametrize("input_string", PRINTS) -def test_cli_parse_args_print_option( - mocker, change_curdir_fixtures, capsys, input_string -): +def test_cli_parse_args_print_option(change_curdir_fixtures, capsys, input_string): """When no arg is given we should find the closest tackle file.""" - mocker.patch("tackle.main.tackle", autospec=True, return_value={}) main([input_string]) assert '{"stuff": "things"}' in capsys.readouterr().out diff --git a/tests/cli/test_cli_args.py b/tests/cli/test_cli_args.py index 227065c6e..0314ffffa 100644 --- a/tests/cli/test_cli_args.py +++ b/tests/cli/test_cli_args.py @@ -3,6 +3,7 @@ COMMANDS = [ (['global-kwarg.yaml', '--key_a', '"stuff and things"'], 'stuff and things'), + (['input.yaml', 'bar', 'baz', '-pf', 'yaml'], 'foo: bar baz'), ] @@ -10,7 +11,8 @@ def test_cli_commands(change_curdir_fixtures, command, expected_output, capsys): """Assert output comes out of cli.""" main(command) - assert expected_output in capsys.readouterr().out + output = capsys.readouterr().out + assert expected_output in output def test_cli_command_find_in_parent(chdir_fixture, capsys): diff --git a/tests/parser/functions/test_functions_cli.py b/tests/parser/functions/test_functions_cli.py index c884c9bc3..034fbe99a 100644 --- a/tests/parser/functions/test_functions_cli.py +++ b/tests/parser/functions/test_functions_cli.py @@ -37,3 +37,10 @@ def test_function_model_extraction_in_directory( output = capsys.readouterr().out assert json.loads(output) == expected_output + + +def test_function_cli_multiple_args(change_curdir_fixtures, capsys): + main(['supplied-args-param-str.yaml', 'foo', 'bing', 'bang', '-pf', 'yaml']) + output = capsys.readouterr().out + + assert 'bar: bing bang' in output diff --git a/tests/parser/functions/test_functions_exceptions.py b/tests/parser/functions/test_functions_exceptions.py index b70999786..bbf1e04ff 100644 --- a/tests/parser/functions/test_functions_exceptions.py +++ b/tests/parser/functions/test_functions_exceptions.py @@ -16,7 +16,7 @@ def test_parser_functions_exceptions_try_in_default(chdir): def test_parser_functions_raises_unknown_arg(change_curdir_fixtures): - with pytest.raises(exceptions.UnknownArgumentException): + with pytest.raises(exceptions.UnknownInputArgumentException): tackle("cli-default-hook-no-context.yaml", 'NOT_HERE') @@ -31,7 +31,7 @@ def test_parser_functions_raises_unknown_flags(change_curdir_fixtures): def test_parser_functions_raises_unknown_arg_hook(change_curdir_fixtures): - with pytest.raises(exceptions.UnknownArgumentException): + with pytest.raises(exceptions.UnknownInputArgumentException): tackle("cli-hook-no-context.yaml", 'run', 'NOT_HERE') diff --git a/tests/parser/functions/test_functions_help.py b/tests/parser/functions/test_functions_help.py index bbb550bfc..08137f228 100644 --- a/tests/parser/functions/test_functions_help.py +++ b/tests/parser/functions/test_functions_help.py @@ -105,5 +105,5 @@ def test_function_cli_tackle_help_with_arg(chdir): def test_function_cli_tackle_arg_error(chdir): """Check that when we give a bad key, even with help we get an exception.""" chdir(os.path.join('fixtures', 'a-tackle')) - with pytest.raises(exceptions.UnknownArgumentException): + with pytest.raises(exceptions.UnknownInputArgumentException): tackle('bad-key', 'help')