简体   繁体   中英

Python: Prettier argparse output

I'm writing a Django management command. When I try to run it with required args omitted, I get this output:

$ ./manage.py generatebooktemplate 
usage: manage.py generatebooktemplate [-h] [--version] [-v {0,1,2,3}]
                                      [--settings SETTINGS]
                                      [--pythonpath PYTHONPATH] [--traceback]
                                      [--no-color] --name NAME --width WIDTH
                                      --height HEIGHT --h-outer-margin
                                      H_OUTER_MARGIN --h-inner-margin
                                      H_INNER_MARGIN --v-outer-margin
                                      V_OUTER_MARGIN --v-inner-margin
                                      V_INNER_MARGIN --cols COLS --rows ROWS
manage.py generatebooktemplate: error: argument --name is required

See how in some places the arg name appears on one line and the placeholder appears on the next. It's kind of hard to read. Any way to fix this?

Note: I'm not referring to the help text I provide argparse, this question pertains to the autogenerated help output that's based on the arguments I define.

Help output is formatted by HelpFormatter (the default) or a subclass specified by the formatter_class argument. Officially, only the names of the classes are considered part of the public API; how they actually work are implementation details and not guaranteed to remain the same.

HelpFormatter.__init__ takes a width argument, but there seems to be no way to actually pass a value to it. You can't even patch the instance, since only the class is stored and it is instantiated on-demand in various places. You might try the following:

from functools import partial

p = ArgumentParser(formatter_class=partial(HelpFormatter, width=200))

to force a longer width. Scanning the code, I didn't seen anywhere that the formatter_class value was used other than as a callable to instantiate it.

As an alternative, you can define a subclass of ArgumentParser and override its _get_formatter method, which currently just instantiates a formatter with a single argument. Your subclass could provide a way of passing arguments to that call.

The problem is with the line wrapping in the function that formats the usage line.

HelpFormatter._format_usage

Unfortunately this function is long and fragile. It uses HelpFormatter._format_actions_usage to format the usage. The rest of the function splits this text into lines, taking into account _width and the current indent . It splits on spaces, regardless of whether they are between arguments or between flag and metavar.

Part of your problem is the large indent:

usage: manage.py generatebooktemplate 

generatebooktemplate is probably a subcommand (subparser) name.

Another issue is text width. Supposedly that's controllable, but as shown in another answer that requires some trickery.

You could write a custom usage line, but I don't think that gives you control over newlines. RawHelp and RawDescription help classes don't apply because usage uses its own line formatter.

You could play with argument metavar parameters, shortening the names in the usage. You could also play with the order in which these arguments are defined.

In your test code, use

parser.format_usage()
parser.print_usage()

to see the usage lines without having to generate an error.

In last resort you could subclass HelpFormatter and rewrite _format_usage to suit your needs. It will be easier if you don't use any mutually_exclusive_groups.

There is the formatter class of argparse which you can use to define your own help text formatting.

See also Python argparse: How to insert newline in the help text?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM