Python argparse: Lots of choices results in ugly help output

Deleted picture Deleted · Feb 20, 2012 · Viewed 32.5k times · Source

I have this code which I am generally pleased with:

import argparse

servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer",
            "SkeppServer", "HavsServer", "PiratServer", "SvartServer", "NattServer", "SovServer" ]

parser = argparse.ArgumentParser(description="A program to update components on servers.")
group = parser.add_mutually_exclusive_group()
group.add_argument('-l', '--list', dest="update", action='store_false', default=False, help='list server components')
group.add_argument('-u', '--updatepom', dest="update", action='store_true', help='update server components')
parser.add_argument('-o', '--only', nargs='*', choices=servers, help='Space separated list of case sensitive server names to process')
parser.add_argument('-s', '--skip', nargs='*', choices=servers, help='Space separated list of case sensitive server names to exclude from processing')
args = parser.parse_args()

I like that the choice=servers validates the server names in the input for me, so that I won´t have to. However, having so many valid choices makes the help output look terrible:

usage: args.py [-h] [-l | -u]
               [-o [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]]
               [-s [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]]

A program to update components on servers.

optional arguments:
  -h, --help            show this help message and exit
  -l, --list            list server components
  -u, --updatepom       update server components
  -o [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]], --only [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]
                        Space separated list of case sensitive server names to
                        process
  -s [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]], --skip [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} [{ApaServer,BananServer,GulServer,SolServer,RymdServer,SkeppServer,HavsServer,PiratServer,SvartServer,NattServer,SovServer} ...]]
                        Space separated list of case sensitive server names to
                        exclude from processing

Which way would you recommend if I want:

  • Nice (mostly) auto-generated help output
  • Validation that the entries given to the -o or the -s options are in servers.

Bonus:

  • Would it be possible to have case insensitive string matching for the server names?

Append

I tried using michaelfilms suggestion where the -o -s options are removed from the above output and this part is added:

server optional arguments:
  Valid server names are: ApaServer, BananServer, GulServer, SolServer,
  RymdServer, SkeppServer, HavsServer, PiratServer, SvartServer,
  NattServer, SovServer

I think it looks pretty good, but I really need to provide help for -o and -s options as the user wouldn´t know about them otherwise. So I am not all the way there yet using this approach.

Answer

user2463717 picture user2463717 · Jun 7, 2013

I am basically repeating what Ernest said - to avoid the ugly long list of choices, set metavar='' for the choice-based arguments (though it won't get rid of the whitespace between the argument and comma (e.g. -o , instead of -o,). You can then describe available choices in detail in general description (RawDescriptionHelpFormatter is useful here if you want them listed with obvious indentation).

I do not understand why Ernest's answer was voted down. This code

import argparse

servers = [ "ApaServer", "BananServer", "GulServer", "SolServer", "RymdServer",
            "SkeppServer", "HavsServer", "PiratServer", "SvartServer", "NattServer", "SovServer" ]

parser = argparse.ArgumentParser(description="A program to update components on servers.")
group = parser.add_mutually_exclusive_group()
group.add_argument('-l', '--list', dest="update", action='store_false', default=False, help='list server components')
group.add_argument('-u', '--updatepom', dest="update", action='store_true', help='update server components')
parser.add_argument('-o', '--only', choices=servers, help='Space separated list of case sensitive server names to process.  Allowed values are '+', '.join(servers), metavar='')
parser.add_argument('-s', '--skip', choices=servers, help='Space separated list of case sensitive server names to exclude from processing.  Allowed values are '+', '.join(servers), metavar='')
args = parser.parse_args()

produces the following help output

usage: run.py [-h] [-l | -u] [-o] [-s]

A program to update components on servers.

optional arguments:
  -h, --help       show this help message and exit
  -l, --list       list server components
  -u, --updatepom  update server components
  -o , --only      Space separated list of case sensitive server names to
                   process. Allowed values are ApaServer, BananServer,
                   GulServer, SolServer, RymdServer, SkeppServer, HavsServer,
                   PiratServer, SvartServer, NattServer, SovServer
  -s , --skip      Space separated list of case sensitive server names to
                   exclude from processing. Allowed values are ApaServer,
                   BananServer, GulServer, SolServer, RymdServer, SkeppServer,
                   HavsServer, PiratServer, SvartServer, NattServer, SovServer

This is hopefully what original post was looking for.