Argparse 两次输出帮助文本

Argparse outputting help text twice

经过一个小时的谷歌搜索,除了我自己,我找不到任何人遇到过类似的问题。我用 argparse 创建了一个命令行界面。最初我曾尝试利用 argparse 的内置帮助文本行为。但是我的老板对默认的帮助文本不满意,所以他让我在文本文件中写下完整的 usage/help 文本,然后只显示整个文件。

由于某些原因,在某些情况下,它输出了两次文本。

下面是我的程序分解的基础知识:

我有一个顶级解析器。我阅读了我的帮助文本文件,将其设置为字符串 help_text,然后在解析器上设置“usage=help_text”。然后我创建子解析器(其中 4 个,然后是一个基本案例)来创建子命令。这些子解析器中只有一个具有任何附加参数(一个位置参数,一个可选参数)。在我重新编写帮助文本之前,我通过使用“help=”为每个单独的子命令提供了帮助文本,但现在这些都是空白的。最后,我设置了一个基本案例,以便在没有给出子命令时显示帮助文本。

这是我得到的行为:

当我在没有子命令和参数的情况下调用主函数时,我的 help_text 从文本文件输出,然后像 2-3 行样板一样我似乎无法摆脱.也因为单词 usage 出现在我的文本文件中,它说“usage: usage”

当我调用主命令然后键入 --help 时,会发生与上面完全相同的事情。

当我调用一个具有必需位置参数的子命令但我没有包含该参数时...它会吐出整个帮助文本两次。在第二次打印的正上方,它打印了该子命令的默认用法行。

最后,当我使用一个没有参数的不同子命令并给它一个参数(一个太多)时,它会完全正确地吐出所有内容,甚至最后没有额外的几行。

我不知道如何引起注意或讲故事。下面是脚本的main函数(我可以验证这个问题只发生在使用了argparse的main函数,而不是main函数调用的其他函数):

def main():
    # Import help text from file
    p = Path(__file__).with_name("help_text.txt")
    with p.open() as file:
        help_text = file.read()

    # Configure the top level Parser
    parser = argparse.ArgumentParser(prog='hubmap-clt', description='Name of cli', usage=help_text)
    subparsers = parser.add_subparsers()

    # Create Subparsers to give subcommands
    parser_transfer = subparsers.add_parser('subcommandone')
    parser_transfer.add_argument('argument1', type=str)
    parser_transfer.add_argument('--optionalargument', default='mydefault')
    parser_login = subparsers.add_parser('subcommandtwo')
    parser_whoami = subparsers.add_parser('subcommandthree')
    parser_logout = subparsers.add_parser('subcommandfour')

    # Assign subparsers to their respective functions
    parser_subcommandone.set_defaults(func=subcommandone)
    parser_subcommandtwo.set_defaults(func=subcommandtwo)
    parser_subcommandthree.set_defaults(func=subcommandthree)
    parser_subcommandfour.set_defaults(func=subcommandfour)
    parser.set_defaults(func=base_case)

    # Parse the arguments and call appropriate functions
    args = parser.parse_args()
    if len(sys.argv) == 1:
        args.func(args, parser)
    else:
        args.func(args)

所以澄清一下:

为什么有时会出现额外的几行样板帮助文本,如下所示:

 name of cli

 positional arguments:
     {subcommandone,subcommandtwo,subcommandthree,subcommandfour}

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

为什么使用参数太少的 subcommandone 会打印两次帮助文本(而不是样板帮助文本的额外行。

为什么使用带有太多参数的 subcommandtwo 可以完美地打印所有内容而没有任何额外的行?

修改你的main

def foo():
    # Import help text from file
    # p = Path(__file__).with_name("help_text.txt")
    # with p.open() as file:
    #    help_text = file.read()
    help_text = "cli usage: foobar\n morebar"

    # Configure the top level Parser
    parser = argparse.ArgumentParser(
        prog="hubmap-clt", description="Name of cli", usage=help_text
    )
    subparsers = parser.add_subparsers()

    # Create Subparsers to give subcommands
    parser_transfer = subparsers.add_parser("subcommandone")
    parser_transfer.add_argument("argument1", type=str)
    parser_transfer.add_argument("--optionalargument", default="mydefault")
    parser_login = subparsers.add_parser("subcommandtwo")
    # parser_whoami = subparsers.add_parser("subcommandthree")
    # parser_logout = subparsers.add_parser("subcommandfour")

    # Assign subparsers to their respective functions
    parser_transfer.set_defaults(func="subcommandone")
    parser_login.set_defaults(func="subcommandtwo")
    # parser_subcommandthree.set_defaults(func="subcommandthree")
    # parser_subcommandfour.set_defaults(func="subcommandfour")
    parser.set_defaults(func="base_case")

    return parser

在迭代 ipython 会话中:

In [8]: p = foo()

In [9]: p.print_usage()
usage: cli usage: foobar
 morebar

用法和我指定的完全一样。以及对主要解析器的帮助:

In [10]: p.print_help()
usage: cli usage: foobar
 morebar

Name of cli

positional arguments:
  {subcommandone,subcommandtwo}

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

这就是我所期望的参数。

对子解析器的帮助:

In [11]: p.parse_args(["subcommandone", "-h"])
usage: cli usage: foobar
 morebar subcommandone [-h] [--optionalargument OPTIONALARGUMENT] argument1

positional arguments:
  argument1

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

用法与主要用法类似,但增加了一些有关如何调用此子解析器及​​其参数的信息。

在没有足够值的情况下调用子解析器时出错:

In [15]: p.parse_args(["subcommandone"])
usage: cli usage: foobar
 morebar subcommandone [-h] [--optionalargument OPTIONALARGUMENT] argument1
cli usage: foobar
 morebar subcommandone: error: the following arguments are required: argument1

重复 cli usage 是否让您感到困扰?这个错误是由子解析器引发的,我怀疑额外的来自该子解析器的 prog 。我想我在 Python bug/issues 上为 argparse.

看到了类似的东西

错误过多:

In [17]: p.parse_args(["subcommandone", "test", "extra"])
usage: cli usage: foobar
 morebar
hubmap-clt: error: unrecognized arguments: extra

在这种情况下,错误是由主解析器产生的,因此出现“hubmat-clt”prog.

更改prog

...: parser_transfer = subparsers.add_parser( ...: "subcommandone", prog="hubmap-clt sobcommandone" ...:)

In [21]: p.parse_args(["subcommandone", "test", "extra"])
usage: cli usage: foobar
 morebar
hubmap-clt: error: unrecognized arguments: extra

In [22]: p.parse_args(["subcommandone"])
usage: hubmap-clt sobcommandone [-h] [--optionalargument OPTIONALARGUMENT] argument1
hubmap-clt sobcommandone: error: the following arguments are required: argument1

[21] 和之前的 [17] 一样。但是 [22] 现在显示的是我设置的 prog。我也可以为子解析器指定自定义 usage

如果我修改函数以使用默认用法和prog,还会显示子解析器的prog。我给了主要一个“main_foo”位置参数:

In [30]: p = foo()
hubmap-clt main_foo subcommandone
In [31]: p.parse_args(["subcommandone"])
Out[31]: Namespace(main_foo='subcommandone')
In [32]: p.parse_args(["foo", "subcommandone"])
usage: hubmap-clt main_foo subcommandone [-h] [--optionalargument OPTIONALARGUMENT] argument1
hubmap-clt main_foo subcommandone: error: the following arguments are required: argument1

注意如何将 main 的用法合并到子解析器的 'prog' 中。

在 bug/issue 中,我发现主解析器的 usage 被合并到子解析器的 prog 中。这就是您看到重复项的原因。

https://bugs.python.org/issue42297 [argparse] 使用自定义用法文本时错误消息格式错误

这个错误问题的最近日期表明自定义用法并不常见,与子解析器一起使用时更是如此。正如我在这个问题上的 post 指出的那样,主解析器、“子解析器”命令和各个子解析器之间的关系变得复杂。