From 191f94abda1c4d565ea5b2dd1bd66e346db3b51b Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Wed, 12 Feb 2020 15:47:29 -0500 Subject: Overhauling tab completion examples --- README.md | 10 +- docs/features/argument_processing.rst | 3 +- docs/features/completion.rst | 10 +- examples/basic_completion.py | 105 +++++++++++++ examples/python_scripting.py | 1 + examples/tab_autocompletion.py | 268 ---------------------------------- examples/tab_completion.py | 81 ---------- 7 files changed, 117 insertions(+), 361 deletions(-) create mode 100755 examples/basic_completion.py delete mode 100755 examples/tab_autocompletion.py delete mode 100755 examples/tab_completion.py diff --git a/README.md b/README.md index c7849243..334d10ab 100755 --- a/README.md +++ b/README.md @@ -175,14 +175,14 @@ Instructions for implementing each feature follow. - See the [paged_output.py](https://github.com/python-cmd2/cmd2/blob/master/examples/paged_output.py) example for a simple use case - See the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example for a more full-featured use case - `flag_based_complete` helper method for tab completion based on a particular flag preceding the token being completed - - See the [tab_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_completion.py) example for a demonstration of how to use this feature + - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use this feature - `index_based_complete` helper method for tab completion based on a fixed position in the input string - - See the [tab_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_completion.py) example for a demonstration of how to use this feature + - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use this feature - `basic_complete` helper method for tab completion against a list - `delimiter_complete` helper method for tab completion against a list but each match is split on a delimiter - - See the [tab_autocompletion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py) example for a demonstration of how to use this feature - - `cmd2` in combination with `argparse` also provide several advanced capabilities for automatic tab-completion - - See the [tab_autocompletion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py) example for more info + - See the [basic_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/basic_completion.py) example for a demonstration of how to use this feature + - `cmd2` in combination with `argparse` also provide several advanced capabilities for automatic tab completion + - See the [argparse_completion.py](https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_completion.py) example for more info - Multi-line commands diff --git a/docs/features/argument_processing.rst b/docs/features/argument_processing.rst index 9d98ea93..39a39804 100644 --- a/docs/features/argument_processing.rst +++ b/docs/features/argument_processing.rst @@ -328,11 +328,10 @@ You may add multiple layers of subcommands for your command. ``cmd2`` will automatically traverse and tab-complete subcommands for all commands using argparse. -See the subcommands_ and tab_autocompletion_ example to learn more about how to +See the subcommands_ example to learn more about how to use subcommands in your ``cmd2`` application. .. _subcommands: https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py -.. _tab_autocompletion: https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py Argparse Extensions diff --git a/docs/features/completion.rst b/docs/features/completion.rst index 14a98caf..9103b543 100644 --- a/docs/features/completion.rst +++ b/docs/features/completion.rst @@ -17,7 +17,7 @@ implementing the ``do_foo`` method. To enable path completion for the ``foo`` command, then add a line of code similar to the following to your class which inherits from ``cmd2.Cmd``:: - complete_foo = self.path_complete + complete_foo = cmd2.Cmd.path_complete This will effectively define the ``complete_foo`` readline completer method in your class and make it utilize the same path completion logic as the built-in @@ -47,9 +47,9 @@ parameters to ``argparse.ArgumentParser.add_argument()`` - ``completer_function`` / ``completer_method`` See the arg_decorators_ or colors_ example for a demonstration of how to -use the ``choices`` parameter. See the tab_autocompletion_ example for a +use the ``choices`` parameter. See the argparse_completion_ example for a demonstration of how to use the ``choices_function`` and ``choices_method`` -parameters. See the arg_decorators_ or tab_autocompletion_ example for a +parameters. See the arg_decorators_ or argparse_completion_ example for a demonstration of how to use the ``completer_method`` parameter. When tab-completing flags and/or argument values for a ``cmd2`` command using @@ -60,7 +60,7 @@ displayed to help the user. .. _arg_decorators: https://github.com/python-cmd2/cmd2/blob/master/examples/arg_decorators.py .. _colors: https://github.com/python-cmd2/cmd2/blob/master/examples/colors.py -.. _tab_autocompletion: https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocompletion.py +.. _argparse_completion: https://github.com/python-cmd2/cmd2/blob/master/examples/argparse_completion.py CompletionItem For Providing Extra Context @@ -76,5 +76,5 @@ or ``completion_method``. .. autoclass:: cmd2.argparse_custom.CompletionItem :members: -See the tab_autocompletion_ example or the implementation of the built-in +See the argparse_completion_ example or the implementation of the built-in **set** command for demonstration of how this is used. diff --git a/examples/basic_completion.py b/examples/basic_completion.py new file mode 100755 index 00000000..4baec16c --- /dev/null +++ b/examples/basic_completion.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# coding=utf-8 +""" +A simple example demonstrating how to enable tab completion by assigning a completer function to do_* commands. +This also demonstrates capabilities of the following completer methods included with cmd2: +- flag_based_complete +- index_based_complete +- delimiter_completer + +For an example enabling tab completion with argparse, see argparse_completion.py +""" +import argparse +import functools + +import cmd2 + +# List of strings used with completion functions +food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] +sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] + +file_strs = \ + [ + '/home/user/file.db', + '/home/user/file space.db', + '/home/user/another.db', + '/home/other user/maps.db', + '/home/other user/tests.db' + ] + + +class TabCompleteExample(cmd2.Cmd): + """ Example cmd2 application where we a base command which has a couple subcommands.""" + def __init__(self): + super().__init__() + + # The add_item command uses flag_based_complete + add_item_parser = argparse.ArgumentParser() + add_item_group = add_item_parser.add_mutually_exclusive_group() + add_item_group.add_argument('-f', '--food', help='Adds food item') + add_item_group.add_argument('-s', '--sport', help='Adds sport item') + add_item_group.add_argument('-o', '--other', help='Adds other item') + + @cmd2.with_argparser(add_item_parser) + def do_add_item(self, args): + """Add item command help""" + if args.food: + add_item = args.food + elif args.sport: + add_item = args.sport + elif args.other: + add_item = args.other + else: + add_item = 'no items' + + self.poutput("You added {}".format(add_item)) + + # Add flag-based tab-completion to add_item command + def complete_add_item(self, text, line, begidx, endidx): + flag_dict = \ + { + # Tab-complete food items after -f and --food flags in command line + '-f': food_item_strs, + '--food': food_item_strs, + + # Tab-complete sport items after -s and --sport flags in command line + '-s': sport_item_strs, + '--sport': sport_item_strs, + + # Tab-complete using path_complete function after -o and --other flags in command line + '-o': self.path_complete, + '--other': self.path_complete, + } + + return self.flag_based_complete(text, line, begidx, endidx, flag_dict=flag_dict) + + # The list_item command uses index_based_complete + @cmd2.with_argument_list + def do_list_item(self, args): + """List item command help""" + self.poutput("You listed {}".format(args)) + + # Add index-based tab-completion to list_item command + def complete_list_item(self, text, line, begidx, endidx): + index_dict = \ + { + 1: food_item_strs, # Tab-complete food items at index 1 in command line + 2: sport_item_strs, # Tab-complete sport items at index 2 in command line + 3: self.path_complete, # Tab-complete using path_complete function at index 3 in command line + } + + return self.index_based_complete(text, line, begidx, endidx, index_dict=index_dict) + + # The file_list command uses delimiter_complete + def do_file_list(self, statement: cmd2.Statement): + """List files entered on command line""" + self.poutput("You selected: {}".format(statement.args)) + + # Use a partialmethod to set arguments to delimiter_complete + complete_file_list = functools.partialmethod(cmd2.Cmd.delimiter_complete, match_against=file_strs, delimiter='/') + + +if __name__ == '__main__': + import sys + app = TabCompleteExample() + sys.exit(app.cmdloop()) diff --git a/examples/python_scripting.py b/examples/python_scripting.py index fc23c562..198e784d 100755 --- a/examples/python_scripting.py +++ b/examples/python_scripting.py @@ -87,6 +87,7 @@ class CmdLineApp(cmd2.Cmd): # Enable tab completion for cd command def complete_cd(self, text, line, begidx, endidx): + # Tab complete only directories return self.path_complete(text, line, begidx, endidx, path_filter=os.path.isdir) dir_parser = argparse.ArgumentParser() diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py deleted file mode 100755 index 3561f968..00000000 --- a/examples/tab_autocompletion.py +++ /dev/null @@ -1,268 +0,0 @@ -#!/usr/bin/env python3 -# coding=utf-8 -""" -A example usage of the AutoCompleter -""" -import argparse -import functools -from typing import List - -import cmd2 -from cmd2 import utils, Cmd2ArgumentParser, CompletionItem - -actors = ['Mark Hamill', 'Harrison Ford', 'Carrie Fisher', 'Alec Guinness', 'Peter Mayhew', - 'Anthony Daniels', 'Adam Driver', 'Daisy Ridley', 'John Boyega', 'Oscar Isaac', - 'Lupita Nyong\'o', 'Andy Serkis', 'Liam Neeson', 'Ewan McGregor', 'Natalie Portman', - 'Jake Lloyd', 'Hayden Christensen', 'Christopher Lee'] - - -def query_actors() -> List[str]: - """Simulating a function that queries and returns a completion values""" - return actors - - -class TabCompleteExample(cmd2.Cmd): - """ Example cmd2 application where we a base command which has a couple subcommands.""" - - CAT_AUTOCOMPLETE = 'AutoComplete Examples' - - def __init__(self): - super().__init__() - - # For mocking a data source for the example commands - ratings_types = ['G', 'PG', 'PG-13', 'R', 'NC-17'] - show_ratings = ['TV-Y', 'TV-Y7', 'TV-G', 'TV-PG', 'TV-14', 'TV-MA'] - static_list_directors = ['J. J. Abrams', 'Irvin Kershner', 'George Lucas', 'Richard Marquand', - 'Rian Johnson', 'Gareth Edwards'] - USER_MOVIE_LIBRARY = ['ROGUE1', 'SW_EP04', 'SW_EP05'] - MOVIE_DATABASE_IDS = ['SW_EP1', 'SW_EP02', 'SW_EP03', 'ROGUE1', 'SW_EP04', - 'SW_EP05', 'SW_EP06', 'SW_EP07', 'SW_EP08', 'SW_EP09'] - MOVIE_DATABASE = {'SW_EP04': {'title': 'Star Wars: Episode IV - A New Hope', - 'rating': 'PG', - 'director': ['George Lucas'], - 'actor': ['Mark Hamill', 'Harrison Ford', 'Carrie Fisher', - 'Alec Guinness', 'Peter Mayhew', 'Anthony Daniels'] - }, - 'SW_EP05': {'title': 'Star Wars: Episode V - The Empire Strikes Back', - 'rating': 'PG', - 'director': ['Irvin Kershner'], - 'actor': ['Mark Hamill', 'Harrison Ford', 'Carrie Fisher', - 'Alec Guinness', 'Peter Mayhew', 'Anthony Daniels'] - }, - 'SW_EP06': {'title': 'Star Wars: Episode VI - Return of the Jedi', - 'rating': 'PG', - 'director': ['Richard Marquand'], - 'actor': ['Mark Hamill', 'Harrison Ford', 'Carrie Fisher', - 'Alec Guinness', 'Peter Mayhew', 'Anthony Daniels'] - }, - 'SW_EP1': {'title': 'Star Wars: Episode I - The Phantom Menace', - 'rating': 'PG', - 'director': ['George Lucas'], - 'actor': ['Liam Neeson', 'Ewan McGregor', 'Natalie Portman', 'Jake Lloyd'] - }, - 'SW_EP02': {'title': 'Star Wars: Episode II - Attack of the Clones', - 'rating': 'PG', - 'director': ['George Lucas'], - 'actor': ['Liam Neeson', 'Ewan McGregor', 'Natalie Portman', - 'Hayden Christensen', 'Christopher Lee'] - }, - 'SW_EP03': {'title': 'Star Wars: Episode III - Revenge of the Sith', - 'rating': 'PG-13', - 'director': ['George Lucas'], - 'actor': ['Liam Neeson', 'Ewan McGregor', 'Natalie Portman', - 'Hayden Christensen'] - }, - - } - USER_SHOW_LIBRARY = {'SW_REB': ['S01E01', 'S02E02']} - SHOW_DATABASE_IDS = ['SW_CW', 'SW_TCW', 'SW_REB'] - SHOW_DATABASE = {'SW_CW': {'title': 'Star Wars: Clone Wars', - 'rating': 'TV-Y7', - 'seasons': {1: ['S01E01', 'S01E02', 'S01E03'], - 2: ['S02E01', 'S02E02', 'S02E03']} - }, - 'SW_TCW': {'title': 'Star Wars: The Clone Wars', - 'rating': 'TV-PG', - 'seasons': {1: ['S01E01', 'S01E02', 'S01E03'], - 2: ['S02E01', 'S02E02', 'S02E03']} - }, - 'SW_REB': {'title': 'Star Wars: Rebels', - 'rating': 'TV-Y7', - 'seasons': {1: ['S01E01', 'S01E02', 'S01E03'], - 2: ['S02E01', 'S02E02', 'S02E03']} - }, - } - - file_list = \ - [ - '/home/user/file.db', - '/home/user/file space.db', - '/home/user/another.db', - '/home/other user/maps.db', - '/home/other user/tests.db' - ] - - # noinspection PyMethodMayBeStatic - def instance_query_actors(self) -> List[str]: - """Simulating a function that queries and returns a completion values""" - return actors - - def instance_query_movie_ids(self) -> List[str]: - """Demonstrates showing tabular hinting of tab completion information""" - completions_with_desc = [] - - # Sort the movie id strings with a natural sort since they contain numbers - 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(CompletionItem(movie_id, movie_entry['title'])) - - # Mark that we already sorted the matches - self.matches_sorted = True - return completions_with_desc - - # This demonstrates a number of customizations of the AutoCompleter version of ArgumentParser - # - The help output will separately group required vs optional flags - # - The help output for arguments with multiple flags or with append=True is more concise - # - cmd2 adds the ability to specify ranges of argument counts in 'nargs' - - suggest_description = "Suggest command demonstrates argparse customizations.\n" - suggest_description += "See hybrid_suggest and orig_suggest to compare the help output." - suggest_parser = Cmd2ArgumentParser(description=suggest_description) - - suggest_parser.add_argument('-t', '--type', choices=['movie', 'show'], required=True) - suggest_parser.add_argument('-d', '--duration', nargs=(1, 2), action='append', - help='Duration constraint in minutes.\n' - '\tsingle value - maximum duration\n' - '\t[a, b] - duration range') - - @cmd2.with_category(CAT_AUTOCOMPLETE) - @cmd2.with_argparser(suggest_parser) - def do_suggest(self, args) -> None: - """Suggest command demonstrates argparse customizations""" - if not args.type: - self.do_help('suggest') - - # If you prefer the original argparse help output but would like narg ranges, it's possible - # to enable narg ranges without the help changes using this method - - suggest_parser_hybrid = argparse.ArgumentParser() - suggest_parser_hybrid.add_argument('-t', '--type', choices=['movie', 'show'], required=True) - suggest_parser_hybrid.add_argument('-d', '--duration', nargs=(1, 2), action='append', - help='Duration constraint in minutes.\n' - '\tsingle value - maximum duration\n' - '\t[a, b] - duration range') - - @cmd2.with_category(CAT_AUTOCOMPLETE) - @cmd2.with_argparser(suggest_parser_hybrid) - def do_hybrid_suggest(self, args): - if not args.type: - self.do_help('orig_suggest') - - # This variant demonstrates the AutoCompleter working with the orginial argparse. - # Base argparse is unable to specify narg ranges. Autocompleter will keep expecting additional arguments - # for the -d/--duration flag until you specify a new flag or end processing of flags with '--' - - suggest_parser_orig = argparse.ArgumentParser() - - suggest_parser_orig.add_argument('-t', '--type', choices=['movie', 'show'], required=True) - suggest_parser_orig.add_argument('-d', '--duration', nargs='+', action='append', - help='Duration constraint in minutes.\n' - '\tsingle value - maximum duration\n' - '\t[a, b] - duration range') - - @cmd2.with_argparser(suggest_parser_orig) - @cmd2.with_category(CAT_AUTOCOMPLETE) - def do_orig_suggest(self, args) -> None: - if not args.type: - self.do_help('orig_suggest') - - def _do_vid_movies(self, args) -> None: - if not args.command: - self.do_help('video movies') - elif args.command == 'list': - for movie_id in TabCompleteExample.MOVIE_DATABASE: - movie = TabCompleteExample.MOVIE_DATABASE[movie_id] - print('{}\n-----------------------------\n{} ID: {}\nDirector: {}\nCast:\n {}\n\n' - .format(movie['title'], movie['rating'], movie_id, - ', '.join(movie['director']), - '\n '.join(movie['actor']))) - - def _do_vid_shows(self, args) -> None: - if not args.command: - self.do_help('video shows') - - elif args.command == 'list': - for show_id in TabCompleteExample.SHOW_DATABASE: - show = TabCompleteExample.SHOW_DATABASE[show_id] - print('{}\n-----------------------------\n{} ID: {}' - .format(show['title'], show['rating'], show_id)) - for season in show['seasons']: - ep_list = show['seasons'][season] - print(' Season {}:\n {}' - .format(season, - '\n '.join(ep_list))) - print() - - video_parser = Cmd2ArgumentParser() - - video_types_subparsers = video_parser.add_subparsers(title='Media Types', dest='type') - - vid_movies_parser = video_types_subparsers.add_parser('movies') - vid_movies_parser.set_defaults(func=_do_vid_movies) - - vid_movies_commands_subparsers = vid_movies_parser.add_subparsers(title='Commands', dest='command') - - vid_movies_list_parser = vid_movies_commands_subparsers.add_parser('list') - - vid_movies_list_parser.add_argument('-t', '--title', help='Title Filter') - vid_movies_list_parser.add_argument('-r', '--rating', help='Rating Filter', nargs='+', - choices=ratings_types) - vid_movies_list_parser.add_argument('-d', '--director', help='Director Filter', choices=static_list_directors) - vid_movies_list_parser.add_argument('-a', '--actor', help='Actor Filter', action='append', - choices_function=query_actors) - - vid_movies_add_parser = vid_movies_commands_subparsers.add_parser('add') - vid_movies_add_parser.add_argument('title', help='Movie Title') - vid_movies_add_parser.add_argument('rating', help='Movie Rating', choices=ratings_types) - - vid_movies_add_parser.add_argument('-d', '--director', help='Director', nargs=(1, 2), required=True, - choices=static_list_directors) - vid_movies_add_parser.add_argument('actor', help='Actors', nargs='*', choices_method=instance_query_actors) - - vid_movies_load_parser = vid_movies_commands_subparsers.add_parser('load') - vid_movies_load_parser.add_argument('movie_file', help='Movie database', - completer_method=functools.partial(cmd2.Cmd.delimiter_complete, - delimiter='/', match_against=file_list)) - - vid_movies_read_parser = vid_movies_commands_subparsers.add_parser('read') - vid_movies_read_parser.add_argument('movie_file', help='Movie database', completer_method=cmd2.Cmd.path_complete) - - vid_movies_delete_parser = vid_movies_commands_subparsers.add_parser('delete') - vid_movies_delete_parser.add_argument('movie_id', help='Movie ID', choices_method=instance_query_movie_ids, - descriptive_header='Title') - - vid_shows_parser = video_types_subparsers.add_parser('shows') - vid_shows_parser.set_defaults(func=_do_vid_shows) - - vid_shows_commands_subparsers = vid_shows_parser.add_subparsers(title='Commands', dest='command') - - vid_shows_list_parser = vid_shows_commands_subparsers.add_parser('list') - - @cmd2.with_category(CAT_AUTOCOMPLETE) - @cmd2.with_argparser(video_parser) - def do_video(self, args): - """Video management command demonstrates multiple layers of subcommands being handled by AutoCompleter""" - func = getattr(args, 'func', None) - if func is not None: - # Call whatever subcommand function was selected - func(self, args) - else: - # No subcommand was provided, so call help - self.do_help('video') - - -if __name__ == '__main__': - import sys - app = TabCompleteExample() - sys.exit(app.cmdloop()) diff --git a/examples/tab_completion.py b/examples/tab_completion.py deleted file mode 100755 index 1a25238f..00000000 --- a/examples/tab_completion.py +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# coding=utf-8 -""" -A simple example demonstrating how to use flag and index based tab-completion functions -For argparse-based tab completion, see tab_autocompletion.py -""" -import argparse - -import cmd2 - -# List of strings used with flag and index based completion functions -food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] -sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] - - -class TabCompleteExample(cmd2.Cmd): - """ Example cmd2 application where we a base command which has a couple subcommands.""" - - def __init__(self): - super().__init__() - - add_item_parser = argparse.ArgumentParser() - add_item_group = add_item_parser.add_mutually_exclusive_group() - add_item_group.add_argument('-f', '--food', help='Adds food item') - add_item_group.add_argument('-s', '--sport', help='Adds sport item') - add_item_group.add_argument('-o', '--other', help='Adds other item') - - @cmd2.with_argparser(add_item_parser) - def do_add_item(self, args): - """Add item command help""" - if args.food: - add_item = args.food - elif args.sport: - add_item = args.sport - elif args.other: - add_item = args.other - else: - add_item = 'no items' - - self.poutput("You added {}".format(add_item)) - - # Add flag-based tab-completion to add_item command - def complete_add_item(self, text, line, begidx, endidx): - flag_dict = \ - { - # Tab-complete food items after -f and --food flags in command line - '-f': food_item_strs, - '--food': food_item_strs, - - # Tab-complete sport items after -s and --sport flags in command line - '-s': sport_item_strs, - '--sport': sport_item_strs, - - # Tab-complete using path_complete function after -o and --other flags in command line - '-o': self.path_complete, - '--other': self.path_complete, - } - - return self.flag_based_complete(text, line, begidx, endidx, flag_dict=flag_dict) - - @cmd2.with_argument_list - def do_list_item(self, args): - """List item command help""" - self.poutput("You listed {}".format(args)) - - # Add index-based tab-completion to list_item command - def complete_list_item(self, text, line, begidx, endidx): - index_dict = \ - { - 1: food_item_strs, # Tab-complete food items at index 1 in command line - 2: sport_item_strs, # Tab-complete sport items at index 2 in command line - 3: self.path_complete, # Tab-complete using path_complete function at index 3 in command line - } - - return self.index_based_complete(text, line, begidx, endidx, index_dict=index_dict) - - -if __name__ == '__main__': - import sys - app = TabCompleteExample() - sys.exit(app.cmdloop()) -- cgit v1.2.1 From c3e3c1c595d65ec4c5fa2c9dac88ffa30cf4738e Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 13 Feb 2020 10:46:42 -0500 Subject: Removed use of argparse from basic completion example --- examples/basic_completion.py | 76 +++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 46 deletions(-) diff --git a/examples/basic_completion.py b/examples/basic_completion.py index 4baec16c..3750c2c6 100755 --- a/examples/basic_completion.py +++ b/examples/basic_completion.py @@ -7,9 +7,8 @@ This also demonstrates capabilities of the following completer methods included - index_based_complete - delimiter_completer -For an example enabling tab completion with argparse, see argparse_completion.py +For an example integrating tab completion with argparse, see argparse_completion.py """ -import argparse import functools import cmd2 @@ -18,6 +17,7 @@ import cmd2 food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] +# This data is used to demonstrate delimiter_complete file_strs = \ [ '/home/user/file.db', @@ -28,34 +28,20 @@ file_strs = \ ] -class TabCompleteExample(cmd2.Cmd): - """ Example cmd2 application where we a base command which has a couple subcommands.""" - def __init__(self): - super().__init__() - - # The add_item command uses flag_based_complete - add_item_parser = argparse.ArgumentParser() - add_item_group = add_item_parser.add_mutually_exclusive_group() - add_item_group.add_argument('-f', '--food', help='Adds food item') - add_item_group.add_argument('-s', '--sport', help='Adds sport item') - add_item_group.add_argument('-o', '--other', help='Adds other item') - - @cmd2.with_argparser(add_item_parser) - def do_add_item(self, args): - """Add item command help""" - if args.food: - add_item = args.food - elif args.sport: - add_item = args.sport - elif args.other: - add_item = args.other - else: - add_item = 'no items' - - self.poutput("You added {}".format(add_item)) - - # Add flag-based tab-completion to add_item command - def complete_add_item(self, text, line, begidx, endidx): +class BasicCompletion(cmd2.Cmd): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def do_flag_based(self, statement: cmd2.Statement): + """Tab completes arguments based on a preceding flag using flag_based_complete + -f, --food [completes food items] + -s, --sport [completes sports] + -p, --path [completes local file system paths] + """ + self.poutput("Args: {}".format(statement.args)) + + def complete_flag_based(self, text, line, begidx, endidx): + """Completion function for do_flag_based""" flag_dict = \ { # Tab-complete food items after -f and --food flags in command line @@ -66,21 +52,19 @@ class TabCompleteExample(cmd2.Cmd): '-s': sport_item_strs, '--sport': sport_item_strs, - # Tab-complete using path_complete function after -o and --other flags in command line - '-o': self.path_complete, - '--other': self.path_complete, + # Tab-complete using path_complete function after -p and --path flags in command line + '-p': self.path_complete, + '--path': self.path_complete, } return self.flag_based_complete(text, line, begidx, endidx, flag_dict=flag_dict) - # The list_item command uses index_based_complete - @cmd2.with_argument_list - def do_list_item(self, args): - """List item command help""" - self.poutput("You listed {}".format(args)) + def do_index_based(self, statement: cmd2.Statement): + """Tab completes first 3 arguments using index_based_complete""" + self.poutput("Args: {}".format(statement.args)) - # Add index-based tab-completion to list_item command - def complete_list_item(self, text, line, begidx, endidx): + def complete_index_based(self, text, line, begidx, endidx): + """Completion function for do_index_based""" index_dict = \ { 1: food_item_strs, # Tab-complete food items at index 1 in command line @@ -90,16 +74,16 @@ class TabCompleteExample(cmd2.Cmd): return self.index_based_complete(text, line, begidx, endidx, index_dict=index_dict) - # The file_list command uses delimiter_complete - def do_file_list(self, statement: cmd2.Statement): - """List files entered on command line""" - self.poutput("You selected: {}".format(statement.args)) + def do_delimiter_complete(self, statement: cmd2.Statement): + """Tab completes files from a list using delimiter_complete""" + self.poutput("Args: {}".format(statement.args)) # Use a partialmethod to set arguments to delimiter_complete - complete_file_list = functools.partialmethod(cmd2.Cmd.delimiter_complete, match_against=file_strs, delimiter='/') + complete_delimiter_complete = functools.partialmethod(cmd2.Cmd.delimiter_complete, + match_against=file_strs, delimiter='/') if __name__ == '__main__': import sys - app = TabCompleteExample() + app = BasicCompletion() sys.exit(app.cmdloop()) -- cgit v1.2.1 From 41019de4b55f42d17149c29d380358dc38347948 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 13 Feb 2020 13:39:53 -0500 Subject: Removed dash from 'tab complete' string --- CHANGELOG.md | 28 ++++++++++++++-------------- README.md | 12 ++++++------ cmd2/argparse_custom.py | 14 +++++++------- docs/features/argument_processing.rst | 2 +- docs/features/completion.rst | 14 +++++++------- docs/features/embedded_python_shells.rst | 2 +- docs/features/initialization.rst | 2 +- docs/features/os.rst | 2 +- docs/features/scripting.rst | 4 ++-- docs/features/settings.rst | 4 ++-- docs/overview/installation.rst | 2 +- examples/basic_completion.py | 12 ++++++------ tests/conftest.py | 2 +- tests/test_completion.py | 14 +++++++------- 14 files changed, 57 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4e3bc0b..f9d71831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ ## 0.10.0 (February 7, 2020) * Enhancements * Changed the default help text to make `help -v` more discoverable - * **set** command now supports tab-completion of values + * **set** command now supports tab completion of values * Added `add_settable()` and `remove_settable()` convenience methods to update `self.settable` dictionary * Added convenience `ansi.fg` and `ansi.bg` enums of foreground and background colors * `ansi.style()` `fg` argument can now either be of type `str` or `ansi.fg` @@ -185,7 +185,7 @@ `delimiter_complete`, `flag_based_complete`, `index_based_complete`, `path_complete`, `shell_cmd_complete` * Renamed history option from `--output-file` to `--output_file` * Renamed `matches_sort_key` to `default_sort_key`. This value determines the default sort ordering of string - results like alias, command, category, macro, settable, and shortcut names. Unsorted tab-completion results + results like alias, command, category, macro, settable, and shortcut names. Unsorted tab completion results also are sorted with this key. Its default value (ALPHABETICAL_SORT_KEY) performs a case-insensitive alphabetical sort, but it can be changed to a natural sort by setting the value to NATURAL_SORT_KEY. * `StatementParser` now expects shortcuts to be passed in as dictionary. This eliminates the step of converting the @@ -434,7 +434,7 @@ * ``ACHelpFormatter`` now inherits from ``argparse.RawTextHelpFormatter`` to make it easier for formatting help/description text * Aliases are now sorted alphabetically - * The **set** command now tab-completes settable parameter names + * The **set** command now tab completes settable parameter names * Added ``async_alert``, ``async_update_prompt``, and ``set_window_title`` functions * These allow you to provide feedback to the user in an asychronous fashion, meaning alerts can display when the user is still entering text at the prompt. See [async_printing.py](https://github.com/python-cmd2/cmd2/blob/master/examples/async_printing.py) @@ -468,7 +468,7 @@ * Improved implementation of lifecycle hooks to support a plugin framework, see ``docs/hooks.rst`` for details. * New dependency on ``attrs`` third party module - * Added ``matches_sorted`` member to support custom sorting of tab-completion matches + * Added ``matches_sorted`` member to support custom sorting of tab completion matches * Added [tab_autocomp_dynamic.py](https://github.com/python-cmd2/cmd2/blob/master/examples/tab_autocomp_dynamic.py) example * Demonstrates updating the argparse object during init instead of during class construction * Deprecations @@ -498,7 +498,7 @@ * Bug Fixes * Fixed issue where piping and redirecting did not work correctly with paths that had spaces * Enhancements - * Added ability to print a header above tab-completion suggestions using `completion_header` member + * Added ability to print a header above tab completion suggestions using `completion_header` member * Added ``pager`` and ``pager_chop`` attributes to the ``cmd2.Cmd`` class * ``pager`` defaults to **less -RXF** on POSIX and **more** on Windows * ``pager_chop`` defaults to **less -SRXF** on POSIX and **more** on Windows @@ -570,7 +570,7 @@ * Fixed ``AttributeError`` on Windows when running a ``select`` command cause by **pyreadline** not implementing ``remove_history_item`` * Enhancements * Added warning about **libedit** variant of **readline** not being supported on macOS - * Added tab-completion of alias names in value field of **alias** command + * Added tab completion of alias names in value field of **alias** command * Enhanced the ``py`` console in the following ways * Added tab completion of Python identifiers instead of **cmd2** commands * Separated the ``py`` console history from the **cmd2** history @@ -628,7 +628,7 @@ ## 0.8.2 (March 21, 2018) * Bug Fixes - * Fixed a bug in tab-completion of command names within sub-menus + * Fixed a bug in tab completion of command names within sub-menus * Fixed a bug when using persistent readline history in Python 2.7 * Fixed a bug where the ``AddSubmenu`` decorator didn't work with a default value for ``shared_attributes`` * Added a check to ``ppaged()`` to only use a pager when running in a real fully functional terminal @@ -685,7 +685,7 @@ and [arg_print.py](https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py) examples * Added support for Argparse subcommands when using the **with_argument_parser** or **with_argparser_and_unknown_args** decorators * See [subcommands.py](https://github.com/python-cmd2/cmd2/blob/master/examples/subcommands.py) for an example of how to use subcommands - * Tab-completion of subcommand names is automatically supported + * Tab completion of subcommand names is automatically supported * The **__relative_load** command is now hidden from the help menu by default * This command is not intended to be called from the command line, only from within scripts * The **set** command now has an additional **-a/--all** option to also display read-only settings @@ -712,7 +712,7 @@ * Fixed a couple broken examples * Enhancements * Improved documentation for modifying shortcuts (command aliases) - * Made ``pyreadline`` a dependency on Windows to ensure tab-completion works + * Made ``pyreadline`` a dependency on Windows to ensure tab completion works * Other changes * Abandoned official support for Python 3.3. It should still work, just don't have an easy way to test it anymore. @@ -750,7 +750,7 @@ * Fixed some pyperclip clipboard interaction bugs on Linux * Fixed some timing bugs when running unit tests in parallel by using monkeypatch * Enhancements - * Enhanced tab-completion of cmd2 command names to support case-insensitive completion + * Enhanced tab completion of cmd2 command names to support case-insensitive completion * Added an example showing how to remove unused commands * Improved how transcript testing handles prompts with ANSI escape codes by stripping them * Greatly improved implementation for how command output gets piped to a shell command @@ -766,7 +766,7 @@ * Enhancements * Organized all attributes used to configure the ParserManager into a single location * Set the default value of `abbrev` to `False` (which controls whether or not abbreviated commands are allowed) - * With good tab-completion of command names, using abbreviated commands isn't particularly useful + * With good tab completion of command names, using abbreviated commands isn't particularly useful * And it can create complications if you are't careful * Improved implementation of `load` to use command queue instead of nested inner loop @@ -778,7 +778,7 @@ * Ability to pipe ``cmd2`` command output to a shell command is now more reliable, particularly on Windows * Fixed a bug in ``pyscript`` command on Windows related to ``\`` being interpreted as an escape * Enhancements - * Ensure that path and shell command tab-completion results are alphabetically sorted + * Ensure that path and shell command tab completion results are alphabetically sorted * Removed feature for load command to load scripts from URLS * It didn't work, there were no unit tests, and it felt out of place * Removed presence of a default file name and default file extension @@ -801,8 +801,8 @@ * Enhancements * Added the ability to exclude commands from the help menu (**eof** included by default) * Redundant **list** command removed and features merged into **history** command - * Added **pyscript** command which supports tab-completion and running Python scripts with arguments - * Improved tab-completion of file system paths, command names, and shell commands + * Added **pyscript** command which supports tab completion and running Python scripts with arguments + * Improved tab completion of file system paths, command names, and shell commands * Thanks to Kevin Van Brunt for all of the help with debugging and testing this * Changed default value of USE_ARG_LIST to True - this affects the beavhior of all **@options** commands * **WARNING**: This breaks backwards compatibility, to restore backwards compatibility, add this to the diff --git a/README.md b/README.md index 334d10ab..ce2fdb7c 100755 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ Main Features - Settable environment parameters - Parsing commands with arguments using `argparse`, including support for subcommands - Unicode character support -- Good tab-completion of commands, subcommands, file system paths, and shell commands -- Automatic tab-completion of `argparse` flags when using one of the `cmd2` `argparse` decorators +- Good tab completion of commands, subcommands, file system paths, and shell commands +- Automatic tab completion of `argparse` flags when using one of the `cmd2` `argparse` decorators - Support for Python 3.5+ on Windows, macOS, and Linux - Trivial to provide built-in help for all commands - Built-in regression testing framework for your applications (transcript-based testing) @@ -96,7 +96,7 @@ Instructions for implementing each feature follow. - By default the docstring for your **do_foo** method is the help for the **foo** command - NOTE: This doesn't apply if you use one of the `argparse` decorators mentioned below - Can provide more custom help by creating a **help_foo** method (except when using `argparse` decorators) - - Can provide custom tab-completion for the **foo** command by creating a **complete_foo** method + - Can provide custom tab completion for the **foo** command by creating a **complete_foo** method - Easy to upgrade an existing `cmd` app to `cmd2` - Run your `cmd2` app using the built-in REPL by executing the **cmdloop** method @@ -164,14 +164,14 @@ Instructions for implementing each feature follow. - Option to display long output using a pager with ``cmd2.Cmd.ppaged()`` - Optionally specify a startup script that end users can use to customize their environment -- Top-notch tab-completion capabilities which are easy to use but very powerful +- Top-notch tab completion capabilities which are easy to use but very powerful - For a command **foo** implement a **complete_foo** method to provide custom tab completion for that command - But the helper methods within `cmd2` discussed below mean you would rarely have to implement this from scratch - - Commands which use one of the `argparse` decorators have automatic tab-completion of `argparse` flags + - Commands which use one of the `argparse` decorators have automatic tab completion of `argparse` flags - And also provide help hints for values associated with these flags - Experiment with the [argprint.py](https://github.com/python-cmd2/cmd2/blob/master/examples/arg_print.py) example using the **oprint** and **pprint** commands to get a feel for how this works - - `path_complete` helper method provides flexible tab-completion of file system paths + - `path_complete` helper method provides flexible tab completion of file system paths - See the [paged_output.py](https://github.com/python-cmd2/cmd2/blob/master/examples/paged_output.py) example for a simple use case - See the [python_scripting.py](https://github.com/python-cmd2/cmd2/blob/master/examples/python_scripting.py) example for a more full-featured use case - `flag_based_complete` helper method for tab completion based on a particular flag preceding the token being completed diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index a0e05ae9..a59270c3 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -62,8 +62,8 @@ Tab Completion: return my_generated_list completer_function - Pass a tab-completion function that does custom completion. Since custom tab completion operations commonly - need to modify cmd2's instance variables related to tab-completion, it will be rare to need a completer + Pass a tab completion function that does custom completion. Since custom tab completion operations commonly + need to modify cmd2's instance variables related to tab completion, it will be rare to need a completer function. completer_method should be used in those cases. Example: @@ -90,7 +90,7 @@ Tab Completion: path_filter=lambda path: os.path.isdir(path)) parser.add_argument('-o', '--options', choices_method=completer_method) - Of the 5 tab-completion parameters, choices is the only one where argparse validates user input against items + Of the 5 tab completion parameters, choices is the only one where argparse validates user input against items in the choices list. This is because the other 4 parameters are meant to tab complete data sets that are viewed as dynamic. Therefore it is up to the developer to validate if the user has typed an acceptable value for these arguments. @@ -118,7 +118,7 @@ Tab Completion: the developer to determine if the user entered the correct argument type (e.g. int) and validate their values. CompletionError Class: - Raised during tab-completion operations to report any sort of error you want printed by the AutoCompleter + Raised during tab completion operations to report any sort of error you want printed by the AutoCompleter Example use cases - Reading a database to retrieve a tab completion data set failed @@ -231,7 +231,7 @@ def generate_range_error(range_min: int, range_max: Union[int, float]) -> str: class CompletionError(Exception): """ - Raised during tab-completion operations to report any sort of error you want printed by the AutoCompleter + Raised during tab completion operations to report any sort of error you want printed by the AutoCompleter Example use cases - Reading a database to retrieve a tab completion data set failed @@ -356,8 +356,8 @@ def _add_argument_wrapper(self, *args, # Added args used by AutoCompleter :param choices_function: function that provides choices for this argument :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 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 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 diff --git a/docs/features/argument_processing.rst b/docs/features/argument_processing.rst index 39a39804..82244d7e 100644 --- a/docs/features/argument_processing.rst +++ b/docs/features/argument_processing.rst @@ -325,7 +325,7 @@ Subcommands are supported for commands using either the ``@with_argparser`` or is based on argparse sub-parsers. You may add multiple layers of subcommands for your command. ``cmd2`` will -automatically traverse and tab-complete subcommands for all commands using +automatically traverse and tab complete subcommands for all commands using argparse. See the subcommands_ example to learn more about how to diff --git a/docs/features/completion.rst b/docs/features/completion.rst index 9103b543..a8c1cc85 100644 --- a/docs/features/completion.rst +++ b/docs/features/completion.rst @@ -1,7 +1,7 @@ Completion ========== -``cmd2`` adds tab-completion of file system paths for all built-in commands +``cmd2`` adds tab completion of file system paths for all built-in commands where it makes sense, including: - ``edit`` @@ -9,7 +9,7 @@ where it makes sense, including: - ``run_script`` - ``shell`` -``cmd2`` also adds tab-completion of shell commands to the ``shell`` command. +``cmd2`` also adds tab completion of shell commands to the ``shell`` command. Additionally, it is trivial to add identical file system path completion to your own custom commands. Suppose you have defined a custom command ``foo`` by @@ -37,9 +37,9 @@ Tab Completion Using Argparse Decorators ---------------------------------------- When using one the Argparse-based :ref:`api/decorators:Decorators`, ``cmd2`` -provides automatic tab-completion of flag names. +provides automatic tab completion of flag names. -Tab-completion of argument values can be configured by using one of five +Tab completion of argument values can be configured by using one of five parameters to ``argparse.ArgumentParser.add_argument()`` - ``choices`` @@ -52,9 +52,9 @@ demonstration of how to use the ``choices_function`` and ``choices_method`` parameters. See the arg_decorators_ or argparse_completion_ example for a demonstration of how to use the ``completer_method`` parameter. -When tab-completing flags and/or argument values for a ``cmd2`` command using +When tab completing flags and/or argument values for a ``cmd2`` command using one of these decorators, ``cmd2`` keeps track of state so that once a flag has -already previously been provided, it won't attempt to tab-complete it again. +already previously been provided, it won't attempt to tab complete it again. When no completion results exists, a hint for the current argument will be displayed to help the user. @@ -66,7 +66,7 @@ displayed to help the user. CompletionItem For Providing Extra Context ------------------------------------------ -When tab-completing things like a unique ID from a database, it can often be +When tab completing things like a unique ID from a database, it can often be beneficial to provide the user with some extra context about the item being completed, such as a description. To facilitate this, ``cmd2`` defines the ``CompletionItem`` class which can be returned from any of the 4 completion diff --git a/docs/features/embedded_python_shells.rst b/docs/features/embedded_python_shells.rst index 68377876..70765b21 100644 --- a/docs/features/embedded_python_shells.rst +++ b/docs/features/embedded_python_shells.rst @@ -67,7 +67,7 @@ code directory for an example of how to achieve this in your own applications. Using ``py`` to run scripts directly is considered deprecated. The newer ``run_pyscript`` command is superior for doing this in two primary ways: -- it supports tab-completion of file system paths +- it supports tab completion of file system paths - it has the ability to pass command-line arguments to the scripts invoked There are no disadvantages to using ``run_pyscript`` as opposed to ``py diff --git a/docs/features/initialization.rst b/docs/features/initialization.rst index 6824c7bf..b1ca4f05 100644 --- a/docs/features/initialization.rst +++ b/docs/features/initialization.rst @@ -133,7 +133,7 @@ override: command via ``self`` (Default: ``False``) - **macros**: dictionary of macro names and their values - **max_completion_items**: max number of CompletionItems to display during - tab-completion (Default: 50) + tab completion (Default: 50) - **pager**: sets the pager command used by the ``Cmd.ppaged()`` method for displaying wrapped output using a pager - **pager_chop**: sets the pager command used by the ``Cmd.ppaged()`` method diff --git a/docs/features/os.rst b/docs/features/os.rst index 89905d17..77bc6a66 100644 --- a/docs/features/os.rst +++ b/docs/features/os.rst @@ -20,7 +20,7 @@ to type:: (Cmd) !ls -al -NOTE: ``cmd2`` provides user-friendly tab-completion throughout the process of +NOTE: ``cmd2`` provides user-friendly tab completion throughout the process of running a shell command - first for the shell command name itself, and then for file paths in the argument section. diff --git a/docs/features/scripting.rst b/docs/features/scripting.rst index 62af2e6d..1128f5e1 100644 --- a/docs/features/scripting.rst +++ b/docs/features/scripting.rst @@ -34,7 +34,7 @@ Running Command Scripts Command script files can be executed using the built-in ``run_script`` command or ``@`` shortcut. Both ASCII and UTF-8 encoded unicode text files are -supported. The ``run_script`` command supports tab-completion of file system +supported. The ``run_script`` command supports tab completion of file system paths. There is a variant ``_relative_run_script`` command or ``@@`` shortcut for use within a script which uses paths relative to the first script. @@ -73,7 +73,7 @@ using ``run_pyscript`` is shown below along with the arg_printer_ script:: arg 2: 'bar' arg 3: 'baz 23' -``run_pyscript`` supports tab-completion of file system paths, and as shown +``run_pyscript`` supports tab completion of file system paths, and as shown above it has the ability to pass command-line arguments to the scripts invoked. Python scripts executed with ``run_pyscript`` can run ``cmd2`` application diff --git a/docs/features/settings.rst b/docs/features/settings.rst index 5a4a9c0f..aa3e5cec 100644 --- a/docs/features/settings.rst +++ b/docs/features/settings.rst @@ -89,11 +89,11 @@ max_completion_items ~~~~~~~~~~~~~~~~~~~~ Maximum number of CompletionItems to display during tab completion. A -CompletionItem is a special kind of tab-completion hint which displays both a +CompletionItem is a special kind of tab completion hint which displays both a value and description and uses one line for each hint. Tab complete the ``set`` command for an example. -If the number of tab-completion hints exceeds ``max_completion_items``, then +If the number of tab completion hints exceeds ``max_completion_items``, then they will be displayed in the typical columnized format and will not include the description text of the CompletionItem. diff --git a/docs/overview/installation.rst b/docs/overview/installation.rst index d8d24ebd..6080fe90 100644 --- a/docs/overview/installation.rst +++ b/docs/overview/installation.rst @@ -119,7 +119,7 @@ macOS Considerations -------------------- macOS comes with the `libedit `_ library which is -similar, but not identical, to GNU Readline. Tab-completion for ``cmd2`` +similar, but not identical, to GNU Readline. Tab completion for ``cmd2`` applications is only tested against GNU Readline. There are several ways GNU Readline can be installed within a Python diff --git a/examples/basic_completion.py b/examples/basic_completion.py index 3750c2c6..f21f75fc 100755 --- a/examples/basic_completion.py +++ b/examples/basic_completion.py @@ -44,15 +44,15 @@ class BasicCompletion(cmd2.Cmd): """Completion function for do_flag_based""" flag_dict = \ { - # Tab-complete food items after -f and --food flags in command line + # Tab complete food items after -f and --food flags in command line '-f': food_item_strs, '--food': food_item_strs, - # Tab-complete sport items after -s and --sport flags in command line + # Tab complete sport items after -s and --sport flags in command line '-s': sport_item_strs, '--sport': sport_item_strs, - # Tab-complete using path_complete function after -p and --path flags in command line + # Tab complete using path_complete function after -p and --path flags in command line '-p': self.path_complete, '--path': self.path_complete, } @@ -67,9 +67,9 @@ class BasicCompletion(cmd2.Cmd): """Completion function for do_index_based""" index_dict = \ { - 1: food_item_strs, # Tab-complete food items at index 1 in command line - 2: sport_item_strs, # Tab-complete sport items at index 2 in command line - 3: self.path_complete, # Tab-complete using path_complete function at index 3 in command line + 1: food_item_strs, # Tab complete food items at index 1 in command line + 2: sport_item_strs, # Tab complete sport items at index 2 in command line + 3: self.path_complete, # Tab complete using path_complete function at index 3 in command line } return self.index_based_complete(text, line, begidx, endidx, index_dict=index_dict) diff --git a/tests/conftest.py b/tests/conftest.py index 7f77a207..9ee8da19 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -185,7 +185,7 @@ def complete_tester(text: str, line: str, begidx: int, endidx: int, app) -> Opti def get_endidx(): return endidx - # Run the readline tab-completion function with readline mocks in place + # Run the readline tab completion function with readline mocks in place with mock.patch.object(readline, 'get_line_buffer', get_line): with mock.patch.object(readline, 'get_begidx', get_begidx): with mock.patch.object(readline, 'get_endidx', get_endidx): diff --git a/tests/test_completion.py b/tests/test_completion.py index 99f832a4..f545c8f9 100755 --- a/tests/test_completion.py +++ b/tests/test_completion.py @@ -1,9 +1,9 @@ # coding=utf-8 # flake8: noqa E302 """ -Unit/functional testing for readline tab-completion functions in the cmd2.py module. +Unit/functional testing for readline tab completion functions in the cmd2.py module. -These are primarily tests related to readline completer functions which handle tab-completion of cmd2/cmd commands, +These are primarily tests related to readline completer functions which handle tab completion of cmd2/cmd commands, file system paths, and shell commands. """ # Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available @@ -39,11 +39,11 @@ delimited_strs = \ # Dictionary used with flag based completion functions flag_dict = \ { - # Tab-complete food items after -f and --food flag in command line + # Tab complete food items after -f and --food flag in command line '-f': food_item_strs, '--food': food_item_strs, - # Tab-complete sport items after -s and --sport flag in command line + # Tab complete sport items after -s and --sport flag in command line '-s': sport_item_strs, '--sport': sport_item_strs, } @@ -51,14 +51,14 @@ flag_dict = \ # Dictionary used with index based completion functions index_dict = \ { - 1: food_item_strs, # Tab-complete food items at index 1 in command line - 2: sport_item_strs, # Tab-complete sport items at index 2 in command line + 1: food_item_strs, # Tab complete food items at index 1 in command line + 2: sport_item_strs, # Tab complete sport items at index 2 in command line } class CompletionsExample(cmd2.Cmd): """ - Example cmd2 application used to exercise tab-completion tests + Example cmd2 application used to exercise tab completion tests """ def __init__(self): cmd2.Cmd.__init__(self, multiline_commands=['test_multiline']) -- cgit v1.2.1 From 09985f668ecdbad87f9fc725e82b3c99cbd2ef5d Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 13 Feb 2020 13:47:39 -0500 Subject: Start of new argparse completion example --- examples/argparse_completion.py | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 examples/argparse_completion.py diff --git a/examples/argparse_completion.py b/examples/argparse_completion.py new file mode 100644 index 00000000..e24fc72e --- /dev/null +++ b/examples/argparse_completion.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# coding=utf-8 +""" +A simple example demonstrating how to integrate tab completion with argparse-based commands. +""" +import argparse +from typing import List + +from cmd2 import Cmd, Cmd2ArgumentParser, with_argparser +from cmd2.utils import basic_complete + +food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] + +# This data is used to demonstrate delimiter_complete +file_strs = \ + [ + '/home/user/file.db', + '/home/user/file space.db', + '/home/user/another.db', + '/home/other user/maps.db', + '/home/other user/tests.db' + ] + + +def choices_function() -> List[str]: + """Choices functions are useful when the choice list is dynamically generated (e.g. from data in a database)""" + return ['a', 'dynamic', 'list'] + + +def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]: + """ + A tab completion function not dependent on instance data. Since custom tab completion operations commonly + need to modify cmd2's instance variables related to tab completion, it will be rare to need a completer + function. completer_method should be used in those cases. + """ + return basic_complete(text, line, begidx, endidx, food_item_strs) + + +class ArgparseCompletion(Cmd): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.sport_item_strs = ['Bat', 'Basket', 'Basketball', 'Football', 'Space Ball'] + + def choices_method(self) -> List[str]: + """Choices methods are useful when the choice list is based on instance data of your application""" + return self.sport_item_strs + + # Parser for complete command + complete_parser = Cmd2ArgumentParser(description="Command demonstrating tab completion with argparse\n" + "Notice even the flags of this command tab complete") + + # Tab complete from a list using argparse choices. Set metavar if you don't + # want the entire choices list showing in the usage text for this command. + complete_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE") + + # Tab complete from choices provided by a choices function and choices method + complete_parser.add_argument('--choices_function', choices_function=choices_function) + complete_parser.add_argument('--choices_method', choices_method=choices_method) + + # Tab complete using a completer function and completer method + complete_parser.add_argument('--completer_function', completer_function=completer_function) + complete_parser.add_argument('--completer_method', completer_method=Cmd.path_complete) + + @with_argparser(complete_parser) + def do_complete(self, _: argparse.Namespace) -> None: + """The complete command""" + self.poutput("I do nothing") + + +if __name__ == '__main__': + import sys + app = ArgparseCompletion() + sys.exit(app.cmdloop()) -- cgit v1.2.1 From cdd81905b5c8c9321d48786c0912368157587477 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 13 Feb 2020 15:02:42 -0500 Subject: Using metavar in CompletionItem header if it has been set --- cmd2/argparse_completer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py index 6513fe13..185e01a2 100644 --- a/cmd2/argparse_completer.py +++ b/cmd2/argparse_completer.py @@ -444,7 +444,9 @@ class AutoCompleter: completions.sort(key=self._cmd2_app.default_sort_key) self._cmd2_app.matches_sorted = True - token_width = ansi.style_aware_wcswidth(action.dest) + # If a metavar was defined, use that instead of the dest field + destination = action.metavar if action.metavar else action.dest + token_width = ansi.style_aware_wcswidth(destination) completions_with_desc = [] for item in completions: @@ -463,7 +465,7 @@ class AutoCompleter: desc_header = getattr(action, ATTR_DESCRIPTIVE_COMPLETION_HEADER, None) if desc_header is None: desc_header = DEFAULT_DESCRIPTIVE_HEADER - header = '\n{: <{token_width}}{}'.format(action.dest.upper(), desc_header, token_width=token_width + 2) + header = '\n{: <{token_width}}{}'.format(destination.upper(), desc_header, token_width=token_width + 2) self._cmd2_app.completion_header = header self._cmd2_app.display_matches = completions_with_desc -- cgit v1.2.1 From ee93add8d49210da554958e70006db52dde88d1e Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 13 Feb 2020 15:04:00 -0500 Subject: Wrote examples for CompletionError and CompletionItem --- examples/argparse_completion.py | 47 +++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/examples/argparse_completion.py b/examples/argparse_completion.py index e24fc72e..a089f285 100644 --- a/examples/argparse_completion.py +++ b/examples/argparse_completion.py @@ -6,25 +6,16 @@ A simple example demonstrating how to integrate tab completion with argparse-bas import argparse from typing import List -from cmd2 import Cmd, Cmd2ArgumentParser, with_argparser +from cmd2 import Cmd, Cmd2ArgumentParser, with_argparser, CompletionError, CompletionItem from cmd2.utils import basic_complete +# Data source for argparse.choices food_item_strs = ['Pizza', 'Ham', 'Ham Sandwich', 'Potato'] -# This data is used to demonstrate delimiter_complete -file_strs = \ - [ - '/home/user/file.db', - '/home/user/file space.db', - '/home/user/another.db', - '/home/other user/maps.db', - '/home/other user/tests.db' - ] - def choices_function() -> List[str]: """Choices functions are useful when the choice list is dynamically generated (e.g. from data in a database)""" - return ['a', 'dynamic', 'list'] + return ['a', 'dynamic', 'list', 'goes', 'here'] def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[str]: @@ -33,7 +24,18 @@ def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[s need to modify cmd2's instance variables related to tab completion, it will be rare to need a completer function. completer_method should be used in those cases. """ - return basic_complete(text, line, begidx, endidx, food_item_strs) + match_against = ['a', 'dynamic', 'list', 'goes', 'here'] + return basic_complete(text, line, begidx, endidx, match_against) + + +def choices_completion_item() -> List[CompletionItem]: + """Return CompletionItem instead of strings. These give more context to what's being tab completed.""" + items = { + 1: "My item", + 2: "Another item", + 3: "Yet another item" + } + return [CompletionItem(item_id, description) for item_id, description in items.items()] class ArgparseCompletion(Cmd): @@ -45,6 +47,18 @@ class ArgparseCompletion(Cmd): """Choices methods are useful when the choice list is based on instance data of your application""" return self.sport_item_strs + def choices_completion_error(self) -> List[str]: + """ + CompletionErrors can be raised if an error occurs while tab completing. + + Example use cases + - Reading a database to retrieve a tab completion data set failed + - A previous command line argument that determines the data set being completed is invalid + """ + if self.debug: + return self.sport_item_strs + raise CompletionError("Debug must be true") + # Parser for complete command complete_parser = Cmd2ArgumentParser(description="Command demonstrating tab completion with argparse\n" "Notice even the flags of this command tab complete") @@ -61,6 +75,13 @@ class ArgparseCompletion(Cmd): complete_parser.add_argument('--completer_function', completer_function=completer_function) complete_parser.add_argument('--completer_method', completer_method=Cmd.path_complete) + # Demonstrate raising a CompletionError while tab completing + complete_parser.add_argument('--completion_error', choices_method=choices_completion_error) + + # Demonstrate returning CompletionItems instead of strings + complete_parser.add_argument('--completion_item', choices_function=choices_completion_item, metavar="ITEM_ID", + descriptive_header="Description") + @with_argparser(complete_parser) def do_complete(self, _: argparse.Namespace) -> None: """The complete command""" -- cgit v1.2.1 From 8cfc02d4fe7f33549ce190aec30535800651e705 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 13 Feb 2020 15:28:45 -0500 Subject: Finished argparse completion example --- examples/argparse_completion.py | 70 ++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/examples/argparse_completion.py b/examples/argparse_completion.py index a089f285..90975d3f 100644 --- a/examples/argparse_completion.py +++ b/examples/argparse_completion.py @@ -4,7 +4,7 @@ A simple example demonstrating how to integrate tab completion with argparse-based commands. """ import argparse -from typing import List +from typing import Dict, List from cmd2 import Cmd, Cmd2ArgumentParser, with_argparser, CompletionError, CompletionItem from cmd2.utils import basic_complete @@ -30,14 +30,31 @@ def completer_function(text: str, line: str, begidx: int, endidx: int) -> List[s def choices_completion_item() -> List[CompletionItem]: """Return CompletionItem instead of strings. These give more context to what's being tab completed.""" - items = { - 1: "My item", - 2: "Another item", - 3: "Yet another item" - } + items = \ + { + 1: "My item", + 2: "Another item", + 3: "Yet another item" + } return [CompletionItem(item_id, description) for item_id, description in items.items()] +def choices_arg_tokens(arg_tokens: Dict[str, List[str]]) -> List[str]: + """ + If a choices or completer function/method takes a value called arg_tokens, then it will be + passed a dictionary that maps the command line tokens up through the one being completed + to their argparse argument name. All values of the arg_tokens dictionary are lists, even if + a particular argument expects only 1 token. + """ + # Check if choices_function flag has appeared + values = ['choices_function', 'flag'] + if 'choices_function' in arg_tokens: + values.append('is {}'.format(arg_tokens['choices_function'][0])) + else: + values.append('not supplied') + return values + + class ArgparseCompletion(Cmd): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -57,34 +74,45 @@ class ArgparseCompletion(Cmd): """ if self.debug: return self.sport_item_strs - raise CompletionError("Debug must be true") + raise CompletionError("debug must be true") - # Parser for complete command - complete_parser = Cmd2ArgumentParser(description="Command demonstrating tab completion with argparse\n" - "Notice even the flags of this command tab complete") + # Parser for example command + example_parser = Cmd2ArgumentParser(description="Command demonstrating tab completion with argparse\n" + "Notice even the flags of this command tab complete") # Tab complete from a list using argparse choices. Set metavar if you don't # want the entire choices list showing in the usage text for this command. - complete_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE") + example_parser.add_argument('--choices', choices=food_item_strs, metavar="CHOICE", + help="tab complete using choices") # Tab complete from choices provided by a choices function and choices method - complete_parser.add_argument('--choices_function', choices_function=choices_function) - complete_parser.add_argument('--choices_method', choices_method=choices_method) + example_parser.add_argument('--choices_function', choices_function=choices_function, + help="tab complete using a choices_function") + example_parser.add_argument('--choices_method', choices_method=choices_method, + help="tab complete using a choices_method") # Tab complete using a completer function and completer method - complete_parser.add_argument('--completer_function', completer_function=completer_function) - complete_parser.add_argument('--completer_method', completer_method=Cmd.path_complete) + example_parser.add_argument('--completer_function', completer_function=completer_function, + help="tab complete using a completer_function") + example_parser.add_argument('--completer_method', completer_method=Cmd.path_complete, + help="tab complete using a completer_method") # Demonstrate raising a CompletionError while tab completing - complete_parser.add_argument('--completion_error', choices_method=choices_completion_error) + example_parser.add_argument('--completion_error', choices_method=choices_completion_error, + help="raise a CompletionError while tab completing if debug is False") # Demonstrate returning CompletionItems instead of strings - complete_parser.add_argument('--completion_item', choices_function=choices_completion_item, metavar="ITEM_ID", - descriptive_header="Description") + example_parser.add_argument('--completion_item', choices_function=choices_completion_item, metavar="ITEM_ID", + descriptive_header="Description", + help="demonstrate use of CompletionItems") + + # Demonstrate use of arg_tokens dictionary + example_parser.add_argument('--arg_tokens', choices_function=choices_arg_tokens, + help="demonstrate use of arg_tokens dictionary") - @with_argparser(complete_parser) - def do_complete(self, _: argparse.Namespace) -> None: - """The complete command""" + @with_argparser(example_parser) + def do_example(self, _: argparse.Namespace) -> None: + """The example command""" self.poutput("I do nothing") -- cgit v1.2.1 From 19312f442be58590f4373e2455c4ee14e3397174 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 13 Feb 2020 16:43:23 -0500 Subject: Updated documentation --- examples/basic_completion.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/basic_completion.py b/examples/basic_completion.py index f21f75fc..615f949d 100755 --- a/examples/basic_completion.py +++ b/examples/basic_completion.py @@ -3,14 +3,14 @@ """ A simple example demonstrating how to enable tab completion by assigning a completer function to do_* commands. This also demonstrates capabilities of the following completer methods included with cmd2: -- flag_based_complete -- index_based_complete - delimiter_completer +- flag_based_complete (see note below) +- index_based_complete (see note below) -For an example integrating tab completion with argparse, see argparse_completion.py +flag_based_complete() and index_based_complete() are basic methods and should only be used if you are not +familiar with argparse. The recommended approach for tab completing positional tokens and flags is to use +argparse-based completion. For an example integrating tab completion with argparse, see argparse_completion.py """ -import functools - import cmd2 # List of strings used with completion functions -- cgit v1.2.1 From 7fe5cf7c1ab7d7a68991d1aeebaa338f7c5d2fd0 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 13 Feb 2020 18:37:50 -0500 Subject: Fixed missing import --- examples/basic_completion.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/basic_completion.py b/examples/basic_completion.py index 615f949d..e021828b 100755 --- a/examples/basic_completion.py +++ b/examples/basic_completion.py @@ -11,6 +11,8 @@ flag_based_complete() and index_based_complete() are basic methods and should on familiar with argparse. The recommended approach for tab completing positional tokens and flags is to use argparse-based completion. For an example integrating tab completion with argparse, see argparse_completion.py """ +import functools + import cmd2 # List of strings used with completion functions -- cgit v1.2.1