cmd2
cmd2 copied to clipboard
Help text formatting question
Hello all,
I am looking for some help in formatting the help text automatically generated by the cmd2. I have a program which produces a help message as follows:
(Cmd) help foo
Usage: foo [-h] {ro-community, ip-address} ...
Commands for configuration
optional arguments:
-h, --help show this help message and exit
List of sub commands:
{ro-community, ip-address}
ro-community Remove ro-community string
ip-address Remove ip-address
Is it possible to format the help as follows:
(Cmd) help foo
Usage: foo [-h] (ro-community | ip-address) ...
Commands for configuration
optional arguments:
-h, --help show this help message and exit
List of sub commands:
ro-community Remove ro-community string
ip-address Remove ip-address
Or even:
(Cmd) help foo
Usage: foo [-h] COMMANDS OPTIONS
Commands for configuration
optional arguments:
-h, --help show this help message and exit
List of sub commands:
ro-community Remove ro-community string
ip-address Remove ip-address
An ideal scenario would be displaying all the help in a single screen some thing like the following
(Cmd) help foo
Usage: foo [-h] COMMANDS OPTIONS
Commands for configuration
optional arguments:
-h, --help show this help message and exit
List of sub commands:
ro-community [-h] <community-name>
ip-address [-h] <ip-address>
Also for this command help:
(Cmd) help foo ip-address
Usage: foo ip-address [-h] ip-address
positional arguments:
ip-address ip-address to remove
optional arguments:
-h, --help show this help message and exit
Is it possible to format this as:
(Cmd) help foo ip-address
Usage: foo ip-address [-h] <ip-address>
positional arguments:
ip-address ip-address to remove
optional arguments:
-h, --help show this help message and exit
The sample program I am testing with is as follows:
import cmd2
from cmd2 import (
CommandSet,
Cmd2ArgumentParser,
with_argparser,
with_default_category
)
class BasicApp(cmd2.Cmd):
def __init__(self):
super().__init__()
# Commands
listen_ip_parser = Cmd2ArgumentParser(
description="Set listen ip address", add_help=False
)
listen_ip_parser.add_argument("ip", help="ip address")
@with_argparser(listen_ip_parser)
def do_listen_ip(self, args):
"""Exit from harware interface."""
self._cmd.cmd_logger.info(f"listen-ip {args.ip}")
# foo command and its sub commands
remove_parser = Cmd2ArgumentParser()
remove_subparsers = remove_parser.add_subparsers(title="List of sub commands")
parser_ro_community = remove_subparsers.add_parser("ro-community", help="Remove ro-community string")
parser_ro_community.add_argument("community-name",help="ro-community name to remove")
def remove_ro_community(self, args):
"""Remove ro-community user"""
self._cmd.cmd_logger.info(f"foo ro-community {args.get('community-name')}")
parser_ro_community.set_defaults(func=remove_ro_community)
parser_ip_address = remove_subparsers.add_parser("ip-address", help="Remove ip-address")
parser_ip_address.add_argument("ip-address",help="ip-address to remove")
def remove_ip_address(self, args):
"""Remove ip-address user"""
self._cmd.cmd_logger.info(f"foo ip-address {args.get('ip-address')}")
parser_ip_address.set_defaults(func=remove_ip_address)
@with_argparser(remove_parser)
def do_foo(self,args):
"""Commands for configuration"""
func = getattr(args, 'func', None)
if func is not None:
# Call whatever subcommand function was selected
return func(self, args)
else:
# Foo subcommand was provided, so call help
return self._cmd.do_help('foo')
if __name__ == '__main__':
app = BasicApp()
app.cmdloop()
https://cmd2.readthedocs.io/en/stable/features/argument_processing.html#help-messages
Also, cmd2 doesn't handle help for sub-parsers very well. In this context when I say sub-parsers I mean commands with sub-commands like you have shown above, ie "foo ip-address" and "foo community" where "ip-address" and "community" are sub-commands. I had the same problem in an app I build using cmd2, I wanted a "theme" command and sub-commands for "edit", "delete", "list", "clone", etc.
To get the behavior I wanted, I had to over-ride the entire cmd2 help system. The good news is that it wasn't very hard. Here's a link to some code so you can see how I did it.
https://github.com/tomcatmanager/tomcatmanager/blob/main/src/tomcatmanager/interactive_tomcat_manager.py#L666C4-L681C53
And here's the output it generates:
tomcat-manager> help theme
usage: theme [-h] action ...
manage themes
positional arguments:
action
list list all themes
clone clone a theme from the gallery or from one of the built-in themes
edit edit a user theme
create create a new user theme
delete delete a user theme
dir show the theme directory
options:
-h, --help show this help message and exit
tomcat-manager> help theme clone
usage: theme clone [-h] name [new_name]
clone a theme from the gallery or from one of the built-in themes
positional arguments:
name name of the gallery or built-in theme to clone to user theme directory
new_name new name of the theme
options:
-h, --help show this help message and exit
tomcat-manager>
Question 1
How to remove {ro-community, ip-address} from the following help output.
(Cmd) help foo
Usage: foo [-h] {ro-community, ip-address} ...
Commands for configuration
optional arguments:
-h, --help show this help message and exit
List of sub commands:
{ro-community, ip-address}
ro-community Remove ro-community string
ip-address Remove ip-address
The braced-list of subcommands is the default value for metavar, which is set when calling add_subparsers().
You can set it to any string you want. If you set metavar to a blank string it will just print a blank line. cmd2 commands like alias set metavar to SUBCOMMAND. It's really up to you what you want it to be. If you don't want the metavar line to print at all, you'd have to write custom argparse help generation code.
remove_subparsers = remove_parser.add_subparsers(title="List of sub commands", metavar="YOUR STRING HERE")
Question 2
How to surround required arguments with < and > characters.
Usage: foo ip-address [-h] <ip-address>
Argparse by default surrounds optional arguments with braces, e.g. [optional_arg], and does not surround required arguments with any characters. To achieve this, you'd have to override some argparse functions.
Kotfu's Custom Help Code
@kotfu Perhaps I'm misunderstanding your change, but did you write that custom help code a while ago? I don't think it's needed anymore. cmd2 prints the help text for argparse subcommands.
For example:
(Cmd) help alias
Usage: alias [-h] SUBCOMMAND ...
Manage aliases
An alias is a command that enables replacement of a word by another string.
optional arguments:
-h, --help show this help message and exit
subcommands:
SUBCOMMAND
create create or overwrite an alias
delete delete aliases
list list aliases
See also:
macro
(Cmd) help alias list
Usage: alias list [-h] [names [...]]
List specified aliases in a reusable form that can be saved to a startup
script to preserve aliases across sessions
Without arguments, all aliases will be listed.
positional arguments:
names alias(es) to list
optional arguments:
-h, --help show this help message and exit
Thanks for the insightful answers, let me check and get back!
@kmvanbrunt Yes, I have had custom help code for quite a while. While cmd2 now can handle the subparsers help, I also have code to apply a theme to help text, and some other minor tweaks:
Closing since questions were answered.