Add smart help formatter for command parser

Some command help messages maybe have multiple line content,
the built-in argparse.HelpFormatter wrap and split the content
according to width, and ignore \n in the raw help message,
it merge multiple line content in one line to output,
that looks messy. SmartHelpFormatter keep the raw help message
format if it contain \n, and wrap long line like HelpFormatter
behavior.

Change-Id: Ib37913891bbf7a31b570404c4668c490d5ac859b
This commit is contained in:
Rui Chen 2017-04-07 16:42:26 +08:00
parent 6a39ba568c
commit 9d505a7faf
2 changed files with 69 additions and 0 deletions

View File

@ -65,6 +65,7 @@ class Command(object):
parser = argparse.ArgumentParser(
description=self.get_description(),
prog=prog_name,
formatter_class=_SmartHelpFormatter,
)
return parser
@ -88,3 +89,24 @@ class Command(object):
Return the value returned by :meth:`take_action` or 0.
"""
return self.take_action(parsed_args) or 0
class _SmartHelpFormatter(argparse.HelpFormatter):
"""Smart help formatter to output raw help message if help contain \n.
Some command help messages maybe have multiple line content, the built-in
argparse.HelpFormatter wrap and split the content according to width, and
ignore \n in the raw help message, it merge multiple line content in one
line to output, that looks messy. SmartHelpFormatter keep the raw help
message format if it contain \n, and wrap long line like HelpFormatter
behavior.
"""
def _split_lines(self, text, width):
lines = text.splitlines() if '\n' in text else [text]
wrap_lines = []
for each_line in lines:
wrap_lines.extend(
super(_SmartHelpFormatter, self)._split_lines(each_line, width)
)
return wrap_lines

View File

@ -17,6 +17,30 @@ class TestCommand(command.Command):
"""Description of command.
"""
def get_parser(self, prog_name):
parser = super(TestCommand, self).get_parser(prog_name)
parser.add_argument(
'long_help_argument',
help="Create a NIC on the server.\n"
"Specify option multiple times to create multiple NICs. "
"Either net-id or port-id must be provided, but not both.\n"
"net-id: attach NIC to network with this UUID\n"
"port-id: attach NIC to port with this UUID\n"
"v4-fixed-ip: IPv4 fixed address for NIC (optional)\n"
"v6-fixed-ip: IPv6 fixed address for NIC (optional)\n"
"none: (v2.37+) no network is attached\n"
"auto: (v2.37+) the compute service will automatically "
"allocate a network.\n"
"Specifying a --nic of auto or none "
"cannot be used with any other --nic value.",
)
parser.add_argument(
'regular_help_argument',
help="The quick brown fox jumps "
"over the lazy dog.",
)
return parser
def take_action(self, parsed_args):
return 42
@ -62,3 +86,26 @@ def test_get_name():
def test_run_return():
cmd = TestCommand(None, None, cmd_name='object action')
assert cmd.run(None) == 42
def test_smart_help_formatter():
cmd = TestCommand(None, None)
parser = cmd.get_parser('NAME')
expected_help_message = """
long_help_argument Create a NIC on the server.
Specify option multiple times to create multiple NICs.
Either net-id or port-id must be provided, but not
both.
net-id: attach NIC to network with this UUID
port-id: attach NIC to port with this UUID
v4-fixed-ip: IPv4 fixed address for NIC (optional)
v6-fixed-ip: IPv6 fixed address for NIC (optional)
none: (v2.37+) no network is attached
auto: (v2.37+) the compute service will automatically
allocate a network.
Specifying a --nic of auto or none cannot be used with
any other --nic value.
regular_help_argument
The quick brown fox jumps over the lazy dog.
"""
assert expected_help_message in parser.format_help()