I’ve been developing some CLI tools with argparser and stumbled in a piece of code that I’ve found a little ugly:
# Setting up argparse instance
# ...
# Then:
parser_foo.set_defaults(func=foo)
parser_bar.set_defaults(func=bar)
# parse the args and call whatever function was selected
args = parser.parse_args('foo 1 -x 2'.split())
args.func(args) # Ugly
>>> 2.0
# parse the args and call whatever function was selected
args = parser.parse_args('bar XYZYX'.split())
args.func(args) # Eeew
>>> ((XYZYX))
This code is supposed to call a function for the correct subparser and it’s in the argparse documentation just before this section.
In this example, its shown that one can add a function as a property to Namespace and then call this function according to the correct subparer.
Quoting the documentation:
One particularly effective way of handling sub-commands is to combine the use of the add_subparsers() method with calls to set_defaults() so that each subparser knows which Python function it should execute. For example:
I feel that calling func seems a little like a gambiarra.
args.func(args)
I’d like to give a couple of options that might be better than the current suggestion:
Bind the function as a Namespace method
def foo(args: Namespace):
print("Called foo!")
subparsers.add_parser('foo')
parser_foo.set_defaults(func=foo) # Some new magic needed here. A little weird still
args = parser.parse_args()
args.func()
Add an optional argument to add_subparsers and a run method to ArgumentParser:
def foo(args: Namespace):
print("Called foo!")
subparsers.add_parser('foo', run=foo)
parser.run() # Runs the correct function
My understanding is that argparse is feature frozen at this point. It’s maintained to fix serious bugs, but new features are not being added. There are a myriad of other CLI frameworks that have various design goals and features that you should look to instead if argparse is not sufficient.
Makes a lot of sense! I would recommend looking at clize which works broadly in this way, but goes further: the function’s parameters come from the subcommand’s arguments and are used to define them. I’ve used it in several projects and it’s quite convenient.