Python argparse value range help message appearanc

2019-04-10 15:51发布

问题:

I have an argument for a program that is an integer from 1-100 and I just don't like the way that it shows up in the -h help message when using argparse (it literally lists 0, 1, 2, 3, 4, 5,... etc)

Is there any way to change this or have it represented in another way?

Thanks

EDIT:

Here is the code for those who asked:

norse = parser.add_argument_group('Norse')
norse.add_argument('-n', '--norse', required=False, help='Run the Norse IPViking scan.', action='store_true')
norse.add_argument('--threshold', required=False, type=int, choices=range(0,101), help='Threshold (0-100) denoting at what threat level to provide additional data on an IP \
                        address. Default is 49.', default=49)

回答1:

Use the metavar parameter of add_argument().

For example:

norse = parser.add_argument_group('Norse')
norse.add_argument('-n', '--norse', required=False, help='Run the Norse IPViking scan.', action='store_true')
norse.add_argument('--threshold', required=False, type=int, choices=range(0,101),
                   metavar="[0-100]", 
                   help='Threshold (0-100) denoting at what threat level to provide additional data on an IP \
                        address. Default is 49.', default=49)

Test:

from argparse import ArgumentParser

norse = ArgumentParser()

norse.add_argument('-n', '--norse', required=False, help='Run the Norse IPViking scan.', action='store_true')
norse.add_argument('--threshold', required=False, type=int, choices=range(0,101), metavar="[0-100]", help='Threshold (0-100) denoting at what threat level to provide additional data on an IP address. Default is 49.', default=49)


norse.print_help()

Results

usage: -c [-h] [-n] [--threshold [0-100]]

optional arguments:
  -h, --help           show this help message and exit
  -n, --norse          Run the Norse IPViking scan.
  --threshold [0-100]  Threshold (0-100) denoting at what threat level to
                       provide additional data on an IP address. Default is
                       49.


回答2:

You can customize action, e.g:

#!/usr/bin/env python
import argparse


class Range(argparse.Action):
    def __init__(self, minimum=None, maximum=None, *args, **kwargs):
        self.min = minimum
        self.max = maximum
        kwargs["metavar"] = "[%d-%d]" % (self.min, self.max)
        super(Range, self).__init__(*args, **kwargs)

    def __call__(self, parser, namespace, value, option_string=None):
        if not (self.min <= value <= self.max):
            msg = 'invalid choice: %r (choose from [%d-%d])' % \
                (value, self.min, self.max)
            raise argparse.ArgumentError(self, msg)
        setattr(namespace, self.dest, value)


norse = argparse.ArgumentParser('Norse')
norse.add_argument('--threshold', required=False, type=int, min=0, max=100,
                   action=Range,
                   help='Threshold [%(min)d-%(max)d] denoting at what threat \
                         level to provide additional data on an IP address. \
                         Default is %(default)s.', default=49)
args = norse.parse_args()
print args

Test it:

~: user$ ./test.py --threshold 10
Namespace(threshold=10)
~: user$ ./test.py --threshold -1
usage: Norse [-h] [--threshold [0-100]]
Norse: error: argument --threshold: invalid choice: -1 (choose from [0-100])
~: user$ ./test.py -h
usage: Norse [-h] [--threshold [0-100]]

optional arguments:
  -h, --help           show this help message and exit
  --threshold [0-100]  Threshold [0-100] denoting at what threat level to
                       provide additional data on an IP address. Default is
                       49.


回答3:

With a custom type, it is easier to control the error message (via the ArgumentTypeError). I still need the metavar to control the usage display.

import argparse

def range_type(astr, min=0, max=101):
    value = int(astr)
    if min<= value <= max:
        return value
    else:
        raise argparse.ArgumentTypeError('value not in range %s-%s'%(min,max))

parser = argparse.ArgumentParser()
norse = parser.add_argument_group('Norse')
...
norse.add_argument('--range', type=range_type, 
    help='Value in range: Default is %(default)s.',
    default=49, metavar='[0-101]')
parser.print_help()
print parser.parse_args()

producing:

2244:~/mypy$ python2.7 stack25295487.py --ran 102
usage: stack25295487.py [-h] [-n] [--threshold [0:101]] [--range [0-101]]

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

Norse:
  ...
  --range [0-101]      Value in range: Default is 49.
usage: stack25295487.py [-h] [-n] [--threshold [0:101]] [--range [0-101]]
stack25295487.py: error: argument --range: value not in range 0-101

I could use functools.partial to customize the range values:

type=partial(range_type, min=10, max=90)


回答4:

Here are a couple ways you can do it instead

def parseCommandArgs():
    parser = argparse.ArgumentParser()
    parser.add_argument('-i', dest='myDest', choices=range(1,101), type=int, required=True, metavar='INT[1,100]', help='my help message')
    return parser.parse_args()

You can also use action instead, which I highly recommend since it allows more customization

def verify():
    class Validity(argparse.Action):
        def __call__(self, parser, namespace, values, option_string=None):
            if values < 1 or values > 100:
                # do something
                pass
    return Validity

def parseCommandArgs():
    parser = argparse.ArgumentParser()
    parser.add_argument('-i', dest='myDest', required=True, metavar='INT[1,100]', help='my help message', action=verify())
    return parser.parse_args()