Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python argparse: Lots of choices results in ugly help output

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.

like image 914
Deleted Avatar asked Feb 20 '12 18:02

Deleted


People also ask

How do you write help in Argparse?

As before, argparse automatically creates a --help option and documents your command-line interface (including the --upper-case option you just added). Try invoking aquarium.py with the --help option again to receive the updated help text: python3 aquarium.py --help.

What does Nargs do in Argparse?

Number of Arguments If you want your parameters to accept a list of items you can specify nargs=n for how many arguments to accept. Note, if you set nargs=1 , it will return as a list not a single value.

What is Metavar in Argparse Python?

Metavar: It provides a different name for optional argument in help messages.

What does Argparse parse_args return?

Adding arguments Later, calling parse_args() will return an object with two attributes, integers and accumulate . The integers attribute will be a list of one or more ints, and the accumulate attribute will be either the sum() function, if --sum was specified at the command line, or the max() function if it was not.


2 Answers

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.

like image 100
user2463717 Avatar answered Sep 20 '22 17:09

user2463717


There is no need to subclass anything. Simply pass a metavar argument with the string you want to appear in the help message.

See the argparse documentation for details.

like image 26
Ernest A Avatar answered Sep 18 '22 17:09

Ernest A