Python argparse,根据父参数值提供不同的参数

Python argparse, provide different arguments based on parent argument value

这是我想做的事情: 看起来像 git 命令行为的命令。无论您输入 git commit 还是 git checkout,您都不会获得相同的选项。 但就我而言,我想根据参数值(文件名)提供不同的参数,如下所示:

>cmd file.a -h
usage: cmd filename [-opt1] [-opt2]
positional arguments:
filename         file to process
optional arguments:
-opt1            do something on files of type 'a'
-opt2            do something else on files of type 'a'

>cmd file.b -h
usage: cmd filename [-opt3] [-opt4] 
positional arguments:
filename         file to process
optional arguments:
-opt3            do something on files of type 'b'
-opt4            do something else on files of type 'b'

是否可以使用 python 和 argparse 做这种事情?
到目前为止我尝试过的是:

parser = argparse.Argument_parser(prog='cmd')
subparsers = parser.add_subparsers()

parser.add_argument('filename', 
                    help="file or sequence to process")

args = parser.parse_args(args=argv[1:])

sub_parser = subparsers.add_parser(args.filename, help="job type")
base, ext = os.path.splitext(args.filename)
if ext == 'a':
    sub_parser.add_argument("-opt1", action='store_true')
    sub_parser.add_argument("-opt2", action='store_true')
elif ext == 'b':
    sub_parser.add_argument("-opt3", action='store_true')
    sub_parser.add_argument("-opt4", action='store_true')

args = parser.parse_args(args=argv[1:])

我不知道我是否应该使用子解析器或子解析器或组,我有点迷失在 argparse

提供的所有可能性中

当你查看 parse_args() implementation 时,你会注意到它一次解析所有参数(它不使用 yield 来连续生成状态)所以你必须准备你的结构之前和之后的一半参数将被解析。

Taking from official example in the docs 你应该在开始解析之前添加子解析器,如下所示:

import argparse

parser = argparse.ArgumentParser(prog='PROG')
subparsers = parser.add_subparsers(help='sub-command help')

# create the parser for the "a" command
parser_a = subparsers.add_parser('a', help='a help')
parser_a.add_argument("--opt1", action='store_true')
parser_a.add_argument("--opt2", action='store_true')

# create the parser for the "b" command
parser_b = subparsers.add_parser('b', help='b help')
parser_b.add_argument("--opt3", action='store_true')
parser_b.add_argument("--opt4", action='store_true')

# parse some argument lists
print(parser.parse_args())

并且输出(在命令行中)帮助很好地打印:

D:\tmp>s.py -h
usage: PROG [-h] {a,b} ...

positional arguments:
  {a,b}       sub-command help
    a         a help
    b         b help

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

A 个参数被解析

D:\tmp>s.py a --opt1
Namespace(opt1=True, opt2=False)

B 个参数被解析

D:\tmp>s.py b
Namespace(opt3=False, opt4=False)

还有参数:

D:\tmp>s.py b --opt3
Namespace(opt3=True, opt4=False)

运行 A B 中的参数导致错误:

D:\tmp>s.py b --opt2
usage: PROG [-h] {a,b} ...
PROG: error: unrecognized arguments: --opt2

此外,如果您需要使用 identify which subparser,您可以将 dest=name 添加到 parser.add_subparsers() 调用(我认为文档中没有正确强调这一点):

subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')

结果为:

D:\tmp>s.py b --opt3
Namespace(opt3=True, opt4=False, subparser_name='b')

如果您确实需要动态创建参数(例如从 expensive 资源加载一些参数选项),您可以使用 parse_known_args():

Sometimes a script may only parse a few of the command-line arguments, passing the remaining arguments on to another script or program. In these cases, the parse_known_args() method can be useful. It works much like parse_args()except that it does not produce an error when extra arguments are present. Instead, it returns a two item tuple containing the populated namespace and the list of remaining argument strings.

毕竟,parse_args() 只是检查尾随参数:

def parse_args(self, args=None, namespace=None):
    args, argv = self.parse_known_args(args, namespace)
    if argv:
        msg = _('unrecognized arguments: %s')
        self.error(msg % ' '.join(argv))
    return args

然后你可以在 argv 上重新执行另一个解析器,但我可以想象这会带来一些问题,我不会推荐它,直到 真的 必要。