pythonargparsesubcommand

Multiple invocation of the same subcommand in a single command line


I'm trying to figure out how to use argparser to do the following:

$ python test.py executeBuild --name foobar1 executeBuild --name foobar2 ....

getBuild itself is a sub-command. My goal is to have the script have the capability to chain a series of sub-command (executeBuild being one of them) and execute them in order. In the example above, it would execute a build, then setup the environment, then execute build again. How can I accomplish this with argparse? I've tried the following:

    main_parser = argparse.ArgumentParser(description='main commands')
    subparsers = main_parser.add_subparsers(help='SubCommands', dest='command')

    build_parser = subparsers.add_parser('executeBuild')
    build_parser.add_argument('--name', action='store', nargs=1, dest='build_name')
    check_parser = subparsers.add_parser('setupEnv')

    args, extra=main_parser.parse_known_args() 

However, it appears that whenever I do this, it goes into the subcommand of executeBuild and report it doesn't know what executeBuild is. I've tried parsing out the extra so I can do a repeat call / chain, however, the first view property appears to have been overwritten, so I can't even just save the extra options and iterate thru.


Solution

  • You are asking argparse something it was not written for : it is good at parsing one command line (but only one) and you want to parse multiple commands in one single line. IMHO, you have to do an initial splitting on your arguments array, and then use argparse on each subcommand. Following function takes a list of arguments (could be sys.argv), skips the first and split remaining in arrays beginning on each known subcommand. You can then use argparse on each sublist :

    def parse(args, subcommands):
        cmds = []
        cmd = None
        for arg in args[1:]:
            if arg in (subcommands):
                if cmd is not None:
                    cmds.append(cmd)
                cmd = [arg]
            else:
                cmd.append(arg)
        cmds.append(cmd)
        return cmds
    

    In your example :

    parse(['test.py', 'executeBuild', '--name', 'foobar1', 'executeBuild', '--name', 'foobar2'],
        ('executeBuild',))
    

    =>

    [['executeBuild', '--name', 'foobar1'], ['executeBuild', '--name', 'foobar2']]
    

    Limits : subcommands are used as reserved words and cannot be used as option arguments.