当nargs='*'时,如何从argparse生成更好的帮助信息

5

像许多命令行工具一样,我的工具也可以接受可选的文件名。 Argparse 似乎通过 nargs='*' 支持此功能,这对我来说已经正常工作:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument(
        'files',
        help='file(s) to parse instead of stdin',
        nargs='*')

parser.parse_args()

然而,help输出的格式很奇怪:

$ ./help.py -h
usage: help.py [-h] [files [files ...]]

我该如何避免嵌套的可选和重复参数名称?在Unix中,重复添加[files ...]没有增加额外信息,这是表明可选参数列表的传统方式:
$ grep --help
usage: grep [-abcDEFGHhIiJLlmnOoqRSsUVvwxZ] [-A num] [-B num] [-C[num]]
        [-e pattern] [-f file] [--binary-files=value] [--color=when]
        [--context[=num]] [--directories=action] [--label] [--line-buffered]
        [--null] [pattern] [file ...]

$ ls --help
Usage:
  exa [options] [files...]

$ vim --help
Usage:
  nvim [options] [file ...]      Edit file(s)

非常感谢任何帮助。我尝试使用argparse,因为使用它似乎是Python最佳实践,但这个帮助输出对我来说是无法接受的。


1
看文档,有一个例子格式与你期望的相同 https://docs.python.org/3/library/argparse.html#argparse.MetavarTypeHelpFormatter (第三个例子),这很可能是一个错误。 - user3064538
@Boris 谢谢。我看到了,但是运行示例代码不再产生文档记录的结果。 - Jeff Schwab
1
@JeffSchwab,事实证明它不是“不再生产”,而是“您的Python版本尚未生产”。这在 Python 3.9 中已更改以按照您的预期工作,该版本于2020年10月5日发布。 - user3064538
太棒了,谢谢!已确认使用来自DockerHub的python:3.9版本。 - Jeff Schwab
请参见 https://stackoverflow.com/questions/58797637/issue-with-automatic-usage-string-from-argparse-with-nargs-and-metavar-with。 - user3064538
2个回答

6
这个问题在 Python 3.9 中已经修复,详情请见https://bugs.python.org/issue38438解决它的提交a0ed99bc
如果在3.9上运行您的代码,则会产生您期望的使用消息。
Python 3.9.0 (default, Oct 12 2020, 02:44:01) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('files', help='file(s) to parse instead of stdin', nargs='*')
_StoreAction(option_strings=[], dest='files', nargs='*', const=None, default=None, type=None, choices=None, help='file(s) to parse instead of stdin', metavar=None)
>>> parser.print_help()
usage: [-h] [files ...]

1

对于 Python 3.9 之前的所有版本:

您可以通过传递一个usage="%(prog)s [options]"字符串在实例化 ArgumentParser 时或通过更新现有实例的 usage 属性来覆盖使用行。

如果您想让 ArgumentParser.format_usage() 生成用法字符串,但只替换nargs选项,可以与此结合使用正则表达式或字符串替换。

例如:

import argparse
import re

parser = argparse.ArgumentParser()
parser.add_argument(
    'files',
    help='file(s) to parse instead of stdin',
    nargs='*',
)

usage = parser.format_usage()[7:]  # remove "usage: " prefix
parser.usage = re.sub(r'\[(.+?) \[\1 ...\]\]', r'[\1 ...]', usage)

parser.parse_args()

生成:

usage: test.py [-h] [files ...]

positional arguments:
  files       file(s) to parse instead of stdin

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

谢谢,这肯定比没有好。但我不想自己维护使用行;我希望argparse能够做正确的事情。说实话,这似乎是一个bug,我很惊讶没有找到任何现有的讨论。 - Jeff Schwab
1
@JeffSchwab 我已经更新了答案,包括一个示例,演示如何让argparse为您处理用法字符串,同时仅覆盖此行为。 - John M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接