diff --git a/cliff/command.py b/cliff/command.py index 84305638..691d6efb 100644 --- a/cliff/command.py +++ b/cliff/command.py @@ -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 diff --git a/cliff/tests/test_command.py b/cliff/tests/test_command.py index ef6b051c..55bb61ce 100644 --- a/cliff/tests/test_command.py +++ b/cliff/tests/test_command.py @@ -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()