关于python:argparse可选的位置参数和subparsers参数

argparse optional positional argument and subparsers arguments

我有一个python脚本,它接受一个可选的位置参数,并有几个子命令。其中一些子命令需要位置参数,有些则不需要。当我尝试使用不需要位置参数的子命令时,会出现问题。考虑以下测试文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import argparse

argp = argparse.ArgumentParser()
argp.add_argument('inputfile', type=str, nargs='?',
                  help='input file to process')
argp.add_argument('--main_opt1', type=str,
                  help='global option')

subp = argp.add_subparsers(title='subcommands',
                           dest='parser_name',
                           help='additional help',
                           metavar="<command>")

tmpp = subp.add_parser('command1', help='command1 help')
tmpp.add_argument('pos_arg1', type=str,
                  help='positional argument')

print repr(argp.parse_args())

当我试图在第一个参数中使用子命令command1时,一切都很顺利。

1
2
macbook-pro:~ jmlopez$ python pytest.py filename command1 otherarg
Namespace(inputfile='filename', main_opt1=None, parser_name='command1', pos_arg1='otherarg')

但是现在让我们假设command1不需要第一个位置参数。

1
2
3
macbook-pro:~ jmlopez$ python pytest.py command1 otherarg
usage: pytest.py [-h] [--main_opt1 MAIN_OPT1] [inputfile] <command> ...
pytest.py: error: argument <command>: invalid choice: 'otherarg' (choose from 'command1')

不知怎的,我希望江户十一〔二〕定为江户十一〔三〕定。argparse是否可以预测command1实际上是一个子命令,因此inputfile应设置为none?


对于argp来说,subparser论点看起来就像是另一种位置,一种选择(子parser的名称)。另外,argppos_arg1一无所知。这在tmpp的论点清单中。

argp看到filename command1 otherarg时,filenamecommand1满足其2个位置。然后将otherarg传递给tmpp

使用command1 otherarg时,再次使用2个字符串,2个argp位置。command分配给inputfile。没有逻辑可以回溯,说command1更适合subcommands,或者"tmpp"需要这些字符串中的一个。

您可以将第一个位置更改为可选的,--inputfile

或者你可以选择inputfile作为tmpp的另一个位置。如果一些子组需要它,考虑使用parents

argparse不像你那么聪明,不能"超前思考"或"回溯思考"。如果它看起来很聪明,那是因为它使用re模式匹配来处理nargs值(例如?、*、+)。

编辑

"欺骗"argparse将第一个位置识别为子排序器的一种方法是在其后面插入一个可选的。在command1 -b xxx otherarg中,-b xxx分解了位置字符串列表,因此只有command1inputfilesubcommands匹配。

1
2
3
4
5
6
7
8
9
p=argparse.ArgumentParser()
p.add_argument('file',nargs='?',default='foo')
sp = p.add_subparsers(dest='cmd')
spp = sp.add_parser('cmd1')
spp.add_argument('subfile')
spp.add_argument('-b')

p.parse_args('cmd1 -b x three'.split())
# Namespace(b='x', cmd='cmd1', file='foo', subfile='three')

这里的问题是,argparse如何使用变量nargs处理过账。事实上,第二个职位是一个子职位并不重要。虽然argparse允许任何顺序的可变长度位置,但它如何处理这些位置可能会令人困惑。如果只有一个这样的位置,那么很容易预测argparse将做什么,并且它发生在最后。


您需要告诉解析器第一个参数是不同的类型。尝试添加标志选项和默认的None值,如下所示:

1
2
argp.add_argument('-i','--inputfile', type=str, nargs='?',
              help='input file to process',default=None)

现在,您需要在inputfile参数之前添加-i,但它会很好地工作。

1
2
macbook-pro:~ jmlopez$ python pytest.py -i filename command1 otherarg
Namespace(inputfile='filename', main_opt1=None, parser_name='command1', pos_arg1='otherarg')

1
2
macbook-pro:~ jmlopez$ python pytest.py command1 otherarg
Namespace(inputfile=None, main_opt1=None, parser_name='command1', pos_arg1='otherarg')