summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2020-02-12 15:47:29 -0500
committerKevin Van Brunt <kmvanbrunt@gmail.com>2020-02-12 15:47:29 -0500
commit191f94abda1c4d565ea5b2dd1bd66e346db3b51b (patch)
tree8bf9b0bfa7a49dd4713e8b31616275cab5cd87c9
parentad0e2ae0d0d426fe08353fd82d1f9ff051be9108 (diff)
downloadcmd2-git-191f94abda1c4d565ea5b2dd1bd66e346db3b51b.tar.gz
Overhauling tab completion examples
-rwxr-xr-xREADME.md10
-rw-r--r--docs/features/argument_processing.rst3
-rw-r--r--docs/features/completion.rst10
-rwxr-xr-xexamples/basic_completion.py (renamed from examples/tab_completion.py)32
-rwxr-xr-xexamples/python_scripting.py1
-rwxr-xr-xexamples/tab_autocompletion.py268
6 files changed, 40 insertions, 284 deletions
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/tab_completion.py b/examples/basic_completion.py
index 1a25238f..4baec16c 100755
--- a/examples/tab_completion.py
+++ b/examples/basic_completion.py
@@ -1,24 +1,39 @@
#!/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
+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 flag and index based completion functions
+# 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')
@@ -58,6 +73,7 @@ class TabCompleteExample(cmd2.Cmd):
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"""
@@ -74,6 +90,14 @@ 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))
+
+ # 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
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())