summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcmd2/argparse_completer.py50
-rwxr-xr-xexamples/tab_autocompletion.py20
-rw-r--r--tests/test_autocompletion.py2
-rw-r--r--tests/test_bashcompletion.py8
4 files changed, 64 insertions, 16 deletions
diff --git a/cmd2/argparse_completer.py b/cmd2/argparse_completer.py
index a8a0f24a..bf6a0e90 100755
--- a/cmd2/argparse_completer.py
+++ b/cmd2/argparse_completer.py
@@ -472,8 +472,23 @@ class AutoCompleter(object):
if action.dest in self._arg_choices:
arg_choices = self._arg_choices[action.dest]
- if isinstance(arg_choices, tuple) and len(arg_choices) > 0 and callable(arg_choices[0]):
- completer = arg_choices[0]
+ # if arg_choices is a tuple
+ # Let's see if it's a custom completion function. If it is, return what it provides
+ # To do this, we make sure the first element is either a callable
+ # or it's the name of a callable in the application
+ if isinstance(arg_choices, tuple) and len(arg_choices) > 0 and \
+ (callable(arg_choices[0]) or
+ (isinstance(arg_choices[0], str) and hasattr(self._cmd2_app, arg_choices[0]) and
+ callable(getattr(self._cmd2_app, arg_choices[0]))
+ )
+ ):
+
+ if callable(arg_choices[0]):
+ completer = arg_choices[0]
+ elif isinstance(arg_choices[0], str) and callable(getattr(self._cmd2_app, arg_choices[0])):
+ completer = getattr(self._cmd2_app, arg_choices[0])
+
+ # extract the positional and keyword arguments from the tuple
list_args = None
kw_args = None
for index in range(1, len(arg_choices)):
@@ -481,14 +496,19 @@ class AutoCompleter(object):
list_args = arg_choices[index]
elif isinstance(arg_choices[index], dict):
kw_args = arg_choices[index]
- if list_args is not None and kw_args is not None:
- return completer(text, line, begidx, endidx, *list_args, **kw_args)
- elif list_args is not None:
- return completer(text, line, begidx, endidx, *list_args)
- elif kw_args is not None:
- return completer(text, line, begidx, endidx, **kw_args)
- else:
- return completer(text, line, begidx, endidx)
+ try:
+ # call the provided function differently depending on the provided positional and keyword arguments
+ if list_args is not None and kw_args is not None:
+ return completer(text, line, begidx, endidx, *list_args, **kw_args)
+ elif list_args is not None:
+ return completer(text, line, begidx, endidx, *list_args)
+ elif kw_args is not None:
+ return completer(text, line, begidx, endidx, **kw_args)
+ else:
+ return completer(text, line, begidx, endidx)
+ except TypeError:
+ # assume this is due to an incorrect function signature, return nothing.
+ return []
else:
return AutoCompleter.basic_complete(text, line, begidx, endidx,
self._resolve_choices_for_arg(action, used_values))
@@ -499,6 +519,16 @@ class AutoCompleter(object):
if action.dest in self._arg_choices:
args = self._arg_choices[action.dest]
+ # is the argument a string? If so, see if we can find an attribute in the
+ # application matching the string.
+ if isinstance(args, str):
+ try:
+ args = getattr(self._cmd2_app, args)
+ except AttributeError:
+ # Couldn't find anything matching the name
+ return []
+
+ # is the provided argument a callable. If so, call it
if callable(args):
try:
if self._cmd2_app is not None:
diff --git a/examples/tab_autocompletion.py b/examples/tab_autocompletion.py
index f3302533..65679e4f 100755
--- a/examples/tab_autocompletion.py
+++ b/examples/tab_autocompletion.py
@@ -96,6 +96,15 @@ class TabCompleteExample(cmd2.Cmd):
},
}
+ 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'
+ ]
+
def instance_query_actors(self) -> List[str]:
"""Simulating a function that queries and returns a completion values"""
return actors
@@ -225,9 +234,18 @@ class TabCompleteExample(cmd2.Cmd):
required=True)
actor_action = vid_movies_add_parser.add_argument('actor', help='Actors', nargs='*')
+ vid_movies_load_parser = vid_movies_commands_subparsers.add_parser('load')
+ movie_file_action = vid_movies_load_parser.add_argument('movie_file', help='Movie database')
+
# 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, instance_query_actors)
+ setattr(actor_action, argparse_completer.ACTION_ARG_CHOICES, 'instance_query_actors')
+
+ # tag the file property with a custom completion function 'delimeter_complete' provided by cmd2.
+ setattr(movie_file_action, argparse_completer.ACTION_ARG_CHOICES,
+ ('delimiter_complete',
+ {'delimiter': '/',
+ 'match_against': file_list}))
vid_movies_delete_parser = vid_movies_commands_subparsers.add_parser('delete')
diff --git a/tests/test_autocompletion.py b/tests/test_autocompletion.py
index 1d0c9678..5132a1d8 100644
--- a/tests/test_autocompletion.py
+++ b/tests/test_autocompletion.py
@@ -246,7 +246,7 @@ def test_autcomp_pos_consumed(cmd2_app):
def test_autcomp_pos_after_flag(cmd2_app):
text = 'Joh'
- line = 'media movies add -d "George Lucas" -- "Han Solo" PG "Emilia Clarke" "{}'.format(text)
+ line = 'video movies add -d "George Lucas" -- "Han Solo" PG "Emilia Clarke" "{}'.format(text)
endidx = len(line)
begidx = endidx - len(text)
diff --git a/tests/test_bashcompletion.py b/tests/test_bashcompletion.py
index ceae2aa9..298bdf1e 100644
--- a/tests/test_bashcompletion.py
+++ b/tests/test_bashcompletion.py
@@ -139,15 +139,13 @@ def test_invalid_ifs(parser1, mock):
@pytest.mark.parametrize('comp_line, exp_out, exp_err', [
('media ', 'movies\013shows', ''),
('media mo', 'movies', ''),
+ ('media movies list -a "J', '"John Boyega"\013"Jake Lloyd"', ''),
+ ('media movies list ', '', ''),
('media movies add ', '\013\013 ', '''
Hint:
TITLE Movie Title'''),
- ('media movies list -a "J', '"John Boyega"\013"Jake Lloyd"', ''),
- ('media movies list ', '', '')
])
def test_commands(parser1, capfd, mock, comp_line, exp_out, exp_err):
- completer = CompletionFinder()
-
mock.patch.dict(os.environ, {'_ARGCOMPLETE': '1',
'_ARGCOMPLETE_IFS': '\013',
'COMP_TYPE': '63',
@@ -157,6 +155,8 @@ def test_commands(parser1, capfd, mock, comp_line, exp_out, exp_err):
mock.patch.object(os, 'fdopen', my_fdopen)
with pytest.raises(SystemExit):
+ completer = CompletionFinder()
+
choices = {'actor': query_actors, # function
}
autocompleter = AutoCompleter(parser1, arg_choices=choices)