Cadene
Cadene

Reputation: 57

Manage multiple following positional argument with argparse

I'm using this lib for parsing arguments in python : https://docs.python.org/2/library/argparse.html

So far I have this:

prog arg1 [-s arg2 [arg2 ...]] [-m arg3 [arg3 ...]] 

And I want this:

prog arg1 -s arg2 [arg2 ...] -m arg3 [arg3 ...]

Here is my python code:

parser = argparse.ArgumentParser()
parser.add_argument('path', type=str,
                    help="path used for the generation of the rouge files")
parser.add_argument('-s', '--systems', type=str, nargs='+',
                    help="path to the systems generated summary files")
parser.add_argument('-m', '--models', type=str, nargs='+',
                    help="path to the reference summary files")
args = parser.parse_args()
print args

The problem is when you call the program without the optional arguments, it doesn't give an error (too few arguments). I want my optional arguments to be obligatory, but when you make the following call, the parser doesn't figure out which kind of args are involved...

For exemple with the following code:

parser = argparse.ArgumentParser()
parser.add_argument('arg1', type=str, nargs='+')
parser.add_argument('arg2', type=str, nargs='+')
parser.add_argument('arg3', type=str, nargs='+')
args = parser.parse_args()

And the following call:

python test.py arg1 arg1 arg1 arg2 arg2 arg3 arg3

I got this:

Namespace(arg1=['arg1', 'arg1', 'arg1', 'arg2', 'arg2'], arg2=['arg3'], arg3=['arg3'])

For sure here is the format of this prog:

prog arg1 [arg1 ...] arg2 [arg2 ...] arg3

Thanks for the help :)

Upvotes: 1

Views: 3303

Answers (3)

hpaulj
hpaulj

Reputation: 231355

optionals can take a required=True parameter. That may be all you need.

 p=argparse.ArgumentParser()
 p.add_argument('-m',nargs='+',required=True)
 p.add_argument('-n',nargs='+',required=True)
 p.print_usage()

producing

 usage: ipython3 [-h] -m M [M ...] -n N [N ...]

As to why:

 Namespace(arg1=['arg1', 'arg1', 'arg1', 'arg2', 'arg2'], arg2=['arg3'], arg3=['arg3'])

you specified that each of arg1, arg2, arg3 required 1 or more strings. It split up the long list accordingly, giving arg2 and arg3 each one (which satisfies their requirement), and allocating the rest to arg1. If you are familiar with regex, this is equivalent

In [96]: re.match('(A+)(A+)(A+)','AAAAAAAAA').groups()
Out[96]: ('AAAAAAA', 'A', 'A')

(The parser can't read your mind and allocate all the 'arg2's to args2 just because the names look similar.:) )

So if you need to split lists of arguments in a specific way, then optionals (flags) is the way to go. And between nargs and required you have quite a bit of control over the numbers.

Upvotes: 3

Cadene
Cadene

Reputation: 57

I added require=True and it works, thanks to hpaulj

parser = argparse.ArgumentParser()
parser.add_argument('path', type=str,
                    help="path used for the generation of the rouge files")
parser.add_argument('-s', '--systems', type=str, nargs='+', required=True,
                    help="path to the systems generated summary files")
parser.add_argument('-m', '--models', type=str, nargs='+', required=True,
                    help="path to the reference summary files")
args = parser.parse_args()

python summary2rouge.py 
usage: summary2rouge.py [-h] -s SYSTEMS [SYSTEMS ...] -m MODELS [MODELS ...]
                    path
summary2rouge.py: error: too few arguments

Upvotes: 0

titusjan
titusjan

Reputation: 5546

What you want is not possible. Think about it: if you would implement your own argument parsing without argparse, how would you determine if a positional argument is the last one of a list of arg1 arguments, or the first of the arg2 arguments?

I think the solution you've got know (optional arguments) works fine and is even preferable.

Upvotes: 1

Related Questions