首页 > 解决方案 > 使用 python argparse 解析可变数量的命令

问题描述

我正在用 Python 开发一个命令行工具,它的功能被分解为许多子命令,基本上每个子命令都作为参数输入和输出文件。棘手的部分是每个命令需要不同数量的参数(有些不需要输出文件,有些需要多个输入文件等)。

理想情况下,接口将被称为:

./test.py ncinfo inputfile

然后,解析器会意识到该ncinfo命令需要一个参数(如果这不适合输入命令,它会抱怨),然后它会调用该函数:

ncinfo(inputfile)

做实际的工作。

例如,当命令需要更多选项时

./test.py timmean inputfile outputfile

解析器会意识到这一点,检查是否确实给出了两个参数,然后它调用:

timmean(inputfile, outputfile)

该方案理想地适用于 1 参数命令、2 参数命令等的任意列表。

但是,我正在努力使用 Python 来获得这种行为argparse。这是我到目前为止所拥有的:

#! /home/navarro/SOFTWARE/anadonda3/bin/python
import argparse

# create the top-level parser
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

# create the parser for the "ncinfo" command
parser_1 = subparsers.add_parser('ncinfo', help='prints out basic netCDF strcuture')
parser_1.add_argument('filein', help='the input file')

# create the parser for the "timmean" command
parser_2 = subparsers.add_parser('timmean', help='calculates temporal mean and stores it in output file')
parser_2.add_argument('filein', help='the input file')
parser_2.add_argument('fileout', help='the output file')


# parse the argument lists
parser.parse_args()

print(parser.filein)
print(parser.fileout)    

但这并没有按预期工作。首先,当我调用不带参数的脚本时,我没有收到错误消息告诉我有哪些选项。其次,当我尝试运行要使用的程序时ncinfo,我得到一个错误

./test.py ncinfo testfile
Traceback (most recent call last):
  File "./test.py", line 21, in <module>
    print(parser.filein)
AttributeError: 'ArgumentParser' object has no attribute 'filein'

我做错了什么使我无法实现预期的行为?在这种情况下使用是否subparsers明智?

加分点:有没有办法概括命令的定义,这样我就不需要手动添加每个命令?例如,将所有 1 参数命令分组到一个列表中,然后在循环中定义解析器。这听起来很合理,但我不知道这是否可能。否则,随着工具数量的增加,解析器本身将变得难以维护。

标签: pythonargparse

解决方案


import argparse
import sys

SUB_COMMANDS = [
    "ncinfo",
    "timmean"
]


def ncinfo(args):
    print("executing: ncinfo")
    print("  inputfile: %s" % args.inputfile)


def timmean(args):
    print("executing: timmean")
    print("  inputfile: %s" % args.inputfile)
    print("  outputfile: %s" % args.outputfile)


def add_parser(subcmd, subparsers):
    if subcmd == "ncinfo":
        parser = subparsers.add_parser("ncinfo")
        parser.add_argument("inputfile", metavar="INPUT")
        parser.set_defaults(func=ncinfo)
    elif subcmd == "timmean":
        parser = subparsers.add_parser("timmean")
        parser.add_argument("inputfile", metavar="INPUT")
        parser.add_argument("outputfile", metavar="OUTPUT")
        parser.set_defaults(func=timmean)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-o', '--common-option', action='store_true')
    subparsers = parser.add_subparsers(help="sub-commands")
    for cmd in SUB_COMMANDS:
        add_parser(cmd, subparsers)
    args = parser.parse_args(sys.argv[1:])
    if args.common_option:
        print("common option is active")
    try:
        args.func(args)
    except AttributeError:
        parser.error("too few arguments")

一些使用示例:

$ python test.py --help
usage: test.py [-h] [-o] {ncinfo,timmean} ...

positional arguments:
  {ncinfo,timmean}     sub-commands

optional arguments:
  -h, --help           show this help message and exit
  -o, --common-option
$ python test.py ncinfo --help
usage: test.py ncinfo [-h] INPUT

positional arguments:
  INPUT

optional arguments:
  -h, --help  show this help message and exit
$ python test.py timmean --help
usage: test.py timmean [-h] INPUT OUTPUT

positional arguments:
  INPUT
  OUTPUT

optional arguments:
  -h, --help  show this help message and exit
$ python test.py -o ncinfo foo
common option is active
executing: ncinfo
  inputfile: foo
$ python test.py -o timmean foo bar
common option is active
executing: timmean
  inputfile: foo
  outputfile: bar

推荐阅读