【问题标题】:Argparse: mixing parent parser with subparsersArgparse:混合父解析器和子解析器
【发布时间】:2017-12-02 05:10:40
【问题描述】:

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

mytool operation input1 input2 ... inputN

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

mytool operation op_argument input1 input2 ... inputN 

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

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

最后一项要求是,我希望用户能够单独获得每个操作的帮助,以及整个工具的使用方面的帮助。

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

#!/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)应该在输入文件之后,显然不是这样。

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

【问题讨论】:

  • parents=[parent] parents 会添加来自所引用解析器的所有参数。您隐含地告诉mytool 接受'inputfiles' 作为第一个参数。

标签: python parameter-passing argparse


【解决方案1】:

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

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

parents 可以轻松地将同一组参数添加到多个子解析器 - 但是这些参数将首先添加。

所以我想你想要:

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 问题的主题。这是可能的,但并不容易。


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

【讨论】:

  • 最后一次编辑确实有效,但会打开一个新问题。操作本身最终会作为输入进行全局化。如果我尝试mytool echo file.txt,它会告诉我argument inputfiles can't open echo. No such file or directory =/(当然,我从父级删除了输入文件)
  • 我必须查看完整的更正代码; echo 应该作为子解析器名称处理。
猜你喜欢
  • 2014-08-31
  • 2018-12-31
  • 1970-01-01
  • 2015-07-17
  • 2018-01-04
  • 2013-02-27
  • 2012-04-25
  • 2017-09-27
  • 1970-01-01
相关资源
最近更新 更多