Home > Net >  Optional positional argument, that only accepts values from a specified list
Optional positional argument, that only accepts values from a specified list

Time:08-03

I'm rewriting a legacy C program in Python 3, using argparse. The program takes zero, one or more positional arguments, that have to be from a specified list. Let's say the possible values are 'A', 'B', 'C', 'D' and 'E', for simplicity's sake. There are no other arguments in the legacy program, and I don't expect them in the new version. But you never know. :-)

If I add the argument without choices, like this:

p.add_argument("action", help = "What to do", nargs='*')

It works perfectly, I can supply zero, one or more of that argument.

But if I specify the choice list, like this:

p.add_argument("action", help = "What to do", choices=["A", "B", "C", "D", "E"], nargs='*')

I can no longer specify zero arguments. I get this error:

error: argument action: invalid choice: [] (choose from 'A', 'B', 'C', 'D', 'E')

Is there any way to be able to add an argument that will accept zero, one or more arguments from a specified list?

CodePudding user response:

I'm not sure there is an out-of-the-box way to achieve this but you can always do the check yourself:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("action", help="What to do", nargs='*')
cli_args = parser.parse_args([])  # no arguments
valid_choices = {"A", "B", "C", "D", "E"}
for arg in cli_args.action:
    if arg not in valid_choices:
        parser.error(f"argument action: invalid choice: "
                     f"'{arg}' (choose from {sorted(valid_choices)})")

print(cli_args.action)

This outputs

[]

And the following outputs for different arguments:

cli_args = parser.parse_args(['A'])
...
['A']
cli_args = parser.parse_args(['A', 'B'])
...
['A', 'B']
cli_args = parser.parse_args(['A', 'B', 'Z'])
...
usage: main.py [-h] [action [action ...]]
main.py: error: argument action: invalid choice: 'Z' (choose from ['A', 'B', 'C', 'D', 'E'])

CodePudding user response:

You can use the add_argument method once for each option in your list, supplying the whole list as possible choices for each argument, and then use the '?' for the nargs field

for example here:

import argparse

parser = argparse.ArgumentParser(sys.argv[0])
choices = ["A", "B", "C", "D", "E"]
for i, choice in enumerate(choices):
    parser.add_argument("arg" str(i)   choice, choices=choices, nargs='?')

args = parser.parse_args()

This will accept 0, 1, 2, ... len(choices) arguments. You will probably want to override the usage string if you go this route.

  • Related