summaryrefslogtreecommitdiff
path: root/cmd2/cmd2.py
diff options
context:
space:
mode:
Diffstat (limited to 'cmd2/cmd2.py')
-rwxr-xr-xcmd2/cmd2.py76
1 files changed, 36 insertions, 40 deletions
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index a0a49a51..c7b759c1 100755
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -119,6 +119,9 @@ CMD_ATTR_HELP_CATEGORY = 'help_category'
# The argparse parser for the command
CMD_ATTR_ARGPARSER = 'argparser'
+# Whether or not tokens are unquoted before sending to argparse
+CMD_ATTR_PRESERVE_QUOTES = 'preserve_quotes'
+
def categorize(func: Union[Callable, Iterable[Callable]], category: str) -> None:
"""Categorize a function.
@@ -225,8 +228,9 @@ def with_argparser_and_unknown_args(argparser: argparse.ArgumentParser, *,
# Set the command's help text as argparser.description (which can be None)
cmd_wrapper.__doc__ = argparser.description
- # Mark this function as having an argparse ArgumentParser
+ # Set some custom attributes for this command
setattr(cmd_wrapper, CMD_ATTR_ARGPARSER, argparser)
+ setattr(cmd_wrapper, CMD_ATTR_PRESERVE_QUOTES, preserve_quotes)
return cmd_wrapper
@@ -283,8 +287,9 @@ def with_argparser(argparser: argparse.ArgumentParser, *,
# Set the command's help text as argparser.description (which can be None)
cmd_wrapper.__doc__ = argparser.description
- # Mark this function as having an argparse ArgumentParser
+ # Set some custom attributes for this command
setattr(cmd_wrapper, CMD_ATTR_ARGPARSER, argparser)
+ setattr(cmd_wrapper, CMD_ATTR_PRESERVE_QUOTES, preserve_quotes)
return cmd_wrapper
@@ -1431,7 +1436,8 @@ class Cmd(cmd.Cmd):
if func is not None and argparser is not None:
import functools
compfunc = functools.partial(self._autocomplete_default,
- argparser=argparser)
+ argparser=argparser,
+ preserve_quotes=getattr(func, CMD_ATTR_PRESERVE_QUOTES))
else:
compfunc = self.completedefault
@@ -1588,13 +1594,17 @@ class Cmd(cmd.Cmd):
self.pexcept(e)
return None
- def _autocomplete_default(self, text: str, line: str, begidx: int, endidx: int,
- argparser: argparse.ArgumentParser) -> List[str]:
+ def _autocomplete_default(self, text: str, line: str, begidx: int, endidx: int, *,
+ argparser: argparse.ArgumentParser, preserve_quotes: bool) -> List[str]:
"""Default completion function for argparse commands"""
from .argparse_completer import AutoCompleter
completer = AutoCompleter(argparser, self)
- tokens, _ = self.tokens_for_completion(line, begidx, endidx)
- return completer.complete_command(tokens, text, line, begidx, endidx)
+ tokens, raw_tokens = self.tokens_for_completion(line, begidx, endidx)
+
+ # To have tab-completion parsing match command line parsing behavior,
+ # use preserve_quotes to determine if we parse the quoted or unquoted tokens.
+ tokens_to_parse = raw_tokens if preserve_quotes else tokens
+ return completer.complete_command(tokens_to_parse, text, line, begidx, endidx)
def get_all_commands(self) -> List[str]:
"""Return a list of all commands"""
@@ -2658,42 +2668,32 @@ class Cmd(cmd.Cmd):
strs_to_match = list(topics | visible_commands)
return utils.basic_complete(text, line, begidx, endidx, strs_to_match)
- def complete_help_subcommand(self, text: str, line: str, begidx: int, endidx: int) -> List[str]:
+ def complete_help_subcommand(self, text: str, line: str, begidx: int, endidx: int,
+ parsed_args: argparse.Namespace) -> List[str]:
"""Completes the subcommand argument of help"""
- # Get all tokens through the one being completed
- tokens, _ = self.tokens_for_completion(line, begidx, endidx)
-
- if not tokens:
+ # Make sure we have a command whose subcommands we will complete
+ parsed_args.command = parsed_args.command[0]
+ if not parsed_args.command:
return []
- # Must have at least 3 args for 'help command subcommand'
- if len(tokens) < 3:
+ # Check if this command uses argparse
+ func = self.cmd_func(parsed_args.command)
+ argparser = getattr(func, CMD_ATTR_ARGPARSER, None)
+ if func is None or argparser is None:
return []
- # Find where the command is by skipping past any flags
- cmd_index = 1
- for cur_token in tokens[cmd_index:]:
- if not cur_token.startswith('-'):
- break
- cmd_index += 1
-
- if cmd_index >= len(tokens):
+ # Get all tokens through the one being completed
+ tokens, _ = self.tokens_for_completion(line, begidx, endidx)
+ if not tokens:
return []
- command = tokens[cmd_index]
- matches = []
-
- # Check if this command uses argparse
- func = self.cmd_func(command)
- argparser = getattr(func, CMD_ATTR_ARGPARSER, None)
-
- if func is not None and argparser is not None:
- from .argparse_completer import AutoCompleter
- completer = AutoCompleter(argparser, self)
- matches = completer.complete_command_help(tokens[cmd_index:], text, line, begidx, endidx)
+ # Get the index of the command
+ cmd_index = tokens.index(parsed_args.command)
- return matches
+ from .argparse_completer import AutoCompleter
+ completer = AutoCompleter(argparser, self)
+ return completer.complete_subcommand_help(tokens[cmd_index:], text, line, begidx, endidx)
help_parser = Cmd2ArgumentParser(description="List available commands or provide "
"detailed help for a specific command")
@@ -2959,14 +2959,11 @@ class Cmd(cmd.Cmd):
choice = int(response)
if choice < 1:
raise IndexError
- result = fulloptions[choice - 1][0]
- break
+ return fulloptions[choice - 1][0]
except (ValueError, IndexError):
self.poutput("{!r} isn't a valid choice. Pick a number between 1 and {}:".format(
response, len(fulloptions)))
- return result
-
def _get_read_only_settings(self) -> str:
"""Return a summary report of read-only settings which the user cannot modify at runtime.
@@ -4121,8 +4118,7 @@ class Cmd(cmd.Cmd):
if getattr(func, CMD_ATTR_HELP_CATEGORY, None) == category:
self.disable_command(cmd_name, message_to_print)
- # noinspection PyUnusedLocal
- def _report_disabled_command_usage(self, *args, message_to_print: str, **kwargs) -> None:
+ def _report_disabled_command_usage(self, *_args, message_to_print: str, **_kwargs) -> None:
"""
Report when a disabled command has been run or had help called on it
:param args: not used