Reputation: 20117
I want to implement the example from the Argparse intro:
import argparse
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
const=sum, default=max,
help='sum the integers (default: find the max)')
args = parser.parse_args()
print(args.accumulate(args.integers))
But in my case, I'd like to have a wider range of possible function names to choose from.
def a(): ...
def b(): ...
def c(): ...
parser.add_argument('-f', '--func',
choices=[a, b, c],
required=True,
help="""Choose one of the specified function to be run.""")
parser.func()
Using it like that doesn't work as intended, I get
$ python program.py -f=a
program.py: error: argument -f/--func: invalid choice: 'a' (choose from
<function a at 0x7fa15f32f5f0>,
<function b at 0x7fa15f32faa0>,
<function c at 0x7ff1967099b0>)
I know I could solve it with basic string arguments and flow control, but it would be a lot less cluttered and easier to maintain if I could use parser arguments directly as function names.
Upvotes: 5
Views: 3896
Reputation: 55469
You can use basic string arguments and minimize the clutter by using a dict
to get the actual function from its name.
I'm on Python 2.6.6, so I can't use argparse
, but this code should give you the general idea:
#!/usr/bin/env python
def a(): return 'function a'
def b(): return 'function b'
def c(): return 'function c'
func_list = [a, b, c]
func_names = [f.func_name for f in func_list]
funcs_dict = dict(zip(func_names, func_list))
f = funcs_dict['b']
print f()
output
function b
So you can pass func_names
to argparse
and compactly retrieve the desired function using funcs_dict
.
In more recent Python versions, you should use f.__name__
instead of f.func_name
. (That will also work in Python 2.6, but I was unfamiliar with the newer syntax when I wrote this answer).
Upvotes: 6
Reputation: 25478
You need to ensure that your choice
s are strings (the user cannot enter a Python function object on the command line). You can use a dictionary to resolve these strings into functions. For example:
# Example functions:
def a(i):
return i + 1
def b(i):
return i + 2
def c(i):
return i + 3
# a dictionary mapping strings of function names to function objects:
funcs = {'a': a, 'b': b, 'c': c}
# Add the -f/--func argument: valid choices are function _names_
parser.add_argument('-f', '--func', dest='func',
choices=['a', 'b', 'c'],
required=True,
help="""Choose one of the specified function to be run.""")
args = parser.parse_args()
# Resolve the chosen function object using its name:
chosen_func = funcs[args.func]
Upvotes: 5
Reputation: 1571
My analysis, I may go wrong but this is my understanding.
ArgParse allow you to create more than just simple object from the command line argument by a casting mechanism. This let you get the number five if you pass the string '5' and specified that you are waiting for an integer.
And actually you are trying to get a function from the string 'a'. There is no casting way to do this. This is my proposal to solve your problem :
import argparse
def foo():
print("called foo")
def bar():
print("called bar")
functions = [foo, bar] #list your functions here
functions = { function.__name__ : function for function in functions}
parser = argparse.ArgumentParser()
parser.add_argument('-f', '--func',
choices=list(functions.keys()),
required=True,
help="""Choose one of the specified function to be run.""")
args = parser.parse_args()
functions[args.func]()
You now just have to register your functons into te list function at start and call them following the last line thank to the function index that is built from and replace automatically the function list
Upvotes: 2