1

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

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

以下是我的程序如何分解的基础知识:

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

这是我得到的行为:

当我在没有子命令和参数的情况下调用主函数时,文本文件中的 help_text 输出,然后像 2-3 行样板代码,我似乎无法摆脱。还因为我的文本文件中出现了用法这个词,所以它说“用法:用法”

当我调用主命令然后键入 --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 可以完美地打印所有内容而没有任何额外的行?

4

1 回答 1

2

修改您的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

用法类似于 main 的,但添加了一些关于如何调用此子解析器及​​其参数的信息。

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

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 错误/问题上看到了类似的内容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,还会显示 subparser 的 prog。我给了 main 一个“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 的用法是如何被合并到 subparser 的“prog”中的。

在错误/问题中,我发现主解析器usage被合并到prog子解析器中。这就是为什么你看到重复。

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

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

于 2022-02-25T21:13:18.177 回答