Python3 Argparse 元变量括号解析异常

Python3 Argparse metavar brackets parsed weirdly

我在 python3 中使用 argparse,我得到了一些奇怪的东西:

我正在使用的代码的简短版本是:

argparser = argparse.ArgumentParser(description='add/remove items')
argparser.add_argument('-a', action='append',     metavar="Item(s)", help='add one or more items to the list')
argparser.add_argument('-r', action='append',     metavar="Item(s)", help='remove one or more items from the list')
args = argparser.parse_args()

当我 运行 带有 -h 标志的脚本时,我得到这个输出:

usage: test.py [-h] [-a Items)] [-r Item(s]

add/remove items

optional arguments:
  -h, --help  show this help message and exit
  -a CPE(s)   add one or more items to the list
  -r CPE(s)   remove one or more items from the list

注意第一行中括号的奇怪解析。

这是什么原因造成的,我该如何解决?

因为你希望有多个项目的可能性。使用 argparse 执行此操作的另一种方法如下:

import argparse
argparser = argparse.ArgumentParser(description='add/remove items')
argparser.add_argument('-a', metavar="item", nargs="*", help='add one or more items to the list')
argparser.add_argument('-r', metavar="item", nargs="*", help='remove one or more items from the list')
args = argparser.parse_args()

关键是nargs="*"(0个或多个参数)的使用。帮助变为:

usage: test.py [-h] [-a [item [item ...]]] [-r [item [item ...]]]

这样,您就不必使用“Item(s)”,而且您也遵循了标准做法。

PS:我明白你想做什么了。使用 action="append",您实际上允许用户指定多个 -a-r 选项。在这种情况下,您绝对应该写 "Item"(而不是“Item(s)”),因为每个选项只需要一个项目。这也解决了您的问题(您的帮助消息应该表明可以给出多个 -a-r 选项)。

是您的元变量中的 () 导致了错误的使用。用法格式化程序使用 () 标记需要互斥的组,然后删除多余的组。所以它试图保留 ( -o | -t),但将 (-o) 更改为 -o。不幸的是,该代码无法区分它添加的代码和您通过元变量(或帮助行)添加的代码。

您的行格式为:

usage: test.py [-h] [-a Item(s)] [-r Item(s)]

但它删除了我用 *:

替换的 () 的外对
usage: test.py [-h] [-a Item*s)] [-r Item(s*]

http://bugs.python.org/issue11874 关注一个不同的使用问题,当使用行很长并且需要拆分时会出现这种问题。但是那个问题的最后 2 个帖子处理这个问题。

如果您不喜欢自动 usage 格式的限制,您可以给 parser 您自己的自定义 usage 参数。

我在 Python 2.7 中的解决方案是覆盖 argparse.format_usage() 和 argparse.format_help()。您对元变量进行编码,然后在 argparse 进行格式化后对其进行解码:

FIXES = (('\[', '%lb'), ('\]', '%rb'), ('\(', '%lp'), ('\)', '%rp'))
def encode_parens(text):
    for orig, encoded in FIXES:
        text = re.sub(orig, encoded, text)
    return text
def decode_parens(text):
    for orig, encoded in FIXES:
        text = re.sub(encoded, orig[1:], text)
    return text

class MyArgParser(argparse.ArgumentParser):
    def __init__(self, *args, **kwargs):
        super(MyArgParser, self).__init__(*args, **kwargs)
    def format_usage(self, *args, **kwargs):
        u = super(MyArgParser, self).format_usage(*args, **kwargs)
        return decode_parens(u)
    def format_help(self, *args, **kwargs):
        h = super(MyArgParser, self).format_help(*args, **kwargs)
        return decode_parens(h)

if __name__ == '__main__':
    argparser = MyArgParser(description='add/remove items')
    argparser.add_argument('-a', action='append', metavar=encode_parens("Item(s)"), help='add one or more items to the list')
    argparser.add_argument('-r', action='append', metavar=encode_parens("Item(s)"), help='remove one or more items from the list')
    args = argparser.parse_args()

这会产生你想要的结果:

usage: arg.py [-h] [-a Item(s)] [-r Item(s)]

它还修复了方括号,argparse 也不喜欢。