diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2019-07-10 16:22:47 -0400 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2019-07-10 16:22:47 -0400 |
commit | 782bab855b0ec1d1b9728a322b932f99e6fb3849 (patch) | |
tree | 7f02393cd0e0b3352badfc1b376c88e299e64e3d | |
parent | 60e294ee645e092b8713d5cf60f8797d3685416a (diff) | |
download | cmd2-git-782bab855b0ec1d1b9728a322b932f99e6fb3849.tar.gz |
Fixed some double-dash handling logic added unit tests
-rw-r--r-- | cmd2/argparse_completer.py | 26 | ||||
-rw-r--r-- | tests/test_argparse_completer.py | 102 |
2 files changed, 72 insertions, 56 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 27e7df25..d7ffc180 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -288,19 +288,31 @@ class AutoCompleter(object): if token_index >= len(tokens) - 1: is_last_token = True - # If a remainder action is found, force all future tokens to go to that + # If we're in a positional REMAINDER arg, force all future tokens to go to that if pos_arg_state is not None and pos_arg_state.is_remainder: consume_positional_argument() continue + + # If we're in a flag REMAINDER arg, force all future tokens to go to that until a double dash is hit elif flag_arg_state is not None and flag_arg_state.is_remainder: skip_remaining_flags = True if token == '--': - # End this flag and don't allow any more flags flag_arg_state = None else: consume_flag_argument() continue + # Handle '--' which tells argparse all remaining arguments are non-flags + elif token == '--' and not skip_remaining_flags: + if is_last_token: + # Exit loop and see if -- can be completed into a flag + break + else: + # End the current flag + flag_arg_state = None + skip_remaining_flags = True + continue + current_is_positional = False # Are we consuming flag arguments? @@ -319,16 +331,8 @@ class AutoCompleter(object): self._positional_actions[next_pos_arg_index].nargs == argparse.REMAINDER: skip_remaining_flags = True - # Handle '--' which tells argparse all remaining arguments are non-flags - if token == '--' and not skip_remaining_flags: - if is_last_token: - # Exit loop and see if -- can be completed into a flag - break - else: - skip_remaining_flags = True - # At this point we're no longer consuming flag arguments. Is the current argument a potential flag? - elif is_potential_flag(token, self._parser) and not skip_remaining_flags: + if is_potential_flag(token, self._parser) and not skip_remaining_flags: # Reset flag arg state but not positional tracking because flags can be # interspersed anywhere between positionals flag_arg_state = None diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index 404ba10b..cf4ac7b3 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -19,6 +19,11 @@ static_choices_list = ['static', 'choices', 'stop', 'here'] choices_from_function = ['choices', 'function', 'chatty', 'smith'] choices_from_method = ['choices', 'method', 'most', 'improved'] +set_value_choices = ['set', 'value', 'choices'] +one_or_more_choices = ['one', 'or', 'more', 'choices'] +optional_choices = ['optional', 'choices'] +remainder_choices = ['remainder', 'choices'] + completions_from_function = ['completions', 'function', 'fairly', 'complete'] completions_from_method = ['completions', 'method', 'missed', 'spot'] @@ -185,11 +190,13 @@ class AutoCompleteTester(cmd2.Cmd): # Flag args for nargs command nargs_parser.add_argument("--set_value", help="a flag with a set value for nargs", nargs=2, - choices=static_choices_list) + choices=set_value_choices) nargs_parser.add_argument("--one_or_more", help="a flag wanting one or more args", nargs=argparse.ONE_OR_MORE, - choices=static_choices_list) + choices=one_or_more_choices) + nargs_parser.add_argument("--optional", help="a flag with an optional value", nargs=argparse.OPTIONAL, + choices=optional_choices) nargs_parser.add_argument("--remainder", help="a flag wanting remaining", nargs=argparse.REMAINDER, - choices=static_choices_list) + choices=remainder_choices) @with_argparser(nargs_parser) def do_nargs(self, args: argparse.Namespace) -> None: @@ -218,48 +225,6 @@ def ac_app(): return app -@pytest.mark.parametrize('args, completions', [ - # Flag with nargs = 2 - ('--set_value', static_choices_list), - ('--set_value static', ['choices', 'stop', 'here']), - ('--set_value static choices', []), - - # Using the flag again will reset the choices available - ('--set_value static choices --set_value', static_choices_list), - - # Flag with nargs = ONE_OR_MORE - ('--one_or_more', static_choices_list), - ('--one_or_more static', ['choices', 'stop', 'here']), - ('--one_or_more static choices', ['stop', 'here']), - - # No more flags after a double dash - ('-- --one_or_more static choices', []), - - # Flag with nargs = REMAINDER - ('--remainder', static_choices_list), - ('--remainder static ', ['choices', 'stop', 'here']), - - # No more flags can appear after a REMAINDER flag) - ('--remainder static --set_value', ['choices', 'stop', 'here']), - - # Double dash ends a remainder flag - ('--remainder static --', []) -]) -def test_autcomp_nargs(ac_app, args, completions): - text = '' - line = 'nargs {} {}'.format(args, text) - endidx = len(line) - begidx = endidx - len(text) - - first_match = complete_tester(text, line, begidx, endidx, ac_app) - if completions: - assert first_match is not None - else: - assert first_match is None - - assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) - - @pytest.mark.parametrize('command', [ 'music', 'music create', @@ -465,6 +430,53 @@ def test_completion_items(ac_app, num_aliases, show_description): assert ('help' in ac_app.display_matches[0]) == show_description +@pytest.mark.parametrize('args, completions', [ + # Flag with nargs = 2 + ('--set_value', set_value_choices), + ('--set_value set', ['value', 'choices']), + ('--set_value set value choices', []), + + # Another flag can't start until all expected args are filled out + ('--set_value --one_or_more', set_value_choices), + + # Using the flag again will reset the choices available + ('--set_value set value --set_value', set_value_choices), + + # Flag with nargs = ONE_OR_MORE + ('--one_or_more', one_or_more_choices), + ('--one_or_more one', ['or', 'more', 'choices']), + + # Flag with nargs = REMAINDER + ('--remainder', remainder_choices), + ('--remainder remainder ', ['choices ']), + + # No more flags can appear after a REMAINDER flag) + ('--remainder choices --set_value', ['remainder ']), + + # Double dash ends the current flag (even if all expected args aren't entered) + ('--set_value --', []), + + # Double dash ends a REMAINDER flag + ('--remainder remainder --', []), + + # No more flags after a double dash + ('-- --one_or_more ', []), +]) +def test_autcomp_nargs(ac_app, args, completions): + text = '' + line = 'nargs {} {}'.format(args, text) + endidx = len(line) + begidx = endidx - len(text) + + first_match = complete_tester(text, line, begidx, endidx, ac_app) + if completions: + assert first_match is not None + else: + assert first_match is None + + assert ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key) + + def test_completion_items_default_header(ac_app): from cmd2.argparse_completer import DEFAULT_DESCRIPTIVE_HEADER |