3

Is there a way to group the arguments from parent parsers into different groups? I don't have access to the parent parser itself, so I can't add the group there. (I'm using Google's OAuth2 framework).

Currently my code is:

# test.py
from argparse import ArgumentParser
from oauth2client import tools

parser = ArgumentParser(description="My program", parents=[tools.argparser])
parser.add_argument("--foo", help="Foo the data")
parser.add_argument("--bar", help="Bar the data")

parser.parse_args()

Which produces the following help:

$ python test.py -h                                    
usage: test.py [-h] [--auth_host_name AUTH_HOST_NAME]
               [--noauth_local_webserver]
               [--auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]]
               [--logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
               [--foo FOO] [--bar BAR]

My program

optional arguments:
  -h, --help            show this help message and exit
  --auth_host_name AUTH_HOST_NAME
                        Hostname when running a local web server.
  --noauth_local_webserver
                        Do not run a local web server.
  --auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]
                        Port web server should listen on.
  --logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
                        Set the logging level of detail.
  --foo FOO             Foo the data
  --bar BAR             Bar the data

So, I would like to create a group for arguments from the parent parser. Is is possible to group the arguments to look something like this?

optional arguments:
  -h, --help            show this help message and exit
  --foo FOO             Foo the data
  --bar BAR             Bar the data

authentication options:
  --auth_host_name AUTH_HOST_NAME
                        Hostname when running a local web server.
  --noauth_local_webserver
                        Do not run a local web server.
  --auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]
                        Port web server should listen on.
  --logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
                        Set the logging level of detail.

I know about parser groups, but I need to somehow get the arguments from one place to another something like:

auth_group = parser.add_argument_group('authentication options')
for arg in get_args_from_parser(tools.argparser):
    auth_group.add_argument(arg)

But I can't find a way to list arguments like that or copy them from one place to another.

dtauxe
  • 153
  • 11
  • Have you tried `add_argument_group`? Read its docs and try it out. https://stackoverflow.com/questions/39047075/reorder-python-argparse-argument-groups – hpaulj Nov 15 '18 at 20:36
  • The problem with those groups is that you create the group, then add the arguments to the group, not directly to the parser. So I need to (somehow) move arguments from the parent parser into this new group. AFAIK there is no way to get arguments from one parser and add them to another like that. I'll add that to the description. – dtauxe Nov 15 '18 at 21:11
  • Then my link is more relevant than I thought. All Actions are in the `parser` and in one of the existing Argument Groups. There are 2 default groups. See the `parser._action_groups`. The link talks about reordering the display of the groups, but you could also rename a group, and (I think) move an Action from one group to another. This is Python. Objects and their attributes are normally accessible, even if they have a `_` in the name. – hpaulj Nov 15 '18 at 21:22
  • Ah, I see now! Thanks! That has some side-effects, but that's to be expected since its not using a public attribute. If I can create a clean example, I'll add it as an answer. – dtauxe Nov 15 '18 at 21:52

1 Answers1

1

Thanks to @hpaulj and the question "Reorder Python argparse argument groups"

The way to do this depends on the implementation details of argparse, as I probably should have expected, so use at your own risk. But this works with the current Python 3.7.0.

We can rename the group(s) in the parent parser before adding it to my own:

# test.py
from argparse import ArgumentParser
from oauth2client import tools

tools.argparser._action_groups[1].title = 'authentication options'

parser = ArgumentParser(description="My program", parents=[tools.argparser])
parser.add_argument("--foo", help="Foo the data")
parser.add_argument("--bar", help="Bar the data")

parser.parse_args()

Which results in the output:

$ python test.py -h                                    
usage: test.py [-h] [--auth_host_name AUTH_HOST_NAME]
               [--noauth_local_webserver]
               [--auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]]
               [--logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
               [--foo FOO] [--bar BAR]

My program

optional arguments:
  -h, --help            show this help message and exit
  --foo FOO             Foo the data
  --bar BAR             Bar the data

authentication options:
  --auth_host_name AUTH_HOST_NAME
                        Hostname when running a local web server.
  --noauth_local_webserver
                        Do not run a local web server.
  --auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]
                        Port web server should listen on.
  --logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
                        Set the logging level of detail.

This works by renaming one of the default groups in the parent parser. The groups are stored in parser._action_groups, and are ordered ['positional arguments', 'optional arguments'] So we are renaming the second one (which is where these arguments are) before it is added to the new parser. This also stops the groups from being merged.

dtauxe
  • 153
  • 11