Python subparser parsing with nargs='+'

I have to make a parser with command line arguments like this

python function.py -v 1 name1 -d abc xyz foo bar name2 -i 3 -p abc xyz

I did this:

import argparse
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('-v', type=int, help='number')

subparsers = parser.add_subparsers(help='sub-command help', dest="command")
parser_a = subparsers.add_parser('name1', help="name1 help")
parser_a.add_argument("-d", help="list", nargs="+")

parser_b = subparsers.add_parser('name2', help="name2 help")
parser_b.add_argument("-p", help="list2", nargs='+')
parser_b.add_argument("-i", help="number inside name2", type=int)

When I run this:

parser.parse_args('-v 1 name1 -d abc xyz foo bar name2 -i 3 -p abc xyz'.split())

It results in:

usage: PROG [-h] [-v V] {name1,name2} ...
PROG: error: unrecognized arguments: -i 3 -p abc xyz

And when running this:

parser.parse_args('-v 1 name1 -d abc xyz foo bar name2'.split())

It results in this:

Namespace(command='name1', d=['abc', 'xyz', 'foo', 'bar', 'name2'], v=1)

I want it to be something like this:

Namespace(command='name1', d=['abc', 'xyz', 'foo', 'bar'], command='name2', p=['abc', 'xyz'], i=3, v=1)

How do I do it?

Answers


I think you are doing something wrong. First you should pass positional arguments, and then keyword arguments, e.g.

python myapp.py arg1 arg2 --kwarg1=1 --kwarg2 val1 --kwarg2 val2 

As you note

In [24]: parser.parse_args('-v 1 name1 -d abc xyz foo bar name2'.split())
Out[24]: Namespace(command='name1', d=['abc', 'xyz', 'foo', 'bar', 'name2'], v=1)

name2 is seen as one of the arguments to parser_a -d argument

The rest ' -i 3 -p abc xyz' is not accepted because -i is understood to be an optional flag.

Effectively you are running:

In [28]: parser_a.parse_args('-d abc xyz foo bar name3 -i 3 -p abc xyz'.split())
usage: PROG name1 [-h] [-d D [D ...]]
PROG name1: error: unrecognized arguments: -i 3 -p abc xyz

The main parser has a positional argument that accepts 2 choices, name1, and name2. When it encounters name1, it passes the rest of the arguments to parser_a.

The net effect is that argparse accepts only one subcommand.

I have discussed some ways around this in previous SO questions. The sidebar found one:

Python argparser repeat subparse

(that may be a good enough fit to mark this question as a duplicate).


If I add a positional to parser_a; and use -- to separate bar and name2 (meaning, all that follows is positional)

In [32]: parser_a.add_argument('extra',nargs='*')
Out[32]: _StoreAction(option_strings=[], dest='extra', nargs='*', const=None, default=None, type=None, choices=None, help=None, metavar=None)

In [33]: parser.parse_args('-v 1 name1 -d abc xyz foo bar -- name2 -i 3 -p abc xyz'.split())
Out[33]: Namespace(command='name1', d=['abc', 'xyz', 'foo', 'bar'], extra=['name2', '-i', '3', '-p', 'abc', 'xyz'], v=1)

I can then pass the extra string (minus name2) to parser_b:

In [34]: args=_
In [36]: parser_b.parse_args(args.extra[1:])
Out[36]: Namespace(i=3, p=['abc', 'xyz'])

Admittedly this a convoluted solution, but the ones I suggested a couple of years ago aren't much better. And hopefully it helps clarify what is going on.


Do you really need to invoke 2 subcommands at a time. argparse would be a lot simpler if you could do:

python function.py -v 1 name1 -d abc xyz foo bar
python function.py -v 1 name2 -i 3 -p abc xyz

In other words, call the script twice. That is easy if each call to function.py performs an independent self contained action - with any connection between the two calls embodied in shared files or database. That is how subcommands are often used. Trying to put multiple subcommands in one call does not save typing.


Need Your Help

AngularJS, Module object and form name

javascript angularjs

I having trouble to understand some weird problem, I will explain as sort as i can:

requireJS why does each define has to be in different file?

javascript jquery requirejs

I am using requireJS in my sample demo app. This is my code: