summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Van Brunt <kmvanbrunt@gmail.com>2020-08-18 12:06:47 -0400
committerKevin Van Brunt <kmvanbrunt@gmail.com>2020-08-18 12:06:47 -0400
commitb5e08b06b4032d8656fa768814069b17ea613b62 (patch)
tree0f942f22d57d2686ed44dde2aec3583767d7d54f
parente8aa84b39872956cf881e845b8464a3807b58a6e (diff)
downloadcmd2-git-b5e08b06b4032d8656fa768814069b17ea613b62.tar.gz
Documented support for standalone functions being used as completers and choices_providers.
Added unit tests for this case.
-rw-r--r--cmd2/argparse_custom.py11
-rw-r--r--cmd2/utils.py6
-rw-r--r--tests/test_argparse_completer.py39
3 files changed, 47 insertions, 9 deletions
diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py
index 0687dc74..85b0d903 100644
--- a/cmd2/argparse_custom.py
+++ b/cmd2/argparse_custom.py
@@ -80,7 +80,7 @@ delimiter_complete)
path_filter=lambda path: os.path.isdir(path))
parser.add_argument('-o', '--options', completer=dir_completer)
-For `choices_provider` and `completer`, do not set them to a bound method. This
+For ``choices_provider`` and ``completer``, do not set them to a bound method. This
is because ArgparseCompleter passes the `self` argument explicitly to these
functions. When ArgparseCompleter calls one, it will detect whether it is bound
to a `Cmd` subclass or `CommandSet`. If bound to a `cmd2.Cmd subclass`, it will
@@ -89,7 +89,12 @@ subclass, it will pass the `CommandSet` instance as the `self` argument.
Therefore instead of passing something like `self.path_complete`, pass
`cmd2.Cmd.path_complete`.
-Of the 3 tab completion parameters, choices is the only one where argparse
+``choices_provider`` and ``completer`` functions can also be implemented as
+standalone functions (i.e. not a member of a class). In this case,
+ArgparseCompleter will pass its ``cmd2.Cmd`` app instance as the first
+positional argument.
+
+Of the 3 tab completion parameters, ``choices`` is the only one where argparse
validates user input against items in the choices list. This is because the
other 2 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
@@ -103,7 +108,7 @@ in __init__() of a custom action class.
- set_completer(action, func)
There are times when what's being tab completed is determined by a previous
-argument on the command line. In theses cases, Autocompleter can pass a
+argument on the command line. In theses cases, ArgparseCompleter can pass a
dictionary that maps the command line tokens up through the one being completed
to their argparse argument name. To receive this dictionary, your
choices/completer function should have an argument called arg_tokens.
diff --git a/cmd2/utils.py b/cmd2/utils.py
index d8d6b7cc..c5874d6e 100644
--- a/cmd2/utils.py
+++ b/cmd2/utils.py
@@ -104,12 +104,6 @@ class Settable:
:param choices: iterable of accepted values
:param choices_provider: function that provides choices for this argument
:param completer: tab completion function that provides choices for this argument
-
- Note:
- For choices_provider and completer, do not set them to a bound method. This is because
- ArgparseCompleter passes the self argument explicitly to these functions.
-
- Therefore instead of passing something like self.path_complete, pass cmd2.Cmd.path_complete.
"""
if val_type == bool:
val_type = str_to_bool
diff --git a/tests/test_argparse_completer.py b/tests/test_argparse_completer.py
index b896a9bd..a964aa46 100644
--- a/tests/test_argparse_completer.py
+++ b/tests/test_argparse_completer.py
@@ -13,6 +13,19 @@ from cmd2 import Cmd2ArgumentParser, CompletionError, CompletionItem, with_argpa
from cmd2.utils import StdSim
from .conftest import complete_tester, run_cmd
+# Data and functions for testing standalone choice_provider and completer
+standalone_choices = ['standalone', 'provider']
+standalone_completions = ['standalone', 'completer']
+
+
+# noinspection PyUnusedLocal
+def standalone_choice_provider(cli: cmd2.Cmd) -> List[str]:
+ return standalone_choices
+
+
+def standalone_completer(cli: cmd2.Cmd, text: str, line: str, begidx: int, endidx: int) -> List[str]:
+ return cli.basic_complete(text, line, begidx, endidx, standalone_completions)
+
# noinspection PyMethodMayBeStatic,PyUnusedLocal,PyProtectedMember
class AutoCompleteTester(cmd2.Cmd):
@@ -263,6 +276,17 @@ class AutoCompleteTester(cmd2.Cmd):
def do_mutex(self, args: argparse.Namespace) -> None:
pass
+ ############################################################################################################
+ # Begin code related to standalone functions
+ ############################################################################################################
+ standalone_parser = Cmd2ArgumentParser()
+ standalone_parser.add_argument('--provider', help='standalone provider', choices_provider=standalone_choice_provider)
+ standalone_parser.add_argument('--completer', help='standalone completer', completer=standalone_completer)
+
+ @with_argparser(standalone_parser)
+ def do_standalone(self, args: argparse.Namespace) -> None:
+ pass
+
@pytest.fixture
def ac_app():
@@ -935,3 +959,18 @@ def test_complete_command_help_no_tokens(ac_app):
completions = ac.complete_subcommand_help(tokens=[], text='', line='', begidx=0, endidx=0)
assert not completions
+
+
+@pytest.mark.parametrize('flag, completions', [
+ ('--provider', standalone_choices),
+ ('--completer', standalone_completions)
+])
+def test_complete_standalone(ac_app, flag, completions):
+ text = ''
+ line = 'standalone {} {}'.format(flag, text)
+ endidx = len(line)
+ begidx = endidx - len(text)
+
+ first_match = complete_tester(text, line, begidx, endidx, ac_app)
+ assert first_match is not None
+ assert ac_app.completion_matches == sorted(completions, key=ac_app.default_sort_key)