20

I have the following code:

parser = argparse.ArgumentParser(description='Postfix Queue Administration Tool',
        prog='pqa',
        usage='%(prog)s [-h] [-v,--version]')
parser.add_argument('-l', '--list', action='store_true',
        help='Shows full overview of all queues')
parser.add_argument('-q', '--queue', action='store', metavar='<queue>', dest='queue',
        help='Show information for <queue>')
parser.add_argument('-d', '--domain', action='store', metavar='<domain>', dest='domain',
        help='Show information about a specific <domain>')
parser.add_argument('-v', '--version', action='version', version='%(prog)s 0.1')
args = parser.parse_args()

Which gives me output like this:

%./pqa                                                                                                                        
usage: pqa [-h] [-v,--version]

Postfix Queue Administration Tool

optional arguments:
  -h, --help            show this help message and exit
  -l, --list            Shows full overview of all queues
  -q <queue>, --queue <queue>
                        Show information for <queue>
  -d <domain>, --domain <domain>
                        Show information about a specific <domain>
  -v, --version         show program's version number and exit

I would very much like to know how I can 'group' commands that have two versions (ie. long options) which each also show a metavar.

This is mostly an aesthetic issue on my side, but I would still like to fix this. I have been reading manuals and texts on the internet, but either the information just isn't there or I am totally missing something here :)

3 Answers 3

25

Putting hpaulj's answer into actual code, something like this works:

class CustomHelpFormatter(argparse.HelpFormatter):
    def _format_action_invocation(self, action):
        if not action.option_strings or action.nargs == 0:
            return super()._format_action_invocation(action)
        default = self._get_default_metavar_for_optional(action)
        args_string = self._format_args(action, default)
        return ', '.join(action.option_strings) + ' ' + args_string

fmt = lambda prog: CustomHelpFormatter(prog)
parser = argparse.ArgumentParser(formatter_class=fmt)

To additionally extend the default column size for help variables, add constructor to CustomHelpFormatter:

def __init__(self, prog):
    super().__init__(prog, max_help_position=40, width=80)

Seeing it in action:

usage: bk set [-h] [-p] [-s r] [-f] [-c] [-b c] [-t x y] [-bs s] [-bc c]
              [--crop x1 y1 x2 y2] [-g u r d l]
              monitor [path]

positional arguments:
  monitor                    monitor number
  path                       input image path

optional arguments:
  -h, --help                 show this help message and exit
  -p, --preview              previews the changes without applying them
  -s, --scale r              scales image by given factor
  -f, --fit                  fits the image within monitor work area
  -c, --cover                makes the image cover whole monitor work area
  -b, --background c         selects background color
  -t, --translate x y        places the image at given position
  -bs, --border-size s       selects border width
  -bc, --border-color c      selects border size
  --crop x1 y1 x2 y2         selects crop area
  -g, --gap, --gaps u r d l  keeps "border" around work area
3
  • While this also does what I want, I feel that hpaulj's code in the accepted answer is easier to read and did exactly what I asked in my question. Thanks for the extra elaboration though! :) Jun 30, 2015 at 20:54
  • Nothing wrong with the accepted answer, but I much prefer this, although it took me a bit to work out that my default python version was 2.7 and this didn't play too nicely, when run under 3.4 it worked like a charm. Thanks!
    – Madivad
    Jun 24, 2016 at 9:38
  • Also, if you know how to mould to work in Python<3 it would be appreciated for other scripts that I can't convert
    – Madivad
    Jun 26, 2016 at 10:34
9

Another solution, using custom descriptions

if you set the metavar='', the help line becomes:

-q , --queue          Show information for <queue>

Here I suppress the regular help lines, and replace them with the description lines for a group:

parser = argparse.ArgumentParser(description='Postfix Queue Administration Tool',
        prog='pqa',
        usage='%(prog)s [-h] [-v,--version]',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        )
parser.add_argument('-l', '--list', action='store_true',
        help='Shows full overview of all queues')
g = parser.add_argument_group(title='information options',
        description='''-q, --queue <queue>     Show information for <queue>
-d, --domain <domain>   Show information about a specific <domain>''')
g.add_argument('-q', '--queue', action='store', metavar='', dest='queue',
        help=argparse.SUPPRESS)
g.add_argument('-d', '--domain', action='store', metavar='<domain>', dest='domain',
        help=argparse.SUPPRESS)
parser.add_argument('-v', '--version', action='version', version='%(prog)s 0.1')
parser.print_help()

usage: pqa [-h] [-v,--version]

Postfix Queue Administration Tool

optional arguments:
  -h, --help     show this help message and exit
  -l, --list     Shows full overview of all queues
  -v, --version  show program's version number and exit

information options:
  -q, --queue <queue>     Show information for <queue>
  -d, --domain <domain>   Show information about a specific <domain>

Or you could put that information in the regular description. You already are using a custom usage line.

3
  • I actually hadn't thought of making an empty metavar, but your second block actually makes way more sense and is a very neat solution! Aug 20, 2013 at 11:19
  • This worked great for me after hours of digging through cobwebs. Also allows easy coloring of any of the text with colorama and the like. 👍🏻
    – liquidRock
    Nov 16, 2021 at 2:29
  • @liquidRock, what a clever boy I was years ago :)
    – hpaulj
    Nov 16, 2021 at 3:57
2

Is the problem that <domain> is repeated in the help line?:

-d <domain>, --domain <domain>

The argparse HelpFormatter does not give the user much control over this part of the display. As you have shown, you can set the usage line, the help text, and the metavar.

You would have to subclass the HelpFormatter, and change one of the functions to produce something like:

-d, --domain <domain>

It doesn't look like a complicated change, probably to the HelpFormatter._format_action_invocation method. But you need to be more explicit about what you want.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.