Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort argparse help alphabetically

I am using Python's (2.7) argparse facility and would like to automatically sort the help it produces alphabetically by option.

By default help entries are sorted in the order they are added*, as in:

p = argparse.ArgumentParser(description='Load duration curves and other plots')
p.add_argument('--first', '-f', type=int, default=1, help='First Hour')
p.add_argument('--dur', '-d', type=int, default=-1, help='Duration in Hours. Use -1 for all')
p.add_argument('--title', '-t', help='Plot Title (for all plots), default=file name')
p.add_argument('--interp', '-i', action="store_true", default=True, 
                help='Use linear interpolation for smoother curves')
...
args = p.parse_args()

Which when called as python script -h produces:

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

Load duration curves and other plots

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

Is it possible to automatically sort them alphabetically instead? This would be dur, first, h, interp, title.

*Obviously the work around is to manually maintain by adding entries using p.add_argument in alphabetical added order but I am trying to avoid doing so.

like image 802
Bryan P Avatar asked Sep 04 '12 17:09

Bryan P


4 Answers

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
like image 116
Martijn Pieters Avatar answered Oct 17 '22 06:10

Martijn Pieters


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.
like image 24
grieve Avatar answered Oct 17 '22 08:10

grieve


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 ...

like image 43
mgilson Avatar answered Oct 17 '22 06:10

mgilson


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.)

like image 1
chepner Avatar answered Oct 17 '22 07:10

chepner