summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAutoCompleter.py28
-rwxr-xr-xexamples/tab_autocompletion.py27
2 files changed, 42 insertions, 13 deletions
diff --git a/AutoCompleter.py b/AutoCompleter.py
index 753c8e63..014ea8ab 100755
--- a/AutoCompleter.py
+++ b/AutoCompleter.py
@@ -2,7 +2,7 @@
import argparse
import re as _re
import sys
-from argparse import OPTIONAL, ZERO_OR_MORE, ONE_OR_MORE, REMAINDER, PARSER
+from argparse import OPTIONAL, ZERO_OR_MORE, ONE_OR_MORE, REMAINDER, PARSER, ArgumentError
from typing import List, Dict, Tuple, Callable, Union
from colorama import Fore
@@ -460,14 +460,14 @@ class AutoCompleter(object):
if action.option_strings:
flags = ', '.join(action.option_strings)
param = ''
- if action.nargs is None or action.nargs > 0:
+ if action.nargs is None or action.nargs != 0:
param += ' ' + str(action.dest).upper()
prefix = '{}{}'.format(flags, param)
else:
prefix = '{}'.format(str(action.dest).upper())
- prefix = ' {0: <{width}}'.format(prefix, width=20)
+ prefix = ' {0: <{width}} '.format(prefix, width=20)
pref_len = len(prefix)
help_lines = action.help.splitlines()
if len(help_lines) == 1:
@@ -533,7 +533,7 @@ class ACHelpFormatter(argparse.HelpFormatter):
# build full usage string
format = self._format_actions_usage
- action_usage = format(positionals + optionals, groups)
+ action_usage = format(positionals + required_options + optionals, groups)
usage = ' '.join([s for s in [prog, action_usage] if s])
# wrap the usage parts if it's too long
@@ -632,7 +632,11 @@ class ACHelpFormatter(argparse.HelpFormatter):
def _format_args(self, action, default_metavar):
get_metavar = self._metavar_formatter(action, default_metavar)
- if action.nargs is None:
+ if isinstance(action, _RangeAction) and \
+ action.nargs_min is not None and action.nargs_max is not None:
+ result = '{}{{{}..{}}}'.format('%s' % get_metavar(1), action.nargs_min, action.nargs_max)
+
+ elif action.nargs is None:
result = '%s' % get_metavar(1)
elif action.nargs == OPTIONAL:
result = '[%s]' % get_metavar(1)
@@ -787,3 +791,17 @@ class ACArgumentParser(argparse.ArgumentParser):
nargs_pattern = nargs_pattern.replace('-', '')
return nargs_pattern
return super(ACArgumentParser, self)._get_nargs_pattern(action)
+
+ def _match_argument(self, action, arg_strings_pattern):
+ # match the pattern for this action to the arg strings
+ nargs_pattern = self._get_nargs_pattern(action)
+ match = _re.match(nargs_pattern, arg_strings_pattern)
+
+ # raise an exception if we weren't able to find a match
+ if match is None:
+ if isinstance(action, _RangeAction) and \
+ action.nargs_min is not None and action.nargs_max is not None:
+ raise ArgumentError(action,
+ 'Expected between {} and {} arguments'.format(action.nargs_min, action.nargs_max))
+
+ return super(ACArgumentParser, self)._match_argument(action, arg_strings_pattern)
diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py
index a28f839d..7b978fed 100755
--- a/examples/tab_autocompletion.py
+++ b/examples/tab_autocompletion.py
@@ -34,18 +34,21 @@ class TabCompleteExample(cmd2.Cmd):
@with_argparser(suggest_parser)
def do_suggest(self, args):
+ """Suggest command demonstrates argparse customizations
+
+ See hybrid_suggest and orig_suggest to compare the help output.
+
+
+ """
if not args.type:
self.do_help('suggest')
def complete_suggest(self, text, line, begidx, endidx):
""" Adds tab completion to media"""
- print('1')
completer = AutoCompleter.AutoCompleter(TabCompleteExample.suggest_parser, 1)
- print('2')
+
tokens, _ = self.tokens_for_completion(line, begidx, endidx)
- print('22')
results = completer.complete_command(tokens, text, line, begidx, endidx)
- print('3')
return results
@@ -62,7 +65,7 @@ class TabCompleteExample(cmd2.Cmd):
'\tsingle value - maximum duration\n'
'\t[a, b] - duration range')
@with_argparser(suggest_parser_hybrid)
- def do_orig_suggest(self, args):
+ def do_hybrid_suggest(self, args):
if not args.type:
self.do_help('orig_suggest')
@@ -75,6 +78,10 @@ class TabCompleteExample(cmd2.Cmd):
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 '--'
+
suggest_parser_orig = argparse.ArgumentParser()
suggest_parser_orig.add_argument('-t', '--type', choices=['movie', 'show'], required=True)
@@ -147,7 +154,7 @@ class TabCompleteExample(cmd2.Cmd):
@with_argparser(media_parser)
def do_media(self, args):
- """Media management"""
+ """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
@@ -156,12 +163,16 @@ class TabCompleteExample(cmd2.Cmd):
# 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"""
- directors = ['J. J. Abrams', 'Irvin Kershner', 'George Lucas', 'Richard Marquand',
+ static_list_directors = ['J. J. Abrams', 'Irvin Kershner', 'George Lucas', 'Richard Marquand',
'Rian Johnson', 'Gareth Edwards']
choices = {'actor': self.query_actors,
- 'director': directors}
+ 'director': static_list_directors}
completer = AutoCompleter.AutoCompleter(TabCompleteExample.media_parser, arg_choices=choices)
tokens, _ = self.tokens_for_completion(line, begidx, endidx)