Search code examples
pythonargparse

argparse choices structure of allowed values


Using argparse in relation to Python dependencies between groups using argparse, I have an argument part of some parser group of a parser - for example:

group_simulate.add_argument('-P',
                            help='simulate FC port down',
                            nargs=1,
                            metavar='fc_port_name',
                            dest='simulate')

How it's possible to use the choices to limit the choices to a list of parameters of the next structure:

1:m:"number between 1 and 10":p:"number between 1 and 4"

I have tried to use the range option but I couldn't find a way to create a list of choices that are acceptable

examples: legal parameters:

test.py -P 1:m:4:p:2

not legal parameters:

test.py -P 1:p:2
test.py -P abvds

Thank you very much for the help guys!


Solution

  • You can define a custom type that will raise an argparse.ArgumentTypeError if the string doesn't match the format you need.

    def SpecialString(v):
        fields = v.split(":")
        # Raise a value error for any part of the string
        # that doesn't match your specification. Make as many
        # checks as you need. I've only included a couple here
        # as examples.
        if len(fields) != 5:
            raise argparse.ArgumentTypeError("String must have 5 fields")
        elif not (1 <= int(fields[2]) <= 10):
            raise argparse.ArgumentTypeError("Field 3 must be between 1 and 10, inclusive")
        else:
            # If all the checks pass, just return the string as is
            return v
    
    group_simulate.add_argument('-P',
                            type=SpecialString,
                            help='simulate FC port down',
                            nargs=1,
                            metavar='fc_port_name',
                            dest='simulate')
    

    UPDATE: here's a full custom type to check the value. All checking is done in the regular expression, although it only gives one generic error message if any part is wrong.

    def SpecialString(v):
        import re  # Unless you've already imported re previously
        try:
            return re.match("^1:m:([1-9]|10):p:(1|2|3|4)$", v).group(0)
        except:
            raise argparse.ArgumentTypeError("String '%s' does not match required format"%(v,))