繁体   English   中英

如何使用 argparse 处理 CLI 子命令

[英]How to handle CLI subcommands with argparse

我需要实现一个命令行界面,程序在其中接受子命令。

例如,如果程序名为“foo”,则 CLI 将如下所示

foo cmd1 <cmd1-options>
foo cmd2
foo cmd3 <cmd3-options>

cmd1cmd3必须至少与它们的选项之一一起使用,并且三个cmd*参数始终是互斥的。

我正在尝试在 argparse 中使用子解析器,但目前没有成功。 问题在于cmd2 ,它没有参数:

如果我尝试添加不带参数的子解析器条目,则parse_args返回的命名空间将不包含任何告诉我已选择此选项的信息(请参见下面的示例)。 如果我尝试将cmd2作为参数添加到parser (而不是子parser ),则 argparse 将期望cmd2参数后跟任何子解析器参数。

有没有一种简单的方法可以使用argparse实现这一点? 用例应该很常见……

以下是我迄今为止尝试的更接近我需要的内容:

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='Functions')
parser_1 = subparsers.add_parser('cmd1', help='...')
parser_1.add_argument('cmd1_option1', type=str, help='...')

parser_2 = subparsers.add_parser(cmd2, help='...')

parser_3 = subparsers.add_parser('cmd3', help='...')
parser_3.add_argument('cmd3_options', type=int, help='...')

args = parser.parse_args()

首先,子解析器永远不会插入到命名空间中。 在您发布的示例中,如果您尝试将脚本运行为:

$python3 test_args.py cmd1 1
Namespace(cmd1_option1='1')

其中test_args.py包含您提供的代码(开头是import argparse ,结尾是print(args) )。

请注意,仅对其参数没有提及cmd1 这是设计使然

正如评论中所指出的,您可以添加将dest参数传递给add_subparsers调用的信息。

处理这些情况的常用方法是使用子解析器的set_defaults方法:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='Functions')
parser_1 = subparsers.add_parser('cmd1', help='...')
parser_1.add_argument('cmd1_option1', type=str, help='...')
parser_1.set_defaults(parser1=True)

parser_2 = subparsers.add_parser('cmd2', help='...')
parser_2.set_defaults(parser2=True)

parser_3 = subparsers.add_parser('cmd3', help='...')
parser_3.add_argument('cmd3_options', type=int, help='...')
parser_3.set_defaults(parser_3=True)

args = parser.parse_args()
print(args)

结果是:

$python3 test_args.py cmd1 1
Namespace(cmd1_option1='1', parser1=True)
$python3 test_args.py cmd2
Namespace(parser2=True)

通常,不同的子解析器在大多数情况下会以完全不同的方式处理参数。 通常的模式是使用不同的函数来运行不同的命令并使用set_defaults来设置func属性。 当您解析参数时,您只需调用该可调用对象:

subparsers = parser.add_subparsers()
parser_1 = subparsers.add_parser(...)
parser_1.set_defaults(func=do_command_one)

parser_k = subparsers.add_parser(...)
parser_k.set_defaults(func=do_command_k)

args = parser.parse_args()
if args.func:
    args.func(args)

如果add_subparsers命令被赋予一个dest则可以将子解析器标识添加到主Namespace

从文档:

但是,如果有必要检查被调用的子解析器的名称,则 add_subparsers() 调用的 dest 关键字参数将起作用:

>>> parser = argparse.ArgumentParser()
>>> subparsers = parser.add_subparsers(dest='subparser_name')
>>> subparser1 = subparsers.add_parser('1')
>>> subparser1.add_argument('-x')
>>> subparser2 = subparsers.add_parser('2')
>>> subparser2.add_argument('y')
>>> parser.parse_args(['2', 'frobble'])
Namespace(subparser_name='2', y='frobble')

默认情况下destargparse.SUPPRESS ,它阻止子subparsers将名称添加到namespace

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM