pythonargparse

Grouping argparse subparser arguments


I have a script that has multiple commands, with each command taking it's own set of required and/or optional arguments, by using add_subparser.

=->test.py -h
usage: test.py [-h] <command> ...

positional arguments:
  <command>   Available Commands
    cmd1      Command 1
    cmd2      Command 2
    cmd3      Command 3
    cmd4      Command 4

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


=->test.py cmd1 -h
usage: test.py cmd1 [-h] --flag1 FLAG1

optional arguments:
  -h, --help     show this help message and exit
  --flag1 FLAG1  Test flag


=->test.py cmd2 -h
usage: test.py cmd2 [-h] [--flag2 FLAG2]

optional arguments:
  -h, --help     show this help message and exit
  --flag2 FLAG2  Test flag

I'd like to somehow separate these commands into groups so that users see something like the following:

=->test.py -h
usage: test.py [-h] <command> ...

First Group:
  cmd1      Command 1
  cmd2      Command 2

Second Group:
  cmd3      Command 3
  cmd4      Command 4

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

But, doesn't look like add_argument_group and add_subparsers work together.

Any way to achieve this?


Solution

  • You are right, argument groups and subparsers don't work together. That's because subparsers (or rather their names) are not arguments.

    The sp = parser.add_subparsers(...) command creates an argument, or technically an instance of a subclass of argparse.Action. This is a positional argument. The add_parser command creates a parser object (i.e. calls argparse.ArgumentParser), and adds it, along with its name (and aliases) to a dictionary owned by this action. And the names populate the choices attribute of the Action.

    That subparsers Action could belong to an argument group, but since there can only be one such action, it doesn't help you group the help lines.

    You can control, to some extent, the help by using a description, and omitting the help for subparsers

    import argparse
    
    description = """
    First Group:
      cmd1      Command 1
      cmd2      Command 2
    
    Second Group:
      cmd3      Command 3
      cmd4      Command 4"""
    
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
    sp = parser.add_subparsers(title='commands',description=description)
    sp.add_parser('cmd1')
    sp.add_parser('cmd2')
    sp.add_parser('cmd3')
    sp.add_parser('cmd4')
    
    parser.print_help()
    

    produces

    1343:~/mypy$ python stack32017020.py 
    usage: stack32017020.py [-h] {cmd1,cmd2,cmd3,cmd4} ...
    
    optional arguments:
      -h, --help            show this help message and exit
    
    commands:
    
      First Group:
        cmd1      Command 1
        cmd2      Command 2
    
      Second Group:
        cmd3      Command 3
        cmd4      Command 4
    
      {cmd1,cmd2,cmd3,cmd4}
    

    http://bugs.python.org/issue9341 - allow argparse subcommands to be grouped

    talks about doing what you want. The patch that I proposed isn't trivial. But you are welcome to test it.