Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best manner of dispatching Python's argparse subcommands

I am playing around with Python's argparse module in order to get a rather complicated and large sub-commands structure. So far, the arguments parse goes pretty well and everything works fine but I am looking for a better way to manage how sub-commands are executed.

Here is an example of my dummy/playaround application:

def a_func(cmd_args):
    print(cmd_args)


def b_func(cmd_args):
    print(cmd_args)


CMD_DISPATCHER = {
    'a': a_func,
    'b': b_func
}


def parse_commands():
    # Create top-level parser
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest='subcmd_name')

    # Create parser for the "a" sub-command
    parser_a = subparsers.add_parser('a')
    parser_a.add_argument('-bar', type=int)

    # Create parser for the "b" sub-command
    parser_b = subparsers.add_parser('b')
    parser_b.add_argument('-foo', type=int)

    args = parser.parse_args()

    CMD_DISPATCHER[args.subcmd_name](args)


def main():
    parse_commands()


if __name__ == '__main__':
    main()

As you can see, here I use a simple dict (CMD_DISPATCHER) as a map relating the sub-command name to its function.

As I said, this is just a simple example of what I want to achieve but the real project will have many nested sub-commands so I would like a better way to manage those several sub-commands.

Do you know a better/more-professional way to do this?

like image 851
kazbeel Avatar asked Oct 23 '25 18:10

kazbeel


1 Answers

OK, after some more research I have found a good way that fits my needs: set_defaults. I have used this function earlier but always to set default existing default argument values. I did not notice the clear example that argparse documentation provides:

>>> # sub-command functions
>>> def foo(args):
...     print(args.x * args.y)
...
>>> def bar(args):
...     print('((%s))' % args.z)
...
>>> # create the top-level parser
>>> parser = argparse.ArgumentParser()
>>> subparsers = parser.add_subparsers()
>>>
>>> # create the parser for the "foo" command
>>> parser_foo = subparsers.add_parser('foo')
>>> parser_foo.add_argument('-x', type=int, default=1)
>>> parser_foo.add_argument('y', type=float)
>>> parser_foo.set_defaults(func=foo)
>>>
>>> # create the parser for the "bar" command
>>> parser_bar = subparsers.add_parser('bar')
>>> parser_bar.add_argument('z')
>>> 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)
2.0
>>>
>>> # parse the args and call whatever function was selected
>>> args = parser.parse_args('bar XYZYX'.split())
>>> args.func(args)
((XYZYX))

As you can see the line parser_bar.set_defaults(func=bar) sets a new variable to parser_bar arguments. In this case bar is a function that is eventually used as the sub-command executor in line args.func(args).

I hope this helps someone in the future.

like image 99
kazbeel Avatar answered Oct 26 '25 09:10

kazbeel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!