4

I am new on Argparse module. I have almost finished my script but there is something I cannot find a way to do it.

Here is the script:

import argparse

def function1(a, b, c):
    #mystaff
def function2(e, f):
    #mystaff
def function3(g):
    #mystaff

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='blahblah')
    parser.add_argument('-a','--a', help='blahblah', required=False, default="defaultvalue")
    parser.add_argument('-b','--b', help='blahblah', required=False)
    .......

    args = parser.parse_args()
    function1(args.a,args.b,args.c)

I want to call the script from command prompt. Now, I can use this for example to call the function1: python myscript.py -a <var>

What I want is:

python myscript.py -a <var>: call the function1 as it is now

python myscript.py function2 -c <var>: call the function2 only

python myscript.py function3 -g <var>: call the function3 only

So, if I don't specify the function, function1 is the default, otherwise I have to pass the function name from the command prompt too. Any ideas?

Tasos
  • 7,325
  • 18
  • 83
  • 176
  • 1
    If argparse isn't a strict requirement you could look at click (http://click.pocoo.org/) which allows nested commands like this. – jbrown Jul 05 '14 at 10:40

4 Answers4

11

Default subcommands

If you want to have subcommands, and make one of them default if no subcommand is specified, then you can't use the typical subparser method.

You need to do your argparse in two passes:

parser = ArgumentParser()
parser.add_argument("function", 
                    nargs="?",
                    choices=['function1', 'function2', 'function2'],
                    default='function1',
                    )
args, sub_args = parser.parse_known_args()

if args.function == "function1":
    parser = ArgumentParser()
    parser.add_argument('-a','--a')
    parser.add_argument('-b','--b')
    parser.add_argument('-c','--c')
    args = parser.parse_args(sub_args)
    function1(args.a, args.b, args.c)
elif args.function == "function2":
    ...
elif args.function == "function3":
    ...

Handling --help

If you want --help option to be useful, you need to do a bit more work:

  • We need to manually handle help because sometimes we want the overall help, and sometimes we want the subcommand's help
  • We can't give a default subcommand straight away because we need to be able to tell if it was specified or not

This should do the trick:

# Parse the subcommand argument first
parser = ArgumentParser(add_help=False)
parser.add_argument("function", 
                    nargs="?",
                    choices=['function1', 'function2', 'function2'],
                    )
parser.add_argument('--help', action='store_true')
args, sub_args = parser.parse_known_args(['--help'])

# Manually handle help
if args.help:
    # If no subcommand was specified, give general help
    if args.function is None: 
        print parser.format_help()
        sys.exit(1)
    # Otherwise pass the help option on to the subcommand
    sub_args.append('--help')

# Manually handle the default for "function"
function = "function1" if args.function is None else args.function

# Parse the remaining args as per the selected subcommand
parser = ArgumentParser(prog="%s %s" % (os.path.basename(sys.argv[0]), function))
if function == "function1":
    parser.add_argument('-a','--a')
    parser.add_argument('-b','--b')
    parser.add_argument('-c','--c')
    args = parser.parse_args(sub_args)
    function1(args.a, args.b, args.c)
elif function == "function2":
    ...
elif function == "function3":
    ...
Jamie Cockburn
  • 7,379
  • 1
  • 24
  • 37
  • Yes. I saw that feature in the official doc. But the problem is how to specify the first function as a default, without passing its name and the others using them only with their name in the commant. – Tasos Jul 05 '14 at 09:21
  • I opted for the first option without the help, since it is easier for me to understand. But when I call from command `python myscript.py` without anything and expected to have function1 running, I have an error in line `if function == 'function1'` that function is not defined – Tasos Jul 05 '14 at 10:21
  • In the first option, the line is `if args.function == 'function1'` you're missing the `args` – Jamie Cockburn Jul 05 '14 at 10:23
  • I was trying to use the solution with `--help` and it seems that `args.help` will always be true after execution of `args, sub_args = parser.parse_known_args(['--help'])`. Shouldn't it be just args, sub_args = parser.parse_known_args() instead? – alexK Jan 02 '17 at 15:42
2

You can do it this way. Use set_defaults(func = your_function)

   _parser = argparse.ArgumentParser(description="blalbabla awesome Framework")
   _subparsers = _parser.add_subparsers(help="sub command help")
   _check_parser = _subparsers.add_parser("check", help="check the code")
   _check_parser.add_argument("--script_path", help="can be a python file or dir", required=True)
   _check_parser.add_argument("--verbose", help='turn on debug level logging', action='store_true', default=False)
   _check_parser.set_defaults(func=do_function) 

   def  do_function(args):
     if args.script_path is file:
       xxxx

And then you can have correct access to do_function()

penny chan
  • 769
  • 1
  • 10
  • 15
  • This is the pattern recommended in [the docs for `argparse.add_subparsers`](https://docs.python.org/library/argparse.html#argparse.ArgumentParser.add_subparsers). – Boris Verkhovskiy Sep 18 '19 at 16:00
1

I assumed you need to call different functions based on the input parameters passed by user through cmd. Here is one way of doing it adding subparsers.

import argparse

def function1(args):
"""
:description: This function creates function-1.
:arguments: args: input arguments added by user
"""
    print("Input path given by user: "+ args.input_path)
    print("No. of Iterations: " + str(args.iterations))

def function2(args):
"""
:description: This function creates function-2.
:arguments: args: input arguments added by user
"""
    print("Time specified by user: "+ str(args.time) + " mins")

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Test helper')
    subparsers = parser.add_subparsers(help='Commands')

    # Arguments for running FUNCTION-1
    parser_0 = subparsers.add_parser('function1', help='Running func1...')
    parser_0.add_argument('-itr', dest='iterations', required=False, default=1, action="store",
                           help='No of iterations (integer value). DEFAULT=1')
    parser_0.add_argument('-p', dest='input_path', required=True, default="C:/Temp", action="store",
                           help='Input path. DEFAULT=C:/Temp')
    parser_0.set_defaults(func=function1)

    # Arguments for running FUNCTION-2
    parser_1 = subparsers.add_parser('function2', help='Running func2...')
    parser_1.add_argument('-time', required=False, dest='time', default=None, action="store",
                           help="Time in minutes. DEFAULT=No time set.")
    parser_1.set_defaults(func=function2)

    args = parser.parse_args()
    if args.func:
        args.func(args)

Now you can give parameters through your cmd like this:

>python mycode.py function1 -itr 3 -p C:/Users/abcxyz
Input path given by user: C:/Users/abcxyz
No. of Iterations: 3

>python mycode.py function2 -time 5
Time specified by user: 5 mins
Dharman
  • 30,962
  • 25
  • 85
  • 135
0
parser.add_argument('-a','--a', dest='action', action='store_const', const=function1)

to call perticular function you need add const=function1

you can see this problem for right answer

Community
  • 1
  • 1
sundar nataraj
  • 8,524
  • 2
  • 34
  • 46