1

In a python script called simulate.py, I have the following arguments:

parser.add_argument('-s', '--simulators', nargs='+',
                     help='specify the simulators to use',
                     choices=['s1', 's2', 's3'])
parser_build = subparsers.add_parser('build')
parser_build.add_argument('-o3', '--optim', action='store_true')
parser_run = subparsers.add_parser('run')
parser_run.add_argument('-v', '--verbose', action='store_true')

The following command line options:

> ./simulate.py -s s1 s2 build

generates the following error message:

simulate.py: error: argument -s/--simulators: invalid choice: 'build'

How should I specify the simulators list in this case where I have another argument without option prefix (such as -c for exemple) ?

Manuel Selva
  • 18,554
  • 22
  • 89
  • 134

1 Answers1

0

As explained in: Argparse - do not catch positional arguments with `nargs`.

an nargs='+' optional (flagged argument) followed by a positional has problems. The optional consumes all the strings, leaving none for the positional. There is a proposed bug patch, but it hasn't been released.

The normal quick solution is to use '--' to mark the end of the optional and its arguments. Everything else is treated as positionals. You could also put some other flagged argument in between.

What complicates your case is that -s takes choices. And the positional is a subparsers, which also takes choices, ['build', 'run']. As a human you understand 's2' as a choice that belongs to -s, and 'build' as one of the parsers. But argparse does not allocate arguments based on choices; it just uses counts (the nargs). choices are checked after allocating the arguments.

I'd have to look at the code to see why '-s s1 s2 -- build' raises a subparsers choices error. Normally '--' is ignored. But apparently subparsers is not doing that. It is a special positional Action class.

==========================

I found the code that explains your '--' problem. In a function that converts strings to the type, there is a code block:

    # for everything but PARSER, REMAINDER args, strip out first '--'
    if action.nargs not in [PARSER, REMAINDER]:
        try:
            arg_strings.remove('--')
        except ValueError:
            pass

Your subparsers argument has nargs=PARSER, so the -- is not removed.

============================

I think you need to change -s so it accepts a fixed number of arguments. Or define some other optional, useful or not, that can mark its end. Feel free to look at the linked bug issue if you want to get into the argparse guts.

Community
  • 1
  • 1
hpaulj
  • 221,503
  • 14
  • 230
  • 353