Python argparse位置参数和子命令

时间:2011-12-29 13:35:14

标签: python argparse subcommand positional-operator

我正在使用argparse,我正在尝试混合子命令和位置参数,并出现了以下问题。

此代码运行良好:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional')
subparsers.add_parser('subpositional')

parser.parse_args('subpositional positional'.split())

上面的代码将args解析为Namespace(positional='positional'),但是当我将position参数更改为nargs ='?'时就这样:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional', nargs='?')
subparsers.add_parser('subpositional')

parser.parse_args('subpositional positional'.split())

它出错:

usage: [-h] {subpositional} ... [positional]
: error: unrecognized arguments: positional

为什么会这样?

4 个答案:

答案 0 :(得分:9)

起初我认为和jcollado一样,但事实上,如果后续(顶级)位置参数具有特定nargsnargs = None,{ {1}} =整数),然后它按预期工作。它在nargsnargs'?'时失败,有时在'*'时失败。所以,我去了解代码,找出发生了什么。

归结为参数被拆分的方式。为了弄清楚谁得到了什么,对'+'的调用总结了像parse_args这样的字符串中的参数,在你的情况下('AA'表示位置参数,'A'表示可选),并最终生成一个与该摘要字符串匹配的正则表达式模式,具体取决于您通过'O'.add_argument方法添加到解析器的操作。

在每种情况下,对于您的示例,参数字符串最终为.add_subparsers。要匹配的模式有哪些更改(您可以在'AA'中查看_get_nargs_pattern下的可能模式。对于argparse.py,它最终为subpositional,这意味着允许一个参数后跟任意数量的选项或参数。对于'(-*A[-AO]*)',它取决于传递给positional的值:

  • nargs => None
  • 3 => '(-*A-*)'(每个预期参数一个'(-*A-*A-*A-*)'
  • '-*A' => '?'
  • '(-*A?-*)' => '*'
  • '(-*[A-]*)' => '+'

附加了这些模式,对于'(-*A[A-]*)'(您的工作示例),您最终得到的nargs=None与两个组'(-*A[-AO]*)(-*A-*)'相匹配。这样,['A', 'A']将仅解析subpositional(您想要的内容),而subpositional将匹配其操作。

但对于positional,您最终会得到nargs='?'。第二组完全由可选模式组成,'(-*A[-AO]*)(-*A?-*)'贪婪,这意味着第一组将字符串中的所有内容全局化,最终识别出两组*。这意味着['AA', '']得到两个参数,当然最终会窒息。

有趣的是,subpositional的模式是nargs='+',只要您传递一个参数,它就可以使用。说'(-*A[-AO]*)(-*A[A-]*)',因为您需要在第二组中至少有一个位置参数。同样,由于第一组是贪婪的,通过subpositional a会得到subpositional a b c d,这不是你想要的。

简而言之:一团糟。我想这应该被视为一个错误,但不确定如果模式变成非贪婪的那个会产生什么影响...

答案 1 :(得分:6)

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional', nargs='?')

subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')

print(parser.parse_args(['positional', 'subpositional']))
# -> Namespace(positional='positional')
print(parser.parse_args(['subpositional']))
# -> Namespace(positional=None)
parser.print_usage()
# -> usage: bpython [-h] [positional] {subpositional} ...

通常的做法是命令之前的参数(在左侧)属于主程序,在(在右侧)之后属于命令。因此positional应该在命令subpositional之前。示例程序:gittwistd

此外,narg=?的参数应该是一个选项(--opt=value),而不是位置参数。

答案 2 :(得分:5)

我认为问题在于,当调用add_subparsers时,会向原始解析器添加一个新参数以传递子分析器的名称。

例如,使用以下代码:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

parser.add_argument('positional')                                             
subparsers.add_parser('subpositional')                                             

parser.parse_args()

您将获得以下帮助字符串:

usage: test.py [-h] {subpositional} ... positional

positional arguments:
  {subpositional}
  positional

optional arguments:
  -h, --help       show this help message and exit

请注意,subpositional之前会显示positional。我要说的是你要找的是在subparser名称之前有位置参数。因此,您可能正在寻找的是在subparsers之前添加参数:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('positional')

subparsers = parser.add_subparsers()
subparsers.add_parser('subpositional')

parser.parse_args()

使用此代码获取的帮助字符串是:

usage: test.py [-h] positional {subpositional} ...

positional arguments:
  positional
  {subpositional}

optional arguments:
  -h, --help       show this help message and exit

这样,你首先将参数传递给主解析器,然后传递subparser的名称,最后传递subparser的参数(如果有的话)。

答案 3 :(得分:0)

Python 3.5仍然是一团糟。

我建议subClass ArgumentParser保留所有剩余的位置参数,并在以后处理它们:

import argparse

class myArgumentParser(argparse.ArgumentParser):
    def parse_args(self, args=None, namespace=None):
       args, argv = self.parse_known_args(args, namespace)
       args.remaining_positionnals = argv
       return args

parser = myArgumentParser()

options = parser.parse_args()

其余的位置参数位于列表options.remaining_positionals