22

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)
4
  • Can you post your code example? Commented Aug 13, 2014 at 20:29
  • API doesn't suggest anything, may be rather than using choices, you can implement your own check. Commented Aug 13, 2014 at 20:32
  • You could try looking at argparse.ArgumentDefaultsHelpFormatter, I haven't used it myself, but it might provide the kind of customization you need. Commented Aug 13, 2014 at 20:35
  • Yeah I was thinking about simply ditching the built in check and running my own, but it would be nice if I could keep it within the argparse... oh well. Commented Aug 13, 2014 at 20:37

5 Answers 5

38

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.
Sign up to request clarification or add additional context in comments.

2 Comments

metavar works for the usage and help. It doesn't help with the error message. More on this at bugs.python.org/issue16418 and bugs.python.org/issue16468
@hpaulj Thanks for the references. That's very interesting to know.
9

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)

Comments

8

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.

1 Comment

I would customize the type rather than the action.
2

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()

1 Comment

Thanks! 8 years later your metavar suggestion helped me with a similar problem where argparse was printing all ~65k port numbers in the help message.
0

I have written a library for this: argparse-range

>>> from argparse import ArgumentParser
>>> from argparse_range import range_action
>>> parser = ArgumentParser()
>>> parser.add_argument("rangedarg", action=range_action(0, 10), help="An argument")
>>> args = parser.parse_args(["0"])
>>> args.rangedarg
0
>>> parser.parse_args(["20"])
Traceback (most recent call last):
    ....
argparse.ArgumentTypeError: Invalid choice: 20 (must be in range 0..=10)

Helptext is added transparently:

foo.py --help

usage: foo.py [-h] rangedarg

positional arguments:
  rangedarg   An argument (must be in range 0..=10)

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

Other features include:

  • Handles default, metavar, and nargs consistently with default argparse handling
  • Type inference, with explicit overrides using the type argument

1 Comment

When linking to your own site or content (or content that you are affiliated with), you must disclose your affiliation in the answer in order for it not to be considered spam. Having the same text in your username as the URL or mentioning it in your profile is not considered sufficient disclosure under Stack Exchange policy.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.