Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python argparse check choices before type

I'm trying to enable a user to pass in a function name. For some reason it seems that argparse performs the type check/conversion BEFORE it checks the choices. Is this a bug? Best thing to do?

import argparse

def foo():
  return 'foo'

def bar():
  return 'bar'

parser = argparse.ArgumentParser()
functions = {f.__name__:f for f in [foo, bar]}
parser.add_argument("function", type=lambda f: functions.get(f), help="which function", choices=functions)
args = parser.parse_args()
print(args.function())

This throws:

$ python blah.py foo
usage: blah.py [-h] {foo,bar}
blah.py: error: argument function: invalid choice: <function foo at 0x7f65746dd848> (choose from 'foo', 'bar')
like image 815
Him Avatar asked Apr 07 '16 14:04

Him


People also ask

What type does parse_args return?

parse_args() returns two values: options , an object containing values for all of your options—e.g. if --file takes a single string argument, then options. file will be the filename supplied by the user, or None if the user did not supply that option.

What does Argparse ArgumentParser () do?

After importing the library, argparse. ArgumentParser() initializes the parser so that you can start to add custom arguments. To add your arguments, use parser.

What are positional and optional arguments in Argparse?

Python argparse optional argument These are optional arguments. The module is imported. An argument is added with add_argument . The action set to store_true will store the argument as True , if present.


1 Answers

Yes, during parsing the type then choices order is clear and intentional (and not just incidental). When preparing to assign arg_strings to the namespace it calls _get_values, which does:

  def _get_values(self, action, arg_strings)
        .... (various nargs tests)
        value = self._get_value(action, arg_string)
        self._check_value(action, value)
        return value

where _get_value applies the action.type function, and _check_value tests

value not in action.choices  

For parsing choices only has to respond to the in (__contains__) expression.

So choices have to reflect values after conversion. If type is int, then choices=[1,2,3] is correct, ['1','2','3'] is not.

There are some (largely unresolved) bug issues over the display of the choices. Long lists, e.g. range(100) work in parsing, but don't display nicely. And display also requires that choices be iterable (e.g. a list, tuple, dictionary). This display issue affects the usage, the help and the error messages (each formats choices slightly differently).

metavar is your most powerful tool for replacing an undesirable choices list. I'd have to run a test case to see whether it solves things for all 3 situations.

like image 187
hpaulj Avatar answered Nov 03 '22 07:11

hpaulj