summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcmd2/argparse_completer.py2
-rwxr-xr-xcmd2/cmd2.py35
-rwxr-xr-xexamples/tab_autocompletion.py196
3 files changed, 102 insertions, 131 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index 6e53a518..e87e9c04 100755
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -415,7 +415,7 @@ class AutoCompleter(object):
return completion_results
def complete_command_help(self, tokens: List[str], text: str, line: str, begidx: int, endidx: int) -> List[str]:
- """Supports the completion of sub-commands for commands thhrough the cmd2 help command."""
+ """Supports the completion of sub-commands for commands through the cmd2 help command."""
for idx, token in enumerate(tokens):
if idx >= self._token_start_index:
if self._positional_completers:
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 10f790c0..288a506b 100755
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -267,8 +267,7 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser) -> Calla
cmd_wrapper.__doc__ = argparser.format_help()
- # Mark this function as having an argparse ArgumentParser (used by do_help)
- cmd_wrapper.__dict__['has_parser'] = True
+ # Mark this function as having an argparse ArgumentParser
setattr(cmd_wrapper, 'argparser', argparser)
return cmd_wrapper
@@ -305,8 +304,7 @@ def with_argparser(argparser: argparse.ArgumentParser) -> Callable:
cmd_wrapper.__doc__ = argparser.format_help()
- # Mark this function as having an argparse ArgumentParser (used by do_help)
- cmd_wrapper.__dict__['has_parser'] = True
+ # Mark this function as having an argparse ArgumentParser
setattr(cmd_wrapper, 'argparser', argparser)
return cmd_wrapper
@@ -1724,14 +1722,12 @@ class Cmd(cmd.Cmd):
compfunc = getattr(self, 'complete_' + command)
except AttributeError:
# There's no completer function, next see if the command uses argparser
- cmd_func = getattr(self, 'do_' + command)
- if hasattr(cmd_func, 'has_parser') and hasattr(cmd_func, 'argparser') and \
- getattr(cmd_func, 'has_parser'):
- # Command uses argparser, switch to the default argparse completer
+ try:
+ cmd_func = getattr(self, 'do_' + command)
argparser = getattr(cmd_func, 'argparser')
- compfunc = functools.partial(self._autocomplete_default,
- argparser=argparser)
- else:
+ # Command uses argparser, switch to the default argparse completer
+ compfunc = functools.partial(self._autocomplete_default, argparser=argparser)
+ except AttributeError:
compfunc = self.completedefault
# A valid command was not entered
@@ -1904,13 +1900,14 @@ class Cmd(cmd.Cmd):
matches = self.basic_complete(text, line, begidx, endidx, strs_to_match)
# check if the command uses argparser
- elif index >= subcmd_index and hasattr(self, 'do_' + tokens[cmd_index]) and\
- hasattr(getattr(self, 'do_' + tokens[cmd_index]), 'has_parser'):
- command = tokens[cmd_index]
- cmd_func = getattr(self, 'do_' + command)
- parser = getattr(cmd_func, 'argparser')
- completer = AutoCompleter(parser)
- matches = completer.complete_command_help(tokens[1:], text, line, begidx, endidx)
+ elif index >= subcmd_index:
+ try:
+ cmd_func = getattr(self, 'do_' + tokens[cmd_index])
+ parser = getattr(cmd_func, 'argparser')
+ completer = AutoCompleter(parser)
+ matches = completer.complete_command_help(tokens[1:], text, line, begidx, endidx)
+ except AttributeError:
+ pass
return matches
@@ -2561,7 +2558,7 @@ Usage: Usage: unalias [-a] name [name ...]
if funcname:
# Check to see if this function was decorated with an argparse ArgumentParser
func = getattr(self, funcname)
- if func.__dict__.get('has_parser', False):
+ if hasattr(func, 'argparser'):
# Function has an argparser, so get help based on all the arguments in case there are sub-commands
new_arglist = arglist[1:]
new_arglist.append('-h')
diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py
index 75ea1f00..17c8391d 100755
--- a/examples/tab_autocompletion.py
+++ b/examples/tab_autocompletion.py
@@ -120,15 +120,6 @@ class TabCompleteExample(cmd2.Cmd):
if not args.type:
self.do_help('suggest')
- def complete_suggest(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
- """ Adds tab completion to media"""
- completer = argparse_completer.AutoCompleter(TabCompleteExample.suggest_parser, 1)
-
- tokens, _ = self.tokens_for_completion(line, begidx, endidx)
- results = completer.complete_command(tokens, text, line, begidx, endidx)
-
- return results
-
# 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
@@ -148,15 +139,6 @@ class TabCompleteExample(cmd2.Cmd):
if not args.type:
self.do_help('orig_suggest')
- def complete_hybrid_suggest(self, text, line, begidx, endidx):
- """ Adds tab completion to media"""
- completer = argparse_completer.AutoCompleter(TabCompleteExample.suggest_parser_hybrid)
-
- tokens, _ = self.tokens_for_completion(line, begidx, endidx)
- results = completer.complete_command(tokens, text, line, begidx, endidx)
-
- return results
-
# 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 flaw or end the list it with '--'
@@ -175,20 +157,12 @@ class TabCompleteExample(cmd2.Cmd):
if not args.type:
self.do_help('orig_suggest')
- def complete_orig_suggest(self, text, line, begidx, endidx) -> List[str]:
- """ Adds tab completion to media"""
- completer = argparse_completer.AutoCompleter(TabCompleteExample.suggest_parser_orig)
-
- tokens, _ = self.tokens_for_completion(line, begidx, endidx)
- results = completer.complete_command(tokens, text, line, begidx, endidx)
-
- return results
-
###################################################################################
# The media command demonstrates a completer with multiple layers of subcommands
- # - This example uses a flat completion lookup dictionary
+ # - This example demonstrates how to tag a completion attribute on each action, enabling argument
+ # completion without implementing a complete_COMMAND function
- def _do_media_movies(self, args) -> None:
+ def _do_vid_media_movies(self, args) -> None:
if not args.command:
self.do_help('media movies')
elif args.command == 'list':
@@ -199,7 +173,7 @@ class TabCompleteExample(cmd2.Cmd):
', '.join(movie['director']),
'\n '.join(movie['actor'])))
- def _do_media_shows(self, args) -> None:
+ def _do_vid_media_shows(self, args) -> None:
if not args.command:
self.do_help('media shows')
@@ -215,72 +189,67 @@ class TabCompleteExample(cmd2.Cmd):
'\n '.join(ep_list)))
print()
- media_parser = argparse_completer.ACArgumentParser(prog='media')
+ video_parser = argparse_completer.ACArgumentParser(prog='media')
- media_types_subparsers = media_parser.add_subparsers(title='Media Types', dest='type')
+ video_types_subparsers = video_parser.add_subparsers(title='Media Types', dest='type')
- movies_parser = media_types_subparsers.add_parser('movies')
- movies_parser.set_defaults(func=_do_media_movies)
+ vid_movies_parser = video_types_subparsers.add_parser('movies')
+ vid_movies_parser.set_defaults(func=_do_vid_media_movies)
- movies_commands_subparsers = movies_parser.add_subparsers(title='Commands', dest='command')
+ vid_movies_commands_subparsers = vid_movies_parser.add_subparsers(title='Commands', dest='command')
- movies_list_parser = movies_commands_subparsers.add_parser('list')
+ vid_movies_list_parser = vid_movies_commands_subparsers.add_parser('list')
- movies_list_parser.add_argument('-t', '--title', help='Title Filter')
- movies_list_parser.add_argument('-r', '--rating', help='Rating Filter', nargs='+',
- choices=ratings_types)
- movies_list_parser.add_argument('-d', '--director', help='Director Filter')
- movies_list_parser.add_argument('-a', '--actor', help='Actor Filter', action='append')
+ 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)
+ # save a reference to the action object
+ director_action = vid_movies_list_parser.add_argument('-d', '--director', help='Director Filter')
+ actor_action = vid_movies_list_parser.add_argument('-a', '--actor', help='Actor Filter', action='append')
- movies_add_parser = movies_commands_subparsers.add_parser('add')
- movies_add_parser.add_argument('title', help='Movie Title')
- movies_add_parser.add_argument('rating', help='Movie Rating', choices=ratings_types)
- movies_add_parser.add_argument('-d', '--director', help='Director', nargs=(1, 2), required=True)
- movies_add_parser.add_argument('actor', help='Actors', nargs='*')
+ # tag the action objects with completion providers. This can be a collection or a callable
+ setattr(director_action, argparse_completer.ACTION_ARG_CHOICES, static_list_directors)
+ setattr(actor_action, argparse_completer.ACTION_ARG_CHOICES, query_actors)
- movies_delete_parser = movies_commands_subparsers.add_parser('delete')
+ 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)
- shows_parser = media_types_subparsers.add_parser('shows')
- shows_parser.set_defaults(func=_do_media_shows)
+ # save a reference to the action object
+ director_action = vid_movies_add_parser.add_argument('-d', '--director', help='Director', nargs=(1, 2),
+ required=True)
+ actor_action = vid_movies_add_parser.add_argument('actor', help='Actors', nargs='*')
- shows_commands_subparsers = shows_parser.add_subparsers(title='Commands', dest='command')
+ # tag the action objects with completion providers. This can be a collection or a callable
+ setattr(director_action, argparse_completer.ACTION_ARG_CHOICES, static_list_directors)
+ setattr(actor_action, argparse_completer.ACTION_ARG_CHOICES, query_actors)
- shows_list_parser = shows_commands_subparsers.add_parser('list')
+ vid_movies_delete_parser = vid_movies_commands_subparsers.add_parser('delete')
+
+ vid_shows_parser = video_types_subparsers.add_parser('shows')
+ vid_shows_parser.set_defaults(func=_do_vid_media_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')
@with_category(CAT_AUTOCOMPLETE)
- @with_argparser(media_parser)
- def do_media(self, args):
- """Media management command demonstrates multiple layers of subcommands being handled by AutoCompleter"""
+ @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('media')
-
- # This completer is implemented using a single dictionary to look up completion lists for all layers of
- # subcommands. For each argument, AutoCompleter will search for completion values from the provided
- # arg_choices dict. This requires careful naming of argparse arguments so that there are no unintentional
- # name collisions.
- def complete_media(self, text, line, begidx, endidx):
- """ Adds tab completion to media"""
- choices = {'actor': query_actors, # function
- 'director': TabCompleteExample.static_list_directors # static list
- }
- completer = argparse_completer.AutoCompleter(TabCompleteExample.media_parser, arg_choices=choices)
-
- tokens, _ = self.tokens_for_completion(line, begidx, endidx)
- results = completer.complete_command(tokens, text, line, begidx, endidx)
-
- return results
+ self.do_help('video')
###################################################################################
# The media command demonstrates a completer with multiple layers of subcommands
- # - This example demonstrates how to tag a completion attribute on each action, enabling argument
- # completion without implementing a complete_COMMAND function
+ # - This example uses a flat completion lookup dictionary
- def _do_vid_media_movies(self, args) -> None:
+ def _do_media_movies(self, args) -> None:
if not args.command:
self.do_help('media movies')
elif args.command == 'list':
@@ -291,7 +260,7 @@ class TabCompleteExample(cmd2.Cmd):
', '.join(movie['director']),
'\n '.join(movie['actor'])))
- def _do_vid_media_shows(self, args) -> None:
+ def _do_media_shows(self, args) -> None:
if not args.command:
self.do_help('media shows')
@@ -307,60 +276,65 @@ class TabCompleteExample(cmd2.Cmd):
'\n '.join(ep_list)))
print()
- video_parser = argparse_completer.ACArgumentParser(prog='media')
-
- 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_media_movies)
-
- vid_movies_commands_subparsers = vid_movies_parser.add_subparsers(title='Commands', dest='command')
+ media_parser = argparse_completer.ACArgumentParser(prog='media')
- vid_movies_list_parser = vid_movies_commands_subparsers.add_parser('list')
+ media_types_subparsers = media_parser.add_subparsers(title='Media Types', dest='type')
- 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)
- # save a reference to the action object
- director_action = vid_movies_list_parser.add_argument('-d', '--director', help='Director Filter')
- actor_action = vid_movies_list_parser.add_argument('-a', '--actor', help='Actor Filter', action='append')
+ movies_parser = media_types_subparsers.add_parser('movies')
+ movies_parser.set_defaults(func=_do_media_movies)
- # tag the action objects with completion providers. This can be a collection or a callable
- setattr(director_action, argparse_completer.ACTION_ARG_CHOICES, static_list_directors)
- setattr(actor_action, argparse_completer.ACTION_ARG_CHOICES, query_actors)
+ movies_commands_subparsers = movies_parser.add_subparsers(title='Commands', dest='command')
- 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)
+ movies_list_parser = movies_commands_subparsers.add_parser('list')
- # save a reference to the action object
- director_action = vid_movies_add_parser.add_argument('-d', '--director', help='Director', nargs=(1, 2), required=True)
- actor_action = vid_movies_add_parser.add_argument('actor', help='Actors', nargs='*')
+ movies_list_parser.add_argument('-t', '--title', help='Title Filter')
+ movies_list_parser.add_argument('-r', '--rating', help='Rating Filter', nargs='+',
+ choices=ratings_types)
+ movies_list_parser.add_argument('-d', '--director', help='Director Filter')
+ movies_list_parser.add_argument('-a', '--actor', help='Actor Filter', action='append')
- # tag the action objects with completion providers. This can be a collection or a callable
- setattr(director_action, argparse_completer.ACTION_ARG_CHOICES, static_list_directors)
- setattr(actor_action, argparse_completer.ACTION_ARG_CHOICES, query_actors)
+ movies_add_parser = movies_commands_subparsers.add_parser('add')
+ movies_add_parser.add_argument('title', help='Movie Title')
+ movies_add_parser.add_argument('rating', help='Movie Rating', choices=ratings_types)
+ movies_add_parser.add_argument('-d', '--director', help='Director', nargs=(1, 2), required=True)
+ movies_add_parser.add_argument('actor', help='Actors', nargs='*')
- vid_movies_delete_parser = vid_movies_commands_subparsers.add_parser('delete')
+ movies_delete_parser = movies_commands_subparsers.add_parser('delete')
- vid_shows_parser = video_types_subparsers.add_parser('shows')
- vid_shows_parser.set_defaults(func=_do_vid_media_shows)
+ shows_parser = media_types_subparsers.add_parser('shows')
+ shows_parser.set_defaults(func=_do_media_shows)
- vid_shows_commands_subparsers = vid_shows_parser.add_subparsers(title='Commands', dest='command')
+ shows_commands_subparsers = shows_parser.add_subparsers(title='Commands', dest='command')
- vid_shows_list_parser = vid_shows_commands_subparsers.add_parser('list')
+ shows_list_parser = shows_commands_subparsers.add_parser('list')
@with_category(CAT_AUTOCOMPLETE)
- @with_argparser(video_parser)
- def do_video(self, args):
- """Video management command demonstrates multiple layers of subcommands being handled by AutoCompleter"""
+ @with_argparser(media_parser)
+ def do_media(self, args):
+ """Media 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')
+ self.do_help('media')
+
+ # This completer is implemented using a single dictionary to look up completion lists for all layers of
+ # subcommands. For each argument, AutoCompleter will search for completion values from the provided
+ # arg_choices dict. This requires careful naming of argparse arguments so that there are no unintentional
+ # name collisions.
+ def complete_media(self, text, line, begidx, endidx):
+ """ Adds tab completion to media"""
+ choices = {'actor': query_actors, # function
+ 'director': TabCompleteExample.static_list_directors # static list
+ }
+ completer = argparse_completer.AutoCompleter(TabCompleteExample.media_parser, arg_choices=choices)
+
+ tokens, _ = self.tokens_for_completion(line, begidx, endidx)
+ results = completer.complete_command(tokens, text, line, begidx, endidx)
+
+ return results
###################################################################################
# The library command demonstrates a completer with multiple layers of subcommands