Skip to content

Commit 2d38b72

Browse files
committed
Merge branch 'master' into alert_printer
2 parents 39e43d6 + 8b12ab9 commit 2d38b72

File tree

4 files changed

+54
-10
lines changed

4 files changed

+54
-10
lines changed

cmd2/argparse_completer.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,18 @@ def complete_command_help(self, tokens: List[str], text: str, line: str, begidx:
510510
return self.basic_complete(text, line, begidx, endidx, completers.keys())
511511
return []
512512

513+
def format_help(self, tokens: List[str]) -> str:
514+
"""Supports the completion of sub-commands for commands through the cmd2 help command."""
515+
for idx, token in enumerate(tokens):
516+
if idx >= self._token_start_index:
517+
if self._positional_completers:
518+
# For now argparse only allows 1 sub-command group per level
519+
# so this will only loop once.
520+
for completers in self._positional_completers.values():
521+
if token in completers:
522+
return completers[token].format_help(tokens)
523+
return self._parser.format_help()
524+
513525
@staticmethod
514526
def _process_action_nargs(action: argparse.Action, arg_state: _ArgumentState) -> None:
515527
if isinstance(action, _RangeAction):

cmd2/cmd2.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,7 +1615,7 @@ def complete_help(self, text: str, line: str, begidx: int, endidx: int) -> List[
16151615
try:
16161616
cmd_func = getattr(self, 'do_' + tokens[cmd_index])
16171617
parser = getattr(cmd_func, 'argparser')
1618-
completer = AutoCompleter(parser)
1618+
completer = AutoCompleter(parser, cmd2_app=self)
16191619
matches = completer.complete_command_help(tokens[1:], text, line, begidx, endidx)
16201620
except AttributeError:
16211621
pass
@@ -2275,14 +2275,9 @@ def do_help(self, arglist: List[str]) -> None:
22752275
# Check to see if this function was decorated with an argparse ArgumentParser
22762276
func = getattr(self, funcname)
22772277
if hasattr(func, 'argparser'):
2278-
# Function has an argparser, so get help based on all the arguments in case there are sub-commands
2279-
new_arglist = arglist[1:]
2280-
new_arglist.append('-h')
2281-
2282-
# Temporarily redirect all argparse output to both sys.stdout and sys.stderr to self.stdout
2283-
with redirect_stdout(self.stdout):
2284-
with redirect_stderr(self.stdout):
2285-
func(new_arglist)
2278+
completer = AutoCompleter(getattr(func, 'argparser'), cmd2_app=self)
2279+
2280+
self.poutput(completer.format_help(arglist))
22862281
else:
22872282
# No special behavior needed, delegate to cmd base class do_help()
22882283
cmd.Cmd.do_help(self, funcname[3:])

examples/subcommands.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,33 @@
3737
setattr(sport_arg, 'arg_choices', sport_item_strs)
3838

3939

40+
# create the top-level parser for the alternate command
41+
# The alternate command doesn't provide its own help flag
42+
base2_parser = argparse.ArgumentParser(prog='alternate', add_help=False)
43+
base2_subparsers = base2_parser.add_subparsers(title='subcommands', help='subcommand help')
44+
45+
# create the parser for the "foo" subcommand
46+
parser_foo2 = base2_subparsers.add_parser('foo', help='foo help')
47+
parser_foo2.add_argument('-x', type=int, default=1, help='integer')
48+
parser_foo2.add_argument('y', type=float, help='float')
49+
parser_foo2.add_argument('input_file', type=str, help='Input File')
50+
51+
# create the parser for the "bar" subcommand
52+
parser_bar2 = base2_subparsers.add_parser('bar', help='bar help')
53+
54+
bar2_subparsers = parser_bar2.add_subparsers(title='layer3', help='help for 3rd layer of commands')
55+
parser_bar2.add_argument('z', help='string')
56+
57+
bar2_subparsers.add_parser('apple', help='apple help')
58+
bar2_subparsers.add_parser('artichoke', help='artichoke help')
59+
bar2_subparsers.add_parser('cranberries', help='cranberries help')
60+
61+
# create the parser for the "sport" subcommand
62+
parser_sport2 = base2_subparsers.add_parser('sport', help='sport help')
63+
sport2_arg = parser_sport2.add_argument('sport', help='Enter name of a sport')
64+
setattr(sport2_arg, 'arg_choices', sport_item_strs)
65+
66+
4067
class SubcommandsExample(cmd2.Cmd):
4168
"""
4269
Example cmd2 application where we a base command which has a couple subcommands
@@ -74,6 +101,17 @@ def do_base(self, args):
74101
# No subcommand was provided, so call help
75102
self.do_help('base')
76103

104+
@cmd2.with_argparser(base2_parser)
105+
def do_alternate(self, args):
106+
"""Alternate command help"""
107+
func = getattr(args, 'func', None)
108+
if func is not None:
109+
# Call whatever subcommand function was selected
110+
func(self, args)
111+
else:
112+
# No subcommand was provided, so call help
113+
self.do_help('alternate')
114+
77115

78116
if __name__ == '__main__':
79117
app = SubcommandsExample()

tests/test_argparse.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,4 +258,3 @@ def test_subcommand_help(subcommand_app):
258258
def test_subcommand_invalid_help(subcommand_app):
259259
out = run_cmd(subcommand_app, 'help base baz')
260260
assert out[0].startswith('usage: base')
261-
assert out[1].startswith("base: error: invalid choice: 'baz'")

0 commit comments

Comments
 (0)