get_argparse_help_string

The command-line interface to our program.

Command line help

The program's command-line help is reproduced here:

> program-name --help
usage: pdoc [-h] [--verbose] {subcommand} ...

A program that does something.

positional arguments:
  {subcommand}
    subcommand  A subcommand

options:
  -h, --help    show this help message and exit
  --verbose     Increase verbosity of output

________________________________________________________________________

> program-name subcommand --help
usage: pdoc subcommand [-h] [--subarg SUBARG]

options:
  -h, --help       show this help message and exit
  --subarg SUBARG  An argument for the subcommand
 1import argparse
 2import textwrap
 3
 4
 5def get_argparse_help_string(
 6    name: str, parser: argparse.ArgumentParser, wrap: int = 80, wrap_indent: int = 8
 7) -> str:
 8    """Generate a docstring for an argparse parser that shows the help for the parser and all subparsers, recursively.
 9
10    Based on an idea from <https://github.com/pdoc3/pdoc/issues/89>
11
12    Arguments:
13    * `name`: The name of the program
14    * `parser`: The parser
15    * `wrap`: The number of characters to wrap the help text to (0 to disable)
16    * `wrap_indent`: The number of characters to indent the wrapped text
17    """
18
19    def get_parser_help_recursive(
20        parser: argparse.ArgumentParser, cmd: str = "", root: bool = True
21    ):
22        docstring = ""
23        if not root:
24            docstring += "\n" + "_" * 72 + "\n\n"
25        docstring += f"> {cmd} --help\n"
26        docstring += parser.format_help()
27
28        for action in parser._actions:
29            if isinstance(action, argparse._SubParsersAction):
30                for subcmd, subparser in action.choices.items():
31                    docstring += get_parser_help_recursive(
32                        subparser, f"{cmd} {subcmd}", root=False
33                    )
34        return docstring
35
36    docstring = get_parser_help_recursive(parser, name)
37
38    if wrap > 0:
39        wrapped = []
40        # From the textwrap docs:
41        # > If replace_whitespace is false,
42        # > newlines may appear in the middle of a line and cause strange output.
43        # > For this reason, text should be split into paragraphs
44        # > (using str.splitlines() or similar) which are wrapped separately.
45        for line in docstring.splitlines():
46            if line:
47                wrapped += textwrap.wrap(
48                    line,
49                    width=wrap,
50                    replace_whitespace=False,
51                    subsequent_indent=" " * wrap_indent,
52                )
53            else:
54                wrapped += [""]
55        return "\n".join(wrapped)
56    else:
57        return docstring
58
59
60def _make_parser() -> argparse.ArgumentParser:
61    """Return the ArgumentParser for this program."""
62    parser = argparse.ArgumentParser(description="A program that does something.")
63    parser.add_argument(
64        "--verbose", action="store_true", help="Increase verbosity of output"
65    )
66    subparsers = parser.add_subparsers()
67    subparser = subparsers.add_parser("subcommand", help="A subcommand")
68    subparser.add_argument("--subarg", help="An argument for the subcommand")
69    return parser
70
71
72__doc__ = f"""
73The command-line interface to our program.
74
75## Command line help
76
77The program's command-line help is reproduced here:
78
79```text
80{get_argparse_help_string("program-name", _make_parser())}
81```
82"""
def get_argparse_help_string( name: str, parser: argparse.ArgumentParser, wrap: int = 80, wrap_indent: int = 8) -> str:
 6def get_argparse_help_string(
 7    name: str, parser: argparse.ArgumentParser, wrap: int = 80, wrap_indent: int = 8
 8) -> str:
 9    """Generate a docstring for an argparse parser that shows the help for the parser and all subparsers, recursively.
10
11    Based on an idea from <https://github.com/pdoc3/pdoc/issues/89>
12
13    Arguments:
14    * `name`: The name of the program
15    * `parser`: The parser
16    * `wrap`: The number of characters to wrap the help text to (0 to disable)
17    * `wrap_indent`: The number of characters to indent the wrapped text
18    """
19
20    def get_parser_help_recursive(
21        parser: argparse.ArgumentParser, cmd: str = "", root: bool = True
22    ):
23        docstring = ""
24        if not root:
25            docstring += "\n" + "_" * 72 + "\n\n"
26        docstring += f"> {cmd} --help\n"
27        docstring += parser.format_help()
28
29        for action in parser._actions:
30            if isinstance(action, argparse._SubParsersAction):
31                for subcmd, subparser in action.choices.items():
32                    docstring += get_parser_help_recursive(
33                        subparser, f"{cmd} {subcmd}", root=False
34                    )
35        return docstring
36
37    docstring = get_parser_help_recursive(parser, name)
38
39    if wrap > 0:
40        wrapped = []
41        # From the textwrap docs:
42        # > If replace_whitespace is false,
43        # > newlines may appear in the middle of a line and cause strange output.
44        # > For this reason, text should be split into paragraphs
45        # > (using str.splitlines() or similar) which are wrapped separately.
46        for line in docstring.splitlines():
47            if line:
48                wrapped += textwrap.wrap(
49                    line,
50                    width=wrap,
51                    replace_whitespace=False,
52                    subsequent_indent=" " * wrap_indent,
53                )
54            else:
55                wrapped += [""]
56        return "\n".join(wrapped)
57    else:
58        return docstring

Generate a docstring for an argparse parser that shows the help for the parser and all subparsers, recursively.

Based on an idea from https://github.com/pdoc3/pdoc/issues/89

Arguments:

  • name: The name of the program
  • parser: The parser
  • wrap: The number of characters to wrap the help text to (0 to disable)
  • wrap_indent: The number of characters to indent the wrapped text