summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd2/argparse_completer.py30
-rw-r--r--tests/test_argparse_completer.py88
2 files changed, 73 insertions, 45 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index 4300365c..9fa502db 100644
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -60,7 +60,6 @@ How to supply completion choice lists or functions for sub-commands:
import argparse
import os
-from argparse import SUPPRESS
from typing import List, Union
from . import utils
@@ -470,7 +469,7 @@ class AutoCompleter(object):
if flag_action.dest in consumed_arg_values else []
completion_results = self._complete_for_arg(flag_action, text, line, begidx, endidx, consumed)
if not completion_results:
- self._print_action_help(flag_action)
+ self._print_arg_hint(flag_action)
elif len(completion_results) > 1:
completion_results = self._format_completions(flag_action, completion_results)
@@ -481,7 +480,7 @@ class AutoCompleter(object):
consumed = consumed_arg_values[pos_name] if pos_name in consumed_arg_values else []
completion_results = self._complete_for_arg(pos_action, text, line, begidx, endidx, consumed)
if not completion_results:
- self._print_action_help(pos_action)
+ self._print_arg_hint(pos_action)
elif len(completion_results) > 1:
completion_results = self._format_completions(pos_action, completion_results)
@@ -611,33 +610,34 @@ class AutoCompleter(object):
return []
- def _print_action_help(self, action: argparse.Action) -> None:
+ def _print_arg_hint(self, arg: argparse.Action) -> None:
+ """Print argument hint to the terminal when tab completion results in no results"""
# is parameter hinting disabled globally?
if not self._tab_for_arg_help:
return
# is parameter hinting disabled for this parameter?
- suppress_hint = getattr(action, ATTR_SUPPRESS_TAB_HINT, False)
+ suppress_hint = getattr(arg, ATTR_SUPPRESS_TAB_HINT, False)
if suppress_hint:
return
- if action.option_strings:
- flags = ', '.join(action.option_strings)
+ # Check if this is a flag
+ if arg.option_strings:
+ flags = ', '.join(arg.option_strings)
param = ''
- if action.nargs is None or action.nargs != 0:
- param += ' ' + str(action.dest).upper()
+ if arg.nargs is None or arg.nargs != 0:
+ param += ' ' + str(arg.dest).upper()
prefix = '{}{}'.format(flags, param)
+
+ # Otherwise this is a positional
else:
- if action.dest != SUPPRESS:
- prefix = '{}'.format(str(action.dest).upper())
- else:
- prefix = ''
+ prefix = '{}'.format(str(arg.dest).upper())
- if action.help is None:
+ if not arg.help or arg.help == argparse.SUPPRESS:
help_text = ''
else:
- help_text = action.help
+ help_text = arg.help
# is there anything to print for this parameter?
if not prefix and not help_text:
diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py
index 4f1ed44a..f2aa40a3 100644
--- a/tests/test_argparse_completer.py
+++ b/tests/test_argparse_completer.py
@@ -31,30 +31,42 @@ class AutoCompleteTester(cmd2.Cmd):
def __init__(self):
super().__init__()
+ ############################################################################################################
+ # Begin code related to testing help and subcommand completion
+ ############################################################################################################
+ basic_parser = Cmd2ArgParser(prog='basic')
+ basic_subparsers = basic_parser.add_subparsers()
+
+
+ ############################################################################################################
+ # Begin code related to testing choices, choices_function, and choices_method parameters
+ ############################################################################################################
def choices_method(self) -> List[str]:
"""Method that provides choices"""
return choices_from_method
- # Basic command with no subcommands that exercises tab completing choices from various sources
- basic_parser = Cmd2ArgParser()
- basic_parser.add_argument("-n", "--no_choices", help="a flag with no choices")
- basic_parser.add_argument("-l", "--choices_list", help="a flag populated with a choices list",
- choices=static_choices_list)
- basic_parser.add_argument("-f", "--choices_function", help="a flag populated with a choices function",
- choices_function=choices_function)
- basic_parser.add_argument("-m", "--choices_method", help="a flag populated with a choices method",
- choices_method=choices_method)
-
- basic_parser.add_argument("no_choice_pos", help="a positional with no choices")
- basic_parser.add_argument("choices_list_pos", help="a positional populated with a choices list",
- choices=static_choices_list)
- basic_parser.add_argument("choices_function_pos", help="a positional populated with a choices function",
- choices_function=choices_function)
- basic_parser.add_argument("choices_method_pos", help="a positional populated with a choices method",
- choices_method=choices_method)
-
- @with_argparser(basic_parser)
- def do_basic(self, args: argparse.Namespace) -> None:
+ choices_parser = Cmd2ArgParser()
+
+ # Flags args for choices command
+ choices_parser.add_argument("-n", "--no_choices", help="a flag with no choices")
+ choices_parser.add_argument("-l", "--choices_list", help="a flag populated with a choices list",
+ choices=static_choices_list)
+ choices_parser.add_argument("-f", "--choices_function", help="a flag populated with a choices function",
+ choices_function=choices_function)
+ choices_parser.add_argument("-m", "--choices_method", help="a flag populated with a choices method",
+ choices_method=choices_method)
+
+ # Positional args for choices command
+ choices_parser.add_argument("no_choice_pos", help="a positional with no choices")
+ choices_parser.add_argument("choices_list_pos", help="a positional populated with a choices list",
+ choices=static_choices_list)
+ choices_parser.add_argument("choices_function_pos", help="a positional populated with a choices function",
+ choices_function=choices_function)
+ choices_parser.add_argument("choices_method_pos", help="a positional populated with a choices method",
+ choices_method=choices_method)
+
+ @with_argparser(choices_parser)
+ def do_choices(self, args: argparse.Namespace) -> None:
pass
@@ -66,14 +78,14 @@ def ac_app():
def test_help_basic(ac_app):
- out1, err1 = run_cmd(ac_app, 'basic -h')
- out2, err2 = run_cmd(ac_app, 'help basic')
+ out1, err1 = run_cmd(ac_app, 'choices -h')
+ out2, err2 = run_cmd(ac_app, 'help choices')
assert out1 == out2
def test_autocomp_flags(ac_app):
text = '-'
- line = 'basic {}'.format(text)
+ line = 'choices {}'.format(text)
endidx = len(line)
begidx = endidx - len(text)
@@ -83,9 +95,9 @@ def test_autocomp_flags(ac_app):
'--no_choices', '-f', '-h', '-l', '-m', '-n']
-def test_autcomp_hint(ac_app, capsys):
+def test_autcomp_flag_hint(ac_app, capsys):
text = ''
- line = 'basic -n {}'.format(text)
+ line = 'choices -n {}'.format(text)
endidx = len(line)
begidx = endidx - len(text)
@@ -96,9 +108,9 @@ def test_autcomp_hint(ac_app, capsys):
assert 'a flag with no choices' in out
-def test_autcomp_flag_comp(ac_app):
+def test_autcomp_flag_completion(ac_app):
text = '--ch'
- line = 'basic {}'.format(text)
+ line = 'choices {}'.format(text)
endidx = len(line)
begidx = endidx - len(text)
@@ -106,7 +118,6 @@ def test_autcomp_flag_comp(ac_app):
assert first_match is not None and \
ac_app.completion_matches == ['--choices_function', '--choices_list', '--choices_method']
-
@pytest.mark.parametrize('flag, completions', [
('-l', static_choices_list),
('--choices_list', static_choices_list),
@@ -115,9 +126,26 @@ def test_autcomp_flag_comp(ac_app):
('-m', choices_from_method),
('--choices_method', choices_from_method),
])
-def test_autocomp_flags_choices(ac_app, flag, completions):
+def test_autocomp_flag_choices_completion(ac_app, flag, completions):
+ text = ''
+ line = 'choices {} {}'.format(flag, text)
+ endidx = len(line)
+ begidx = endidx - len(text)
+
+ first_match = complete_tester(text, line, begidx, endidx, ac_app)
+ assert first_match is not None and \
+ ac_app.completion_matches == sorted(completions, key=ac_app.matches_sort_key)
+
+
+@pytest.mark.parametrize('pos, completions', [
+ (2, static_choices_list), # choices_list_pos
+ (3, choices_from_function), # choices_function_pos
+ (4, choices_from_method), # choices_method_pos
+])
+def test_autocomp_positional_choices_completion(ac_app, pos, completions):
+ # Test completions of positional arguments by generating a line were preceding positionals are already filled
text = ''
- line = 'basic {} {}'.format(flag, text)
+ line = 'choices {} {}'.format('foo ' * (pos - 1), text)
endidx = len(line)
begidx = endidx - len(text)