Sort argparse help alphabetically

吃可爱长大的小学妹 提交于 2019-11-30 11:06:20

You can do this by providing a custom HelpFormatter class; the internals of which are officially undocumented. This means you are on your own when it comes to compatibility from Python version to version, but I find the interface quite stable:

from argparse import HelpFormatter
from operator import attrgetter

class SortingHelpFormatter(HelpFormatter):
    def add_arguments(self, actions):
        actions = sorted(actions, key=attrgetter('option_strings'))
        super(SortingHelpFormatter, self).add_arguments(actions)


p = argparse.ArgumentParser(...
    formatter_class=SortingHelpFormatter,
)

Here I sort on the option strings (('--dur', '-d'), etc.), but you could take your pick as to what you want to sort on. This simple sorting option puts the single-dash options last, like the -h option.

which outputs:

usage: [-h] [--first FIRST] [--dur DUR] [--title TITLE] [--interp]

Load duration curves and other plots

optional arguments:
  --dur DUR, -d DUR     Duration in Hours. Use -1 for all
  --first FIRST, -f FIRST
                        First Hour
  --interp, -i          Use linear interpolation for smoother curves
  --title TITLE, -t TITLE
                        Plot Title (for all plots), default=file name
  -h, --help            show this help message and exit

When you create the ArgumentParser class you can pass in a help formatter: http://docs.python.org/library/argparse.html#formatter-class

So apparently you can use one of the supplied formatters, but cannot override and replace them without reverse engineering a bit:

>>> h = argparse.ArgumentDefaultsHelpFormatter
>>> print h.__doc__
Help message formatter which adds default values to argument help.

    Only the name of this class is considered a public API. All the methods
    provided by the class are considered an implementation detail.

An alternative, definitely more ugly way to do it than proposed by @MartijnPieters:

p = argparse.ArgumentParser()

#add arguements here

for g in p._action_groups:
    g._group_actions.sort(key=lambda x:x.dest)

It may be nice to put this in a try/except clause as it's only formatting help, therefore it shouldn't really matter for the execution of the program if this piece of code fails on an AttributeError or something ...

This is similar to @mgilson's answer. I thought I had posted this earlier, but apparently not.

d = dict()
d['--first'] = ('-f', "type=int", "default=1", "help='First Hour'")
d['--dur'] = ('-d', type=int, default=-1, help='Duration in Hours. Use -1 for all')
# etc

for prim_option in sorted(d):
    p.add_arguments(prim_option, *d[prim_option])

You can adjust what exactly is used as the key in the dictionary, as well as the arguments to sorted and the exact structure of the call to add_arguments, to get the desired sorting order. This adheres to the publicly documented interface of argparse, but does add a layer to the process of defining your parser. (Depending on your philosophy, such separation of information about the options from the implementation of the parser might be a good thing.)

The order of arguments in the help is determined by the parser.format_help method:

Definition:  parser.format_help(self)
Source:
    def format_help(self):
        formatter = self._get_formatter()
        ...
        # positionals, optionals and user-defined groups
        for action_group in self._action_groups:
            formatter.start_section(action_group.title)
            formatter.add_text(action_group.description)
            formatter.add_arguments(action_group._group_actions)
            formatter.end_section()

help is created by fetching an formatter object, and then adding 'sections' to it. Here it loops through the _action_groups, putting each in its own section, and adding its actions (arguments) with the add_arguments method. The formatter is temporary, existing only to create a strings (usually multiple lines).

Action groups include the default postionals and optionals, plus any the the user creates. These groups are only used for help, not for parsing. So the action_group._group_actions list could be reordered without affecting parsing. (the parser has its own list of actions, parser._actions).

This confirms @mgilson's observation that sorting p._actions does not affect the help, but sorting the _group_actions does.

Sorting _actions will affect the usage (whether part of help or standalone):

    # usage
    formatter.add_usage(self.usage, self._actions,
                        self._mutually_exclusive_groups)

Note that action_groups are not passed to the usage section. The usage section does reorder its actions, displaying optionals first, then positionals.

Sort arguments before/during the add_argument stage if you want to control parsing order of positionals, and their order in the usage.

If you just want to control order in the help groups, then feel free to reorder things in the ._group_actions list, either before calling the formatter, or within it.

There have been other SO questions about controlling the order of actions in the usage. Some, for example, don't want the positionals ordered after optionals.

I agree that the Formatter class is cumbersom. But it is, for the most part, separate from the Parser class. So it could be rewritten with minimal effect on parsing. The existing Formatter subclasses just tweak low level methods, ones that control line wrapping and help line formating. The significant interface between parser and formatter are the format_usage and format_help methods, which are relatively simpler and highlevel.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!