Before and after: argparse help¶
argparse colourises the parts it generates (usage lines, section headers, metavars) but the text you supply stays undifferentiated. The generated frame is richer than the content it frames.
It also mangles your paragraphs. The default HelpFormatter collapses all whitespace, so a description with two paragraphs comes out as one. RawDescriptionHelpFormatter fixes that by reflowing nothing at all, which breaks on narrow terminals. There is no middle option.
Simplemark is that middle option, in about ten lines.
The formatter¶
smhelp.py:
import argparse
import simplemark
class SimpleMarkHelpFormatter(argparse.HelpFormatter):
"""Render descriptions, epilogs and per-option help as simplemark."""
def _fill_text(self, text, width, indent):
rendered = simplemark.render_text(simplemark.parse(text), width=width)
return "\n".join(indent + line for line in rendered.split("\n"))
def _split_lines(self, text, width):
return simplemark.render_text(simplemark.parse(text), width=width).split("\n")
_fill_text handles description and epilog; _split_lines handles each option's help. Those are the only two hooks argparse offers, and they are enough.
A parser to compare¶
arc.py:
import argparse
from smhelp import SimpleMarkHelpFormatter
DESCRIPTION = """Pack and unpack archives.
Reads ``member`` names using the encoding given by ``--metadata-encoding``.
Set *strict* to fail on the first bad entry rather than skipping it."""
ENCODING_HELP = (
"Encoding of member names, used by ``-l``, ``-e`` and ``-t``. "
"Defaults to *utf-8* on every platform."
)
def build(formatter):
parser = argparse.ArgumentParser(
prog="arc", description=DESCRIPTION, formatter_class=formatter
)
parser.add_argument("--metadata-encoding", metavar="<encoding>", help=ENCODING_HELP)
parser.add_argument("--strict", action="store_true", help="Fail on the first bad entry.")
return parser
if __name__ == "__main__":
import sys
chosen = SimpleMarkHelpFormatter if sys.argv[1] == "after" else argparse.HelpFormatter
build(chosen).print_help()
Before¶
The default formatter, on a 64-column terminal:
usage: arc [-h] [--metadata-encoding <encoding>] [--strict]
Pack and unpack archives. Reads ``member`` names using the
encoding given by ``--metadata-encoding``. Set *strict* to
fail on the first bad entry rather than skipping it.
options:
-h, --help show this help message and exit
--metadata-encoding <encoding>
Encoding of member names, used by
``-l``, ``-e`` and ``-t``. Defaults to
*utf-8* on every platform.
--strict Fail on the first bad entry.
Two things are wrong there. The blank line between the two paragraphs of the description is gone; argparse collapsed it. And the markup is showing: member, *strict*, -l are all printed literally at the user.
After¶
usage: arc [-h] [--metadata-encoding <encoding>] [--strict]
Pack and unpack archives.
Reads 'member' names using the encoding given by
'--metadata-encoding'. Set strict to fail on the first bad
entry rather than skipping it.
options:
-h, --help show this help message and exit
--metadata-encoding <encoding>
Encoding of member names, used by
'-l', '-e' and '-t'. Defaults to utf-8
on every platform.
--strict Fail on the first bad entry.
The paragraph break is back, the markup is gone, and the option names in the help text are now visibly option names rather than words. On a colour terminal they would be bold instead of quoted; argparse passes the real terminal width, and simplemark's style="auto" picks styling when stdout is a TTY and plain text when it is a pipe, honouring NO_COLOR.
Note that the option help column is only about 40 characters wide, and the text reflowed into it correctly. _split_lines receives the available width and simplemark uses it; there is no hard-coded assumption about 80 columns anywhere.
A narrower terminal¶
The same command at 48 columns, where the default formatter's fixed layout would hurt most:
usage: arc [-h]
[--metadata-encoding <encoding>]
[--strict]
Pack and unpack archives.
Reads 'member' names using the encoding given
by '--metadata-encoding'. Set strict to fail
on the first bad entry rather than skipping
it.
options:
-h, --help show this help message
and exit
--metadata-encoding <encoding>
Encoding of member
names, used by '-l',
'-e' and '-t'.
Defaults to utf-8 on
every platform.
--strict Fail on the first bad
entry.
Inline-only parsing¶
A one-line help= string has no block structure worth speaking of, and parse_inline skips straight to the inline layer:
python -c "
import simplemark
print(simplemark.parse_inline('Encoding used by \`\`-l\`\` and \`\`-t\`\`.'))
"
Use it when you are formatting a fragment rather than a document: it does no dedenting, produces no blocks, and returns a flat tuple of inline nodes you can style yourself.
Next¶
- Python API:
parse_inline,render_textand the node types - Syntax: what you can write in a help string