I have two classes:
OptionParser
Application
stored in separate files (option_parser.py and application.py). The former defines how command line input should be handled. The latter imports the former, reads the user input, and carries on with the request.
How do I call a method defined in application.py's Application without importing it inside option_parser.py (as that would lead to an infinite loop, since application.py imports OptionParser from option_parser.py). If I use set_defaults(func=method_name)
, then I am told that global function method_name
is not defined in that scope.
I already have a workaround for this, but it just doesn't seem right:
if self.options.action == 'tags':
self.list_tags()
elif self.options.action == 'branches':
self.list_branches()
I've tried to find the answer in the official documentation, Google, and on SO - to no avail.
I assume you are using set_defaults(func=method_name)
with a subparser as illustrated in the argparse
docs? That's an easy way of invoking a function. It takes advantage of flexibility built into defining defaults and setting attributes in the args
namespace. But there's no code devoted to this feature.
Associating functions with actions like this is typically the easiest way to handle the different actions for each of your subparsers. However, if it is necessary to check the name of the subparser that was invoked, the dest keyword argument to the add_subparsers() call will work:
That 2nd sentence is giving you permission to use
if self.options.action == 'tags'
In your case set_defaults
is not the easiest way.
argparse
is primarily a means of finding out what your user wants. It isn't meant to be a dispatcher. Usually that's the responsibility of your code.
My impression from other argparse
questions is that most of the time programmers don't use this set_defaults
option. A search of SO for [argparse] set_default
only turns up 37 posts (answers or questions) (compared to 1.5K questions for just the argparse tag).
Here's a working script that implements my idea of passing set_defaults
values to an parser
setup function. It's one file, but the parser
part could just as well be imported.
import argparse
def make_parser(defaults):
parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='cmdstr')
sp.required = True
for key in defaults:
spp = sp.add_parser(key)
spp.set_defaults(cmd=defaults[key])
return parser
def foo(args):
print('foo')
def bar(args):
print('bar')
if __name__=='__main__':
dd = {'cmd1': foo, 'cmd2': bar}
parser = make_parser(dd)
args = parser.parse_args()
print(args)
args.cmd(args)
Sample calls:
0930:~/mypy$ python3 stack42897689.py cmd1
Namespace(cmd=<function foo at 0xb70c2dac>, cmdstr='cmd1')
foo
0931:~/mypy$ python3 stack42897689.py cmd2
Namespace(cmd=<function bar at 0xb7145614>, cmdstr='cmd2')
bar
With this dd
dictionary, I could just as easily have done the dispatching with:
dd[args.cmdstr](None)
The basic goal is to pair up a commandline string with a function call. set_defaults
is one way to do within argparse
. But dictionary like dd
is also widely used in Python code. And so are if/else
statements.