Repository: python-cmd2/cmd2
Pull request #1007 (2020-10-10 03:35:13)

Format multiline doc strings to match style of other help messages
Changed cmd2 do_cmd to dedent doc strings using `pydoc.getdoc` in order for the output of multi-line doc strings to look similar to argparse or a single line doc string. It continues prioritizing `help_func` functions over doc strings.

Before patch:
```
(cmd2) ➜  cmd2 git:(docstr_fmt) ✗ python examples/help_categories.py
(Cmd) help findleakers
Find Leakers command
(Cmd) help sslconnectorciphers

        SSL Connector Ciphers command is an example of a command that contains
        multiple lines of help information for the user. Each line of help in a
        contiguous set of lines will be printed and aligned in the verbose output
        provided with 'help --verbose'

        This is after a blank line and won't de displayed in the verbose help
```

After patch:
```shell
(cmd2) ➜  cmd2 git:(docstr_fmt) ✗ python examples/help_categories.py
(Cmd) help findleakers
Find Leakers command
(Cmd) help sslconnectorciphers
SSL Connector Ciphers command is an example of a command that contains
multiple lines of help information for the user. Each line of help in a
contiguous set of lines will be printed and aligned in the verbose output
provided with 'help --verbose'

This is after a blank line and won't de displayed in the verbose help
```


Patch:
diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py
index 8e23eca0..c8f5a9bd 100644
--- a/cmd2/cmd2.py
+++ b/cmd2/cmd2.py
@@ -34,6 +34,7 @@ import glob
 import inspect
 import os
 import pickle
+import pydoc
 import re
 import sys
 import threading
@@ -3155,17 +3156,21 @@ class Cmd(cmd.Cmd):
                 # Set end to blank so the help output matches how it looks when "command -h" is used
                 self.poutput(completer.format_help(tokens), end='')
 
+            # If there is a help func delegate to do_help
+            elif help_func is not None:
+                super().do_help(args.command)
+
+            # If there's no help_func __doc__ then format and output it
+            elif func is not None and func.__doc__ is not None:
+                self.poutput(pydoc.getdoc(func))
+
             # If there is no help information then print an error
-            elif help_func is None and (func is None or not func.__doc__):
+            else:
                 err_msg = self.help_error.format(args.command)
 
                 # Set apply_style to False so help_error's style is not overridden
                 self.perror(err_msg, apply_style=False)
 
-            # Otherwise delegate to cmd base class do_help()
-            else:
-                super().do_help(args.command)
-
     def _help_menu(self, verbose: bool = False) -> None:
         """Show a list of commands which help can be displayed for"""
         cmds_cats, cmds_doc, cmds_undoc, help_topics = self._build_command_info()


Test patch:
diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py
index 034f7096..554f2ba7 100755
--- a/tests/test_cmd2.py
+++ b/tests/test_cmd2.py
@@ -981,6 +981,16 @@ class HelpApp(cmd2.Cmd):
     def do_undoc(self, arg):
         pass
 
+    def do_multiline_docstr(self, arg):
+        """
+        This documentation
+        is multiple lines
+        and there are no
+        tabs
+        """
+        pass
+
+
 @pytest.fixture
 def help_app():
     app = HelpApp()
@@ -1004,6 +1014,11 @@ def test_help_overridden_method(help_app):
     expected = normalize('This overrides the edit command and does nothing.')
     assert out == expected
 
+def test_help_multiline_docstring(help_app):
+    out, err = run_cmd(help_app, 'help multiline_docstr')
+    expected = normalize('This documentation\nis multiple lines\nand there are no\ntabs')
+    assert out == expected
+
 
 class HelpCategoriesApp(cmd2.Cmd):
     """Class for testing custom help_* methods which override docstring help."""