Repository: radiasoft/pykern

Issue:
pkcli docstring printing too greedy
When using the `pkcli` interaction with the middl project, I'm finding that the help printing seems a bit too greedy, printing the entire docstring instead of just the function summary. The command is below:

```bash
(middl) [joshec@roentgen middl]$ middlsoft train -h
usage: middlsoft train [-h] {regressor,vrae} ...
positional arguments:
  {regressor,vrae}
    regressor       Train and run regressor to map latent space back to data
                    Args: configuration (str): path to configuration file in
                    model directory no_cuda (bool): whether or not to use
                    CUDA. Defaults to False. cuda_device (int): CUDA device
                    index. Defaults to -1 (all). do_write (bool): write
                    regressor output to file. Defaults to False. use_sklearn
                    (bool): use scikit-learn MLPRegressor vs pytorch. Defaults
                    to False. datadir (str): alternative directory to find
                    data in. Defaults to None. profile (bool): perform simple
                    profiling of pytorch model. Defaults to False. Returns:
                    None
    vrae
optional arguments:
  -h, --help        show this help message and exit
```

This printing occurs on the disvae_losses branch of the middl project: [middl/middlsoft/pkcli/train.py](https://github.com/radiasoft/middl/blob/d8ebd67e8dff4ff563fc7be78220f785c1fb5ad3/middlsoft/pkcli/train.py#L253-L274)

Comments:

Patch:
diff --git a/pykern/pkcli/__init__.py b/pykern/pkcli/__init__.py
index cb8bd7e..27cbf90 100644
--- a/pykern/pkcli/__init__.py
+++ b/pykern/pkcli/__init__.py
@@ -66,6 +66,42 @@ def command_error(fmt, *args, **kwargs):
     raise CommandError(fmt.format(*args, **kwargs))
 
 
+class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
+                      argparse.RawDescriptionHelpFormatter):
+    def _expand_help(self, action):
+        return super()._expand_help(action).split('\n')[0]
+
+class CustomParser(argparse.ArgumentParser):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.program = kwargs.copy()
+        self.options = []
+
+    def format_help(self):
+        f = argh.PARSER_FORMATTER(prog=self.prog)
+        if not self.description:
+            f = CustomFormatter(prog=self.prog)
+        f.add_usage(
+            self.usage,
+            self._actions,
+            self._mutually_exclusive_groups
+            )
+        f.add_text(self.description)
+        for a in self._action_groups:
+            f.start_section(a.title)
+            f.add_text(a.description)
+            f.add_arguments(a._group_actions)
+            f.end_section()
+        f.add_text(self.epilog)
+        if not self.description:
+            return f.format_help().replace('positional arguments', 'commands')
+        return f.format_help()
+
+    def print_help(self):
+        print(self.format_help())
+
+
 def main(root_pkg, argv=None):
     """Invokes module functions in :mod:`pykern.pkcli`
 
@@ -90,8 +126,7 @@ def main(root_pkg, argv=None):
     if not cli:
         return 1
     prog = prog + ' ' + module_name
-    parser = argparse.ArgumentParser(
-        prog=prog, formatter_class=argh.PARSER_FORMATTER)
+    parser = CustomParser(prog)
     cmds = _commands(cli)
     dc = _default_command(cmds, argv)
     if dc:


Test patch:
diff --git a/tests/pkcli_data/p1/pkcli/conf1.py b/tests/pkcli_data/p1/pkcli/conf1.py
index 3feccce..057bb50 100644
--- a/tests/pkcli_data/p1/pkcli/conf1.py
+++ b/tests/pkcli_data/p1/pkcli/conf1.py
@@ -5,11 +5,21 @@ last_cmd = None
 from pykern.pkdebug import pkdp
 
 def cmd1(arg1):
+    """Subject line for cmd1
+
+    Args:
+        arg1
+    """
     global last_cmd
     last_cmd = cmd1
     return
 
 def cmd2():
+    """Subject line for cmd2
+
+    Args:
+        -
+    """
     global last_cmd
     last_cmd = cmd2
     return
diff --git a/tests/pkcli_test.py b/tests/pkcli_test.py
index fcaeb56..09d6087 100644
--- a/tests/pkcli_test.py
+++ b/tests/pkcli_test.py
@@ -42,7 +42,7 @@ def test_main2(capsys):
     _dev(rp, [], None, all_modules, capsys)
     _dev(rp, ['--help'], None, all_modules, capsys)
     _dev(rp, ['conf1'], SystemExit, r'cmd1,cmd2.*too few', capsys)
-    _dev(rp, ['conf1', '-h'], SystemExit, r'\{cmd1,cmd2\}.*positional arguments', capsys)
+    _dev(rp, ['conf1', '-h'], SystemExit, r'\{cmd1,cmd2\}.*commands', capsys)
     if six.PY2:
         _dev(rp, ['not_found'], None, r'no module', capsys)
     else:
@@ -91,8 +91,9 @@ def _dev(root_pkg, argv, exc, expect, capsys):
     out, err = capsys.readouterr()
     if not err:
         err = out
-    assert re.search(expect, err, flags=re.IGNORECASE+re.DOTALL) is not None, \
-         'Looking for {} in err={}'.format(expect, err)
+    assert re.search('Args.*arg1', err, flags=re.DOTALL) is None, \
+        'failure to ignore arguments and only print subject. out: {}'.format(err)
+    pkunit.pkre(expect, err)
 
 
 def _main(root_pkg, argv):