Python argparse: combine optional parameters with nargs = argparse.REMAINDER

I have to miss something obvious. The goal is to use argparse with the first parameter required, the second optional and any other remaining parameters.

To show the problem, I made two test analyzers; the only difference between the two is the use of nargs = argparse.REMAINDER in one and nargs = '*' in the other.

def doParser1(argsin): parser = argparse.ArgumentParser(description='Parser demo.') parser.add_argument('req1', help='first required parameter') parser.add_argument('--opt1', help='first optional parameter') parser.add_argument('leftovers', nargs=argparse.REMAINDER, help='all the other parameters') argsout = parser.parse_args(args=argsin) print argsout return argsout def doParser2(argsin): parser = argparse.ArgumentParser(description='Parser demo.') parser.add_argument('req1', help='first required parameter') parser.add_argument('--opt1', help='first optional parameter') parser.add_argument('leftovers', nargs='*', help='all the other parameters') argsout = parser.parse_args(args=argsin) print argsout return argsout 

If there are no additional parameters, then parser2 works. This is the input followed by parser1 and parser 1:

 input: ['req1value', '--opt1', 'opt1value'] Namespace(leftovers=['--opt1', 'opt1value'], opt1=None, req1='req1value') Namespace(leftovers=None, opt1='opt1value', req1='req1value') 

If there are additional parameters, opt1 is omitted in parser1, and parser2 is simply confused:

 input: ['req1value', '--opt1', 'opt1value', 'r1', 'r2'] Namespace(leftovers=['--opt1', 'opt1value', 'r1', 'r2'], opt1=None, req1='req1value') usage: py-argparse.py [-h] [--opt1 OPT1] [-leftovers [LEFTOVERS [LEFTOVERS ...]]] req1 py-argparse.py: error: unrecognized arguments: r1 r2 

The expected result should be:

 Namespace(leftovers=['r1', 'r2'], opt1='opt1value', req1='req1value') 

It seems that this should be a simple case, and what is here is simplified from what I'm really trying to do. I tried to make the rest extra, adding a lot of other options, but nothing improved.

Any help would be appreciated.

+7
python argparse
source share
3 answers

--opt1 must be present before the "unnamed" arguments. Your real test cases:

  ['--opt1', 'opt1value', 'req1value'] 

and

  ['--opt1', 'opt1value', 'req1value', 'r1', 'r2'] 
+3
source share

You can use parse_known_args :

 import argparse parser = argparse.ArgumentParser(description='Parser demo.') parser.add_argument('req1', help='first required parameter') parser.add_argument('--opt1', help='first optional parameter') args, leftovers = parser.parse_known_args(['req1value', '--opt1', 'opt1value']) print(args, leftovers) # (Namespace(opt1='opt1value', req1='req1value'), []) args, leftovers = parser.parse_known_args(['req1value', '--opt1', 'opt1value', 'r1', 'r2']) print(args, leftovers) # (Namespace(opt1='opt1value', req1='req1value'), ['r1', 'r2']) 
+4
source share

Mixing positions and options is difficult if one or more positioners are of type zero or more ( ? * REMAINDER ). A simple solution is to not mix them - first give options, and then all the positions.

Here's what happens:

 input: ['req1value', '--opt1', 'opt1value'] Namespace(leftovers=['--opt1', 'opt1value'], opt1=None, req1='req1value') 

Because of the req1value line req1value parser parses the positions first. req1 wants 1 line, leftovers captures everything else, including --opt1 .

 Namespace(leftovers=None, opt1='opt1value', req1='req1value') 

With * leftovers executed [] , there is no line, so None (actually I get [] ). --opt1 parsed as optional.

 input: ['req1value', '--opt1', 'opt1value', 'r1', 'r2'] ... py-argparse.py: error: unrecognized arguments: r1 r2 

As before * leftovers set to [] . -opt1 processed. But now there are 2 lines where they do not belong. You intended to go to leftovers , but it has already been used. If the leftovers were + , he would accept them the way you planned.

The key is that when he tries to parse the 1st position, he also tries to parse all the positions that he can use. At one parse_args level, re.match('(A)(A*)','AOA') is executed, producing groups ('A', '') .

There are 2 suggested fixes that address this issue. One uses 2 steps of parse_known_args to provide a complete mix of options and positional elements. This is the behavior optparse users might expect.

Another patch is trying to delay the processing of positions that can take lines 0 arguments http://bugs.python.org/issue15112 .

+2
source share

All Articles