Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reorder Python argparse argument groups

I'm using argparse and I have a custom argument group required arguments. Is there any way to change the order of the argument groups in the help message? I think it is more logical to have the required arguments before optional arguments, but haven't found any documentation or questions to help.

For example, changing this:

usage: foo.py [-h] -i INPUT [-o OUTPUT]

Foo

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                      Output file name

required arguments:
  -i INPUT, --input INPUT
                      Input file name

to this:

usage: foo.py [-h] -i INPUT [-o OUTPUT]

Foo

required arguments:
  -i INPUT, --input INPUT
                      Input file name

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                      Output file name

(example taken from this question)

like image 769
Logan Avatar asked Aug 19 '16 20:08

Logan


3 Answers

This is admittedly a hack, and is reliant on the changeable internal implementation, but after adding the arguments, you can simply do:

parser._action_groups.reverse()

This will effectively make the required arguments group display above the optional arguments group. Note that this answer is only meant to be descriptive, not prescriptive.


Credit: answer by hpaulj

like image 68
Asclepius Avatar answered Nov 14 '22 18:11

Asclepius


The parser starts out with 2 argument groups, the usual positional and optionals. The -h help is added to optionals. When you do add_argument_group, a group is created (and returned to you). It is also appended to the parser._action_groups list.

When you ask for help (-h) parser.format_help() is called (you can do that as well in testing). Look for that method in argparse.py. That sets up the help message, and one step is:

    # 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()

So if we reorder the items in the parser._action_groups list, we will reorder the groups in the display. Since this is the only use of _action_groups it should be safe and easy. But some people aren't allowed to peak under the covers (look or change ._ attributes).

The proposed solution(s) is to make your own groups in the order you want to see them, and make sure that the default groups are empty (the add_help=False parameter). That's the only way to do this if you stick with the public API.

Demo:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('foo')
g1 = parser.add_argument_group('REQUIRED')
g1.add_argument('--bar', required=True)
g1.add_argument('baz', nargs=2)

print(parser._action_groups)   
print([group.title for group in parser._action_groups])
print(parser.format_help())

parser._action_groups.reverse()  # easy inplace change
parser.print_help()

Run result:

1504:~/mypy$ python stack39047075.py 

_actions_group list and titles:

[<argparse._ArgumentGroup object at 0xb7247fac>,
 <argparse._ArgumentGroup object at 0xb7247f6c>,
 <argparse._ArgumentGroup object at 0xb721de0c>]
['positional arguments', 'optional arguments', 'REQUIRED']

default help:

usage: stack39047075.py [-h] --bar BAR foo baz baz

positional arguments:
  foo

optional arguments:
  -h, --help  show this help message and exit

REQUIRED:
  --bar BAR
  baz

after reverse:

usage: stack39047075.py [-h] --bar BAR foo baz baz

REQUIRED:
  --bar BAR
  baz

optional arguments:
  -h, --help  show this help message and exit

positional arguments:
  foo
1504:~/mypy$ 

Another way to implement this is to define a ArgumentParser subclass with a new format_help method. In that method reorder the list used in that for action_group... loop.

like image 20
hpaulj Avatar answered Nov 14 '22 18:11

hpaulj


You might consider adding an explicit optional arguments group:

import argparse

parser = argparse.ArgumentParser(description='Foo', add_help=False)

required = parser.add_argument_group('required arguments')
required.add_argument('-i', '--input', help='Input file name', required=True)

optional = parser.add_argument_group('optional arguments')
optional.add_argument("-h", "--help", action="help", help="show this help message and exit")
optional.add_argument('-o', '--output', help='Output file name', default='stdout')

parser.parse_args(['-h'])

You can move the help action to your optional group as described here: Move "help" to a different Argument Group in python argparse

As you can see, the code produces the required output:

usage: code.py -i INPUT [-h] [-o OUTPUT]

Foo

required arguments:
  -i INPUT, --input INPUT
                        Input file name

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        Output file name
like image 13
taras Avatar answered Nov 14 '22 18:11

taras