diff options
-rw-r--r-- | cmd2/__init__.py | 1 | ||||
-rw-r--r-- | cmd2/argparse_completer.py | 55 | ||||
-rw-r--r-- | cmd2/argparse_custom.py | 56 | ||||
-rw-r--r-- | cmd2/cmd2.py | 6 | ||||
-rwxr-xr-x | examples/tab_autocompletion.py | 5 | ||||
-rw-r--r-- | tests/test_argparse_completer.py | 9 |
6 files changed, 70 insertions, 62 deletions
diff --git a/cmd2/__init__.py b/cmd2/__init__.py index d3c92636..3b149601 100644 --- a/cmd2/__init__.py +++ b/cmd2/__init__.py @@ -11,6 +11,7 @@ except DistributionNotFound: pass from .ansi import style +from .argparse_custom import Cmd2ArgParser, CompletionItem from .cmd2 import Cmd, Statement, EmptyStatement, categorize from .cmd2 import with_argument_list, with_argparser, with_argparser_and_unknown_args, with_category from .constants import DEFAULT_SHORTCUTS diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 1a8dd473..1a156f8f 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -62,66 +62,17 @@ import argparse import shutil from typing import List, Union +from . import cmd2 from . import utils from .ansi import ansi_safe_wcswidth from .argparse_custom import ATTR_SUPPRESS_TAB_HINT, ATTR_DESCRIPTIVE_COMPLETION_HEADER, ATTR_NARGS_RANGE -from .argparse_custom import ChoicesCallable, ATTR_CHOICES_CALLABLE +from .argparse_custom import ChoicesCallable, CompletionItem, ATTR_CHOICES_CALLABLE from .rl_utils import rl_force_redisplay # If no descriptive header is supplied, then this will be used instead DEFAULT_DESCRIPTIVE_HEADER = 'Description' -class CompletionItem(str): - """ - Completion item with descriptive text attached - - Returning this instead of a regular string for completion results will signal the - autocompleter to output the completions results in a table of completion tokens - with descriptions instead of just a table of tokens. - - For example, you'd see this: - TOKEN Description - MY_TOKEN Info about my token - SOME_TOKEN Info about some token - YET_ANOTHER Yet more info - - Instead of this: - TOKEN_ID SOME_TOKEN YET_ANOTHER - - This is especially useful if you want to complete ID numbers in a more - user-friendly manner. For example, you can provide this: - - ITEM_ID Item Name - 1 My item - 2 Another item - 3 Yet another item - - Instead of this: - 1 2 3 - - Example: - token = 1 - token_description = "My Item" - completion_item = CompletionItem(token, token_description) - """ - def __new__(cls, value: object, *args, **kwargs) -> str: - return super().__new__(cls, value) - - # noinspection PyUnusedLocal - def __init__(self, value: object, desc: str = '', *args, **kwargs) -> None: - """ - CompletionItem Initializer - - :param value: the value being tab completed - :param desc: description text to display - :param args: args for str __init__ - :param kwargs: kwargs for str __init__ - """ - super().__init__(*args, **kwargs) - self.description = desc - - # noinspection PyProtectedMember def is_potential_flag(token: str, parser: argparse.ArgumentParser) -> bool: """Determine if a token looks like a potential flag. Based on argparse._parse_optional().""" @@ -171,7 +122,7 @@ class AutoCompleter(object): self.needed = False self.variable = False - def __init__(self, parser: argparse.ArgumentParser, cmd2_app, *, + def __init__(self, parser: argparse.ArgumentParser, cmd2_app: cmd2.Cmd, *, token_start_index: int = 1) -> None: """ Create an AutoCompleter diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index b05ca6ed..5e3ed7f5 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -26,6 +26,56 @@ ATTR_SUPPRESS_TAB_HINT = 'suppress_tab_hint' ATTR_DESCRIPTIVE_COMPLETION_HEADER = 'desc_completion_header' +class CompletionItem(str): + """ + Completion item with descriptive text attached + + Returning this instead of a regular string for completion results will signal the + autocompleter to output the completions results in a table of completion tokens + with descriptions instead of just a table of tokens. + + For example, you'd see this: + TOKEN Description + MY_TOKEN Info about my token + SOME_TOKEN Info about some token + YET_ANOTHER Yet more info + + Instead of this: + TOKEN_ID SOME_TOKEN YET_ANOTHER + + This is especially useful if you want to complete ID numbers in a more + user-friendly manner. For example, you can provide this: + + ITEM_ID Item Name + 1 My item + 2 Another item + 3 Yet another item + + Instead of this: + 1 2 3 + + Example: + token = 1 + token_description = "My Item" + completion_item = CompletionItem(token, token_description) + """ + def __new__(cls, value: object, *args, **kwargs) -> str: + return super().__new__(cls, value) + + # noinspection PyUnusedLocal + def __init__(self, value: object, desc: str = '', *args, **kwargs) -> None: + """ + CompletionItem Initializer + + :param value: the value being tab completed + :param desc: description text to display + :param args: args for str __init__ + :param kwargs: kwargs for str __init__ + """ + super().__init__(*args, **kwargs) + self.description = desc + + class ChoicesCallable: """ Enables using a callable as the choices provider for an argparse argument. @@ -77,8 +127,10 @@ def _add_argument_wrapper(self, *args, :param choices_method: cmd2-app method that provides choices for this argument :param completer_function: tab-completion function that provides choices for this argument :param completer_method: cmd2-app tab-completion method that provides choices for this argument - :param suppress_tab_hint: when AutoCompleter has no choices to show during tab completion, it displays the current - argument's help text as a hint. Set this to True to suppress the hint. Defaults to False. + :param suppress_tab_hint: when AutoCompleter has no results to show during tab completion, it displays the current + argument's help text as a hint. Set this to True to suppress the hint. If this argument's + help text is set to argparse.SUPPRESS, then tab hints will not display regardless of the + value passed for suppress_tab_hint. Defaults to False. :param descriptive_header: if the provided choices are CompletionItems, then this header will display during tab completion. Defaults to None. diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index bfda6ae2..0d14d5ad 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -46,8 +46,7 @@ from . import ansi from . import constants from . import plugin from . import utils -from .argparse_completer import AutoCompleter, CompletionItem -from .argparse_custom import Cmd2ArgParser +from .argparse_custom import Cmd2ArgParser, CompletionItem from .clipboard import can_clip, get_paste_buffer, write_to_paste_buffer from .history import History, HistoryItem from .parsing import StatementParser, Statement, Macro, MacroArg, shlex_split @@ -1582,6 +1581,7 @@ class Cmd(cmd.Cmd): def _autocomplete_default(self, text: str, line: str, begidx: int, endidx: int, argparser: argparse.ArgumentParser) -> List[str]: """Default completion function for argparse commands""" + from .argparse_completer import AutoCompleter completer = AutoCompleter(argparser, self) tokens, _ = self.tokens_for_completion(line, begidx, endidx) return completer.complete_command(tokens, text, line, begidx, endidx) @@ -2643,6 +2643,7 @@ class Cmd(cmd.Cmd): # Check if this is a command with an argparse function func = self.cmd_func(command) if func and hasattr(func, 'argparser'): + from .argparse_completer import AutoCompleter completer = AutoCompleter(getattr(func, 'argparser'), self) matches = completer.complete_command_help(tokens[cmd_index:], text, line, begidx, endidx) @@ -2673,6 +2674,7 @@ class Cmd(cmd.Cmd): # If the command function uses argparse, then use argparse's help if func and hasattr(func, 'argparser'): + from .argparse_completer import AutoCompleter completer = AutoCompleter(getattr(func, 'argparser'), self) tokens = [args.command] + args.subcommand self.poutput(completer.format_help(tokens)) diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py index 4b13b5c3..a6d5487d 100755 --- a/examples/tab_autocompletion.py +++ b/examples/tab_autocompletion.py @@ -8,8 +8,7 @@ import functools from typing import List import cmd2 -from cmd2 import argparse_completer, utils -from cmd2.argparse_custom import Cmd2ArgParser +from cmd2 import utils, Cmd2ArgParser, CompletionItem actors = ['Mark Hamill', 'Harrison Ford', 'Carrie Fisher', 'Alec Guinness', 'Peter Mayhew', 'Anthony Daniels', 'Adam Driver', 'Daisy Ridley', 'John Boyega', 'Oscar Isaac', @@ -116,7 +115,7 @@ class TabCompleteExample(cmd2.Cmd): for movie_id in utils.natural_sort(self.MOVIE_DATABASE_IDS): if movie_id in self.MOVIE_DATABASE: movie_entry = self.MOVIE_DATABASE[movie_id] - completions_with_desc.append(argparse_completer.CompletionItem(movie_id, movie_entry['title'])) + completions_with_desc.append(CompletionItem(movie_id, movie_entry['title'])) # Mark that we already sorted the matches self.matches_sorted = True diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py index a3fa6a59..6e092619 100644 --- a/tests/test_argparse_completer.py +++ b/tests/test_argparse_completer.py @@ -9,9 +9,8 @@ from typing import List import pytest import cmd2 -from cmd2 import with_argparser -from cmd2.argparse_completer import CompletionItem, is_potential_flag, DEFAULT_DESCRIPTIVE_HEADER -from cmd2.argparse_custom import Cmd2ArgParser +from cmd2 import with_argparser, Cmd2ArgParser, CompletionItem +from cmd2.argparse_completer import is_potential_flag, DEFAULT_DESCRIPTIVE_HEADER from cmd2.utils import StdSim, basic_complete from .conftest import run_cmd, complete_tester @@ -208,6 +207,7 @@ def test_autocomp_positional_choices_completion(ac_app, pos, text, completions): 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('flag, text, completions', [ ('-f', '', completions_from_function), ('--function', 'f', ['function', 'fairly']), @@ -222,6 +222,7 @@ def test_autocomp_flag_completers(ac_app, flag, text, completions): 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, text, completions', [ (1, '', completions_from_function), (1, 'c', ['completions', 'complete']), @@ -338,6 +339,7 @@ Hint: ''' + def test_autocomp_hint_no_help(ac_app, capsys): text = '' line = 'hint foo {}'.format(text) @@ -503,6 +505,7 @@ Hint: # # Since -- appeared before the -- being completed, nothing should be completed # assert complete_tester(text, line, begidx, endidx, cmd2_app) is None + def test_is_potential_flag(): parser = Cmd2ArgParser() |