Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
386 views
in Technique[技术] by (71.8m points)

argparse - Python argparser repeat subparse

I'm using pythons(2.7.2) argparse (1.1) to parse command line and what I want is to create subparser and make it possible to enter subparser commands multiple times. Like this:

./script.py version 1 --file 1 2 3 version 3 --file 4 5 6

Is it possible to create such thing? Because now when I try to run script with such arguments in result namespase a get:

Namespace(file=['4', '5', '6'], n=[1])

n it is a version number. So I get first version number and second list of files instead both file lists and versions.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To the main parser, the subparsers argument is a positional that takes choices. But it also allocates ALL of the remaining argument strings to the subparser.

I expect that your string is parsed as follows:

./script.py version 1 --file 1 2 3 version 3 --file 4 5 6

version is accepted as a subparser name. 1 is accepted as value to positional argument n. (of the subparser). --file is accepted as a optional argument (by the subparser). The values from the second invocation overwrite the values from the first. I'm guessing --file has nargs='*'. If so, the first one writes ['1','2','3','version','3'] to the namespace, while the second overwrites it with ['4','5','6']. If nargs=3, I would expect the subparser to choke on the second version, which it would see as an unknown positional.

So the basic point is - once the 'version' subparser gets the argument list, it does not let go until it has parsed everything it can. In this case it parses both --file occurrences. Anything it can't handle comes back to the main parser as 'UNKNOWNS', which normally raises an error.


If you want values from repeated optionals, use an append action

parser.add_argument('--foo',action='append', nargs=3)

import argparse
parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='version')
spp = sp.add_parser('version')
spp.add_argument('n',nargs='*',type=int)
spp.add_argument('--file',nargs=3,action='append')
str = 'version 1 --file 1 2 3 version 3 --file 4 5 6'
print(parser.parse_known_args(str.split()))

produces

(Namespace(file=[['1', '2', '3'], ['4', '5', '6']], n=[1], version='version'), ['version', '3'])

Still only one call to version subparser, but all the data is present.


A different approach would be to nest subparsers

parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='sub')
spp = sp.add_parser('version')
spp.add_argument('n',nargs=1,type=int)
spp.add_argument('--file',nargs=3)

sp = spp.add_subparsers(dest='sub1')
spp = sp.add_parser('version')
spp.add_argument('n1',nargs=1,type=int)
spp.add_argument('--file',dest='file1',nargs=3)

str = 'version 1 --file 1 2 3 version 3 --file 4 5 6'
print(parser.parse_args(str.split()))

Note that I have to change the 'dest' to avoid over writing values. This produces

Namespace(file=['1', '2', '3'], file1=['4', '5', '6'], n=[1], n1=[3], sub='version', sub1='version')

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...