Argparse:混合父解析器和子解析器 [英] Argparse: mixing parent parser with subparsers

查看:41
本文介绍了Argparse:混合父解析器和子解析器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个简单的工具,它接受任意数量的输入文件并对每个文件执行一个操作.语法非常简单:

I want to write a simple tool that takes an arbitrary number of input files and performs one operation on each of them. The syntax is stupidly simple:

mytool operation input1 input2 ... inputN

其中一些操作可能需要额外的参数

Some of these operations may require an extra argument

mytool operation op_argument input1 input2 ... inputN 

除此之外,我希望用户能够指定是否应就地执行操作,以及指定输出的目标目录.

In addition to this I'd like the users to be able to specify whether the operations should be performed in place, and to specify the target directory of the output.

mytool -t $TARGET --in-place operation op_argument input1 input2 input3

作为最后一个要求,我希望用户能够获得有关每个操作以及整个工具使用的帮助.

And as a very last requirement, I'd like users to be able to get help on each operation individually, as well as on the usage of the tool as a whole.

这是我为上述工具设计参数解析器的尝试,以及一个最小的、完整的、可验证的示例:

Here's my attempt at designing an Argument Parser for said tool, together with a Minimal, Complete, Verifiable Example:

#!/bin/env python

import argparse
from collections import namedtuple

Operations = namedtuple('Ops', 'name, argument, description')
IMPLEMENTED_OPERATIONS = {'echo': Operations('echo',
                                             None,
                                             'Echo inputs'),
                          'fancy': Operations('fancy',
                                              'fancyarg',
                                              'Do fancy stuff')}


if __name__ == "__main__":

    # Parent parser with common stuff.
    parent = argparse.ArgumentParser(add_help=False)
    parent.add_argument('-t', '--target-directory', type=str, default='.',
                        help="An output directory to store output files.")
    parent.add_argument('-i', '--in-place', action='store_true',
                        help="After succesful execution, delete the inputs.")
    # The inputfiles should be the very last positional argument.
    parent.add_argument('inputfiles', nargs='*', type=argparse.FileType('r'),
                        help="A list of input files to operate on.")

    # Top level parser.
    top_description = "This is mytool. It does stuff"
    parser = argparse.ArgumentParser(prog="mytool",
                                     description=top_description,
                                     parents=[parent])

    # Operation parsers.
    subparsers = parser.add_subparsers(help='Sub-command help', dest='chosen_op')

    op_parsers = {}
    for op_name, op in IMPLEMENTED_OPERATIONS.items():
        op_parsers[op_name] = subparsers.add_parser(op_name,
                                                    description=op.description,
                                                    parents=[parent])
        if op.argument is not None:
            op_parsers[op_name].add_argument(op.argument)

    args = parser.parse_args()
    op_args = {}
    for key, subparser in op_parsers.items():
        op_args[key] = subparser.parse_args()


    print(args.chosen_op)

我遇到的问题是位置参数的顺序错误.不知何故,我实现的方式让 Argparse 认为操作(及其 op_argument)应该在输入文件之后,这显然不是这种情况.

The problem I have is that the order of the positional arguments is wrong. Somehow, the way I implemented this makes Argparse think that the operation (and its op_argument) should come after the input files, which is obviously not the case.

如何将父位置参数(在我的情况下是输入文件)作为最后一个位置参数?

推荐答案

对于主解析器来说,subparsers 只是另一个位置参数,但具有唯一的 nargs ('+...').所以它将首先查找 inputfiles 参数,然后将任何剩余的部分分配给 subparsers.

To the main parser, subparsers is just another positional argument, but with a unique nargs ('+...'). So it will look for the inputfiles arguments first, and then allocate any left overs to subparsers.

将位置词与子解析器混合起来很棘手.最好将 inputfiles 定义为每个子解析器的参数.

Mixing positionals with subparsers is tricky. It is best to define inputfiles as an argument for each subparser.

parents 可以很容易地将相同的参数集添加到多个子解析器 - 但是这些参数将首先添加.

parents can make it easy to add the same set of arguments to several subparsers- however those arguments will added first.

所以我认为你想要:

for op_name, op in IMPLEMENTED_OPERATIONS.items():
        op_parsers[op_name] = subparsers.add_parser(op_name,
                                                    description=op.description,
                                                    parents=[parent])
        if op.argument is not None:
            op_parsers[op_name].add_argument(op.argument)

        op_parsers[op_name].add_argument('inputfiles', nargs='*', type=argparse.FileType('r'),
                        help="A list of input files to operate on.")

至于帮助,正常行为是获取主解析器或每个子解析器的帮助.将这些组合成一个显示一直是几个 SO 问题的主题.这是可能的,但并不容易.

As for the help, the normal behavior is to get help for the main parser, or for each subparser. Combining those into one display has been the topic of several SO questions. It's possible but not easy.

主解析器按顺序处理输入字符串 - 标志、位置等.当它处理 subparsers 位置时,它将任务交给名称子解析器,以及所有剩余的命令行字符串.然后,子解析器就像一个新的独立解析器,并将命名空间返回给主解析器以合并到主命名空间中.主解析器不会继续解析命令行.所以子解析器的动作总是最后.

The main parser handles input strings in order - flags, positionals etc. When it handles the subparsers positional, it hands the task of to the name subparser, along with all remaining commandline strings. The subparser then acts like a new independent parser, and returns a namespace to the main parser to be incorporated into the main namespace. The main parser does not resume parsing the commandline. So the subparser action is always last.

这篇关于Argparse:混合父解析器和子解析器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆