【问题标题】:Correct Usage of Argparse for Subparser Subcommands为 Subparser 子命令正确使用 Argparse
【发布时间】:2019-12-25 08:12:40
【问题描述】:

如何让 argparse 子解析器只解析它自己的参数?似乎在子解析器上调用parse_known_args 会解析所有父级的参数。

鉴于此简化版本,以及输入 foo bar

main_parser = argparse.ArgumentParser()
main_parser.add_argument("command")
args, subc_args = main_parser.parse_known_args()
print("main parser = {}".format(args))

sub_parser = main_parser.add_subparsers()
sub_command_parser = sub_parser.add_parser("sub_command")
sub_command_parser.add_argument("hostname")

sub_args = sub_command_parser.parse_known_args()
print("sub parser = {}".format(sub_args))

第一次打印返回我所期望的:

main parser = Namespace(command='foo')

而倒数第二行返回相同的内容,只是将额外的参数作为“未知”列表的一部分:

sub parser = (Namespace(hostname='foo'), ['bar'])

我怎样才能得到像sub_command_parser.parse_known_args() 这样的东西来忽略它之前给出的参数?我需要sub_args 只包含那些用sub_command_parser.add_argument() 添加的参数。我可以像这样直接打电话给他们; sub_args[1],但这似乎很老套且不可靠。

背景:我将一个包拆分为一个主文件作为处理顶级参数的入口点,以及执行实际工作的模块。我想在主文件中添加顶级“主解析器”,并在模块中添加子解析器。

【问题讨论】:

  • parse_known_args() 解析sys.argv[1:] 列表。它不会从该列表中删除元素。通常我们调用main_parser,让它调用子解析器,剩下的参数。你可以试试sub_command_parser.parse_args(subc_args);也就是将第一个未知数提供给子解析器。而那个用途,不要打扰sub_parsers 机制。只需定义子解析器。
  • 另一种说法——如果你有多个解析器,每个解析器都会看到相同的sys.argv[1:]。如果您想更改它,可以即时自定义sys.argv,或者使用argv 列表调用parse_args
  • 谢谢@hpaulj,我已经在下面发布了答案。如果您想获得荣誉;)只需发布作为答案,我会将您的标记为解决方案。

标签: python python-3.x argparse


【解决方案1】:

这有帮助,谢谢@hpaulj。看起来没有一个很好的方法来处理带有 argparse 的子命令。遇到此问题的人的完整工作示例是:

# Main file
#
main_parser = argparse.ArgumentParser()
main_parser.add_argument("command")
args, subc_args = main_parser.parse_known_args()
print("main parser = {}".format(args.command))

# Subcommand file
#
sub_parser = main_parser.add_subparsers()
sub_command_parser = sub_parser.add_parser("sub_command")
sub_command_parser.add_argument("hostname")

sub_args, unknown_args = sub_command_parser.parse_known_args(subc_args)
print("sub parser = {}".format(sub_args.hostname))

请注意,在调用类时(或者您已经在子命令文件中实现了子解析器创建),您还需要提供 subc_args 作为可选参数。

编辑:在主解析器中指定 add_help=False 可以解决这个问题 :)

【讨论】:

    【解决方案2】:

    普通子命令用法,都在一个文件中:

    main_parser = argparse.ArgumentParser()
    sub_parser = main_parser.add_subparsers(dest='command')
    sub_command_parser = sub_parser.add_parser("sub_command")
    sub_command_parser.add_argument("hostname")
    
    args = main_parser.parse_args()
    print(args)
    args.command   # name of the subcommand
    args.hostname   # value from subcommand argument
    

    这会自动将剩余的字符串传递给子解析器,在command 中命名。这种用法应该在 Python 文档中明确。

    如果您要在文件之间拆分解析器,并分别调用它们,请跳过add_subparsers 位。它对你没有任何作用。

    # Main file
    #
    parser = argparse.ArgumentParser()
    parser.add_argument("command")
    args, subc_args = parser.parse_known_args()
    print("main parser = {}".format(args.command))
    
    # Subcommand file
    #
    parser = argparse.ArgumentParser()
    parser.add_argument("hostname")
    sub_args, unknown_args = parser.parse_known_args(subc_args)
    print("sub parser = {}".format(sub_args.hostname))
    

    【讨论】:

    • 是的,这确实是我最终要做的(跳过子解析器)。此解决方案的唯一问题是,当您在调用脚本时使用 -h 时,即使它在子命令之后,您总是会得到第一个解析器的帮助,因为您之前立即调用 parse_known_args第二个解析器。我打算改用 click 库,它更适合这种事情。
    猜你喜欢
    • 2017-11-01
    • 2018-06-24
    • 2015-07-30
    • 2011-09-15
    • 2013-02-27
    • 2014-11-30
    • 2011-06-02
    • 2014-03-08
    相关资源
    最近更新 更多