0
votes

I'm trying to get the help screen of the sub-parsers to show the required arguments ABOVE (before) the optional args.

I followed the last answer given at Argparse: Required arguments listed under "optional arguments"?, but could not get the required args to appear above the optional args.

Here is my code snippet:

## using Python 3.6.3
import argparse
from argparse import RawTextHelpFormatter

main_parser = argparse.ArgumentParser(prog="myProg")
subparsers = main_parser.add_subparsers()

## common to all sub-parsers
common_parser = argparse.ArgumentParser(add_help=False)
common_parser.add_argument('foo')

optional = common_parser._action_groups.pop()
required = common_parser.add_argument_group('required arguments')
required.add_argument("-p", type=int, required=True, help='help for -p')
optional.add_argument('-x', help='help for -x')
common_parser._action_groups.append(optional) 

abcd_parser = subparsers.add_parser("abcd", parents=[common_parser])
wxyz_parser = subparsers.add_parser("wxyz", parents=[common_parser])

args = main_parser.parse_args()

The output is:

$ ./myProg abcd -h
usage: myProg abcd [-h] -p P [-x X] foo

positional arguments:
  foo

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

required arguments:
  -p P        help for -p

However, i would like the output to look like:

$ ./myProg abcd -h
usage: myProg abcd [-h] -p P [-x X] foo

positional arguments:
  foo

required arguments:
  -p P        help for -p

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

Is it possible to get the desired results? What needs to be done?

Thanks

--Andrew

1

1 Answers

0
votes

This isn't unique to subparsers. Added groups are always listed after the default ones.

In [2]: parser = argparse.ArgumentParser()

The help is produced by the format_help method:

In [3]: parser.format_help??
Signature: parser.format_help()
Docstring: <no docstring>
Source:   
    def format_help(self):
        formatter = self._get_formatter()

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

        # description
        formatter.add_text(self.description)

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

        # epilog
        formatter.add_text(self.epilog)

        # determine help from format above
        return formatter.format_help()

It loops through the _action_groups, and adds each to the formatter object.

For a freshly initiated parser, there are 2 groups:

In [4]: parser._action_groups
Out[4]: 
[<argparse._ArgumentGroup at 0x7f4dfc2a22e8>,
 <argparse._ArgumentGroup at 0x7f4dfc2a2400>]
In [5]: [g.title for g in parser._action_groups]
Out[5]: ['positional arguments', 'optional arguments']

The help action has also been added to the 'optional arguments' group.

Add a group, and the list is now:

In [6]: g1 = parser.add_argument_group('required arguments')
In [7]: [g.title for g in parser._action_groups]
Out[7]: ['positional arguments', 'optional arguments', 'required arguments']

So:

In [8]: parser.add_argument('foo');
In [9]: parser.add_argument('--bar');
In [10]: g1.add_argument('--baz',required=True);
In [11]: parser.print_help()
usage: ipython3 [-h] [--bar BAR] --baz BAZ foo

positional arguments:
  foo

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

required arguments:
  --baz BAZ

Changing the order requires either changing the format_help method, or (possibly) changing the order of elements in the _action_groups list.

For example, we could reverse that list IN_PLACE:

In [18]: parser._action_groups.reverse()
In [19]: [g.title for g in parser._action_groups]
Out[19]: ['required arguments', 'optional arguments', 'positional arguments']
In [20]: parser.print_help()
usage: ipython3 [-h] [--bar BAR] --baz BAZ foo

required arguments:
  --baz BAZ

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

positional arguments:
  foo

A custom reordering like this seems to work fine too:

In [22]: alist = parser._action_groups
In [23]: alist[:] = [alist[0], alist[2], alist[1]]

No guarantees. I'm just exploring options based my knowledge of the code.