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

查看:141
本文介绍了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天全站免登陆