1

I am trying to find a way to add method name as an argument in command line. I am using argparse module in python 2.7.10.

def create_parser():
    parser = argparse.ArgumentParser(description='add method name')
    parser.add_argument('--method_name', help='pass method name to be used')
return parser

def foo():
    return "I am here."

def bar():
    return "I am not here."

def where_are_you(method_name):
    return method_name()

def main():
    return where_are_you(args.method_name)

if __name__ == '__main__':
    main()

I am trying to call one of the methods thru command line and getting this error: "TypeError: 'str' object is not callable." Please guide me here I am new to python.

June2017
  • 169
  • 3
  • 14
  • I can not find the solution I a looking for in "Calling a function of a module from a string with the function's name". Can you please unmark this question from being duplicate. Thanks – June2017 Aug 14 '17 at 05:44
  • I think they could have found a better duplicate. `getattr` works, but may be too advanced for a beginner. A mapping from the `args.method_name` to function objects, either with explicit `if` clauses, or a dictionary would be more appropriate. For a couple of alternative functions, the `argparse` docs starts off with a example using `const`. – hpaulj Aug 14 '17 at 06:46

1 Answers1

2

I've added a dictionary that maps from names to function objects. This dictionary can also be used to limit the choices that the parser accepts.

import argparse
def create_parser():
    parser = argparse.ArgumentParser(description='add method name')
    parser.add_argument('--method_name', help='pass method name to be used', 
          choices=adict)
    return parser

def foo():
    return "I am here."

def bar():
    return "I am not here."

adict = {'foo': foo, 'bar': bar}  # map names to functions

def where_are_you(method_name):
    fn = adict[method_name]
    # could also use getattr
    return fn()

def main():
    args = create_parser().parse_args()     # get the string
    print(args.method_name)                 # debugging
    return where_are_you(args.method_name)

if __name__ == '__main__':
    print(main())          # show the return

testing:

0001:~/mypy$ python stack45667979.py -h
usage: stack45667979.py [-h] [--method_name {foo,bar}]

add method name

optional arguments:
  -h, --help            show this help message and exit
  --method_name {foo,bar}
                        pass method name to be used
0001:~/mypy$ python stack45667979.py --method_name foo
foo
I am here.
0002:~/mypy$ python stack45667979.py --method_name baz
usage: stack45667979.py [-h] [--method_name {foo,bar}]
stack45667979.py: error: argument --method_name: invalid choice: 'baz' (choose from 'foo', 'bar')

The previous duplicate

Calling a function of a module from a string with the function's name

uses getattr to perform a general search of the module namespace to match a string with a function. That could be used here, but may be too advanced for a beginner, and maybe too open ended even for advanced programmer. It is generally better to limit the choices you give your user to the set of known valid ones.

If this dictionary mapping is confusing I'd suggest a more obvious mapping

if args.method == 'foo':
    foo()
elif args.method == 'bar':
    bar()
else:
    parser.error('bad method choice')
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • Thank you so much for the detailed answer. I had tried dictionary mapping. My problem was the methods and the place where I plan to call these methods reside in different modules (repos). The methods live in many repos coping their module name in the main repo is not advisable. So, I was looking for something that being a newbie I am not able to figure out. Thank you once again. – June2017 Aug 14 '17 at 07:51