Разбор переменного количества команд с помощью python argparse - PullRequest
0 голосов
/ 10 января 2019

Я разрабатываю инструмент командной строки с 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 Ответ

0 голосов
/ 10 января 2019
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
...