Arne
Arne

Reputation: 20117

How to use argparse arguments as function names

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

Answers (3)

PM 2Ring
PM 2Ring

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

xnx
xnx

Reputation: 25478

You need to ensure that your choices 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

Arthur Va&#239;sse
Arthur Va&#239;sse

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

Related Questions