Jon Cage
Jon Cage

Reputation: 37516

How should you use argparse to choose which action to perform and pass arguments to it?

I want to use the argparse library to parse some arguments but I'm struggling to work out what in the myriad of ways you can specify arguments is the simplest way to choose between a few actions. Different actions require different numbers of arguments.

Given the following calls I'd expect the following outputs:

> python MyClass.py action1 foo
Action 1: 12345 - foo

> python MyClass.py action2 20 30
Action 2: 12345 - 20 30

The following seems to work:

import argparse

class MyClass:
    def __init__(self, someVar):
        self.someVar = someVar

    def Action1(self, intToPrint):
        print("Print 1: %d - %s"%(self.someVar,intToPrint))

    def Action2(self, firstNum, firstString):
        print("Print 2: %d - %d %s"%(self.someVar,firstNum, firstString))

def CallAction1(mc, args):
    mc.Action1(args.intToPrint)

def CallAction2(mc, args):
    mc.Action2(args.firstNum, args.firstString)

def Main():
    parser = argparse.ArgumentParser(prog='PythonArgumentParsing.py')
    subparsers = parser.add_subparsers(help='commands')

    action1Group = subparsers.add_parser('action1', help='action 1 help')
    action1Group.add_argument('intToPrint', type=str)
    action1Group.set_defaults(func=CallAction1)

    action2Group = subparsers.add_parser('action2', help='action 1 help')
    action2Group.add_argument('firstNum', type=int)
    action2Group.add_argument('firstString', type=str)
    action2Group.set_defaults(func=CallAction2)

    args = parser.parse_args()
    someVar = 12345
    mc = MyClass(someVar)
    args.func(mc, args)

if __name__ == "__main__":
    Main()

...but it seems a little clunky to have to create a CallAction to pass arguments from the parser.

Is there any way to clean this up?

Upvotes: 0

Views: 83

Answers (1)

hpaulj
hpaulj

Reputation: 231665

I gather that you are just bothered by needing to write the Call_Action... functions which convert the args namespace into positional parameters for the method calls.

Using keyword parameters might eliminate this need. The following hasn't been tested yet:

def Action1(self, intToPrint=None, **kwargs):
    print("Print 1: %d - %s"%(self.someVar,intToPrint))

def Action2(self, firstNum=None, firstString=None, **kwargs):
    print("Print 2: %d - %d %s"%(self.someVar,firstNum, firstString))
...
action1Group.set_defaults(func=MyClass.Action1)
...
args.func(mc, **vars(args))

If I've done this right I can pass the whole vars(args) dictionary to the method. It will use the parameters that it needs, and ignore the rest.

argparse makes extensive use of the **kwargs method of passing parameters.

Upvotes: 1

Related Questions