我想编写一个简单的工具,它可以使用任意数量的输入文件并对每个文件执行一个操作。语法很简单:
mytool operation input1 input2 ... inputN
其中一些操作可能需要额外的参数
mytool operation op_argument input1 input2 ... inputN
除此之外,我希望用户能够指定是否应该执行操作,以及指定输出的目标目录。
mytool -t $TARGET --in-place operation op_argument input1 input2 input3
作为最后一项要求,我希望用户能够单独获得每项操作的帮助,以及整个工具的使用情况。
我尝试为所述工具设计一个Argument Parser,以及一个最小的,完整的,可验证的示例:
#!/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)应该在输入文件之后,显然不是这样。
我怎样才能将父位置参数(在我的情况下是输入文件)作为最后一个位置参数?
答案 0 :(得分:1)
对于主解析器,subparsers
只是另一个位置参数,但具有唯一的nargs
('+ ...')。因此,它将首先查找inputfiles
个参数,然后将任何剩余部分分配给subparsers
。
将位置与子分析器混合是棘手的。最好将inputfiles
定义为每个subparser的参数。
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
位置时,它将任务交给名称subparser以及所有剩余的命令行字符串。然后,subparser就像一个新的独立解析器,并将一个命名空间返回给主解析器,以便合并到主命名空间中。主解析器不会继续解析命令行。所以subparser动作总是持续的。