Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Command help (via -h) where `argparse` is range checking input port number

I'm using argparse to parse the inputs to my python3 program. I was recently asked to range check some of the numeric inputs, a seemingly good idea. Argparse has a facility to do just that.

The numeric inputs are port numbers, in the usual range 0-65535, so I altered my parse command line to :

import argparse
cmd_parser = argparse.ArgumentParser()
cmd_parser = add_argument('-p', help='Port number to connect to', dest='cmd_port', default=1234, type=int, choices=range(0,65536))
cmd_parser.parse_args(['-h'])

Now, however, when I request the help, I get flooded with all the possible values from argparse. eg.

optional arguments:
    -h, --help            show this help message and exit
    -p {0,1,2,3,4,5,6,7,8,9,10,11,12,13 ...
    65478,65479,65480,65481,65482,65483,65484,65485,65486,65487,65488,65489,
    65490,65491,65492,65493,65494,65495,65496,65497,65498,65499,65500,65501,
    65502,65503,65504,65505,65506,65507,65508,65509,65510,65511,65512,65513,
    65514,65515,65516,65517,65518,65519,65520,65521,65522,65523,65524,65525,
    65526,65527,65528,65529,65530,65531,65532,65533,65534,65535}
                    Port number to connect to
...

It lists every single port in that range. Is there a way to truncate this or make it realize its a range (0-65535) or for it to use ellipsis or something to make it a bit prettier? Is my only option to explicitly range check my inputs with if statements?

I've been googling this but I'm having trouble finding examples where people used argparse and specified choices. I also checked the documentation on argparse but didn't see anything useful. https://docs.python.org/2/library/argparse.html

like image 780
LawfulEvil Avatar asked Jun 07 '16 13:06

LawfulEvil


3 Answers

Use custom action...

import argparse

class PortAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if not 0 < values < 2**16:
            raise argparse.ArgumentError(self, "port numbers must be between 0 and 2**16")
        setattr(namespace, self.dest, values)

cmd_parser = argparse.ArgumentParser()
cmd_parser.add_argument('-p',
                        help='Port number to connect to',
                        dest='cmd_port',
                        default=1234,
                        type=int,
                        action=PortAction,
                        metavar="{0..65535}")

An invalid port number will display the error message based on the raised ArgumentError. If you enter a value of 65536, the following line will be printed:

error: argument -p: port numbers must be between 0 and 2**16

The usage and help messages will be printed based on the metavar displayed

like image 99
arewm Avatar answered Nov 12 '22 17:11

arewm


Just use int as type in add_argument, and manually verify it's in the permitted range. Or, use a type of your own, which has a constructor that does the checking for you, and a __int__ method for implicit conversion:

class portnumber:
    def __init__(self, string):
        self._val = int(string)
        if (not self._val > 0) or (not self.val < 2**16):
            raise argparse.ArgumentTypeError("port numbers must be integers between 0 and 2**16")
    def __int__(self):
        return self._val

...

parser.add_argument("-p",type=portnumber)
like image 25
Marcus Müller Avatar answered Nov 12 '22 17:11

Marcus Müller


Supply an explicit metavar argument instead of letting argparse produce one for you.

cmd_parser.add_argument('-p',
                        help='Port number to connect to',
                        dest='cmd_port',
                        default=1234,
                        type=int,
                        choices=range(0,65536),
                        metavar="{0..65535}")
like image 2
chepner Avatar answered Nov 12 '22 18:11

chepner