1

I am trying to create an option that takes two arguments. The first argument should be validated by choices, but the second argument is an arbitrary user-supplied value. For example.

> app -h
usage: app [--option {count,label} arg]

Correct usage examples:

> app --option count 1
> app --option count 912
> app --option label userfoo

I tried setting it up like this:

parser.add_argument('--option', choices=['count','label'], nargs=2)

This does not work, as it tries to validate BOTH arguments using the choices. The help string shows this:

usage: app [--option {count,label} {count,label}]

There are several ways I could do it manually:

  • remove the choices field and manually validate the first argument in code.
  • separate it into --option count --value 3, but that is awkward as --value is required by option but invalid without it. It is really a single option with two values
  • make --option have a compound value, for example --option count=3 and then parse the value

Part of what I want is to have the auto-generated help string show the choices for the first argument. I would also like for argparse to detect and report errors whenever possible with a minimum of custom code. The reason is that we have a very complex CLI, and this helps maintain consistency.

Is there a way to do this with argparse?

Alcamtar
  • 1,478
  • 13
  • 19
  • This SO question has a good discussion on how to do it an also why it might be bad to have multiple values for a single option: https://stackoverflow.com/questions/4109436/processing-multiple-values-for-one-single-option-using-getopt-optparse/14824041 – Rohan Varma Jan 19 '18 at 20:11
  • You could write a custom `type` function and/or `Action` class to handle this. Or you could do the value testing after parsing. But as you discovered `argparse` normally applies the same testing to all of the `nargs` values. – hpaulj Jan 19 '18 at 20:15

1 Answers1

1

parser._get_values does, when nargs is number:

        value = [self._get_value(action, v) for v in arg_strings]
        for v in value:
            self._check_value(action, v)

_get_value applies the type function, while _check_value test the choices. After this the values list is passed to the store Action.

So the normal processing applies the same type and choices test to each string.

I can imagine writing a type function that accepts both numbers and strings from a list. But it couldn't distinguish between the first and second arguments. A custom Action will see both, and could do further testing.

But often it's simpler to just do your own value testing after parsing. The parser doesn't have to do everything. It's primary function is to figure out what the user wants. Checking values and raising a standardized error is nice part of parsing, but isn't its primary purpose.

Also think about how you'd specify the usage/help for the expected input.

hpaulj
  • 221,503
  • 14
  • 230
  • 353