Как мне реализовать «вложенные» подкоманды в Python? - PullRequest
8 голосов
/ 15 декабря 2011

Реализация «вложенных» подкоманд в Python с помощью cmdln.

Я не уверен, что здесь используется правильная терминология.Я пытаюсь реализовать инструмент командной строки, используя cmdln , который допускает "вложенные" подкоманды.Вот пример из реальной жизни:

git svn rebase

Каков наилучший способ реализации этого?Я искал дополнительную информацию по этому вопросу в документе, здесь и в Интернете, но он оказался пустым.(Возможно, я искал с неверными терминами.)

За исключением недокументированной функции, которая делает это автоматически, я сначала хотел, чтобы предыдущий обработчик подкоманды определил, что есть другая подкоманда, и снова отправил диспетчер команд.Я посмотрел внутреннюю часть cmdln, и диспетчер является частным методом _dispatch_cmd.Моя следующая мысль - создать моего собственного диспетчера под-подкоманд, но это кажется не совсем идеальным и грязным.

Любая помощь будет принята.

Ответы [ 3 ]

6 голосов
/ 28 июля 2015

Поздно здесь, на вечеринке, но мне пришлось сделать это совсем немного, и я нашел argparse довольно неуклюжим, чтобы сделать это.Это побудило меня написать расширение к argparse с именем arghandler , которое явно поддерживает это - возможно реализовать подкоманды с практически нулевыми строками кода.

Вот пример:

from arghandler import *

@subcmd
def push(context,args):
    print 'command: push'

@subcmd
def pull(context,args):
    print 'command: pull'

# run the command - which will gather up all the subcommands
handler = ArgumentHandler()
handler.run()
6 голосов
/ 15 декабря 2011

argparse очень упрощает подкоманды.

4 голосов
/ 20 августа 2012

Мне кажется, что в argparse есть небольшое ограничение для sub_parsers, если, скажем, у вас есть набор инструментов, которые могут иметь похожие параметры, которые могут распространяться на разные уровни.Такое может случиться редко, но если вы пишете подключаемый / модульный код, это может произойти.

У меня есть следующий пример.Это надуманно и не очень хорошо объяснено в настоящее время, потому что уже довольно поздно, но здесь это идет:

Usage: tool [-y] {a, b}
  a [-x] {create, delete}
    create [-x]
    delete [-y]
  b [-y] {push, pull}
    push [-x]
    pull [-x]
from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument('-x', action = 'store_true')
parser.add_argument('-y', action = 'store_true')

subparsers = parser.add_subparsers(dest = 'command')

parser_a = subparsers.add_parser('a')
parser_a.add_argument('-x', action = 'store_true')
subparsers_a = parser_a.add_subparsers(dest = 'sub_command')
parser_a_create = subparsers_a.add_parser('create')
parser_a_create.add_argument('-x', action = 'store_true')
parser_a_delete = subparsers_a.add_parser('delete')
parser_a_delete.add_argument('-y', action = 'store_true')

parser_b = subparsers.add_parser('b')
parser_b.add_argument('-y', action = 'store_true')
subparsers_b = parser_b.add_subparsers(dest = 'sub_command')
parser_b_create = subparsers_b.add_parser('push')
parser_b_create.add_argument('-x', action = 'store_true')
parser_b_delete = subparsers_b.add_parser('pull')
parser_b_delete.add_argument('-y', action = 'store_true')

print parser.parse_args(['-x', 'a', 'create'])
print parser.parse_args(['a', 'create', '-x'])
print parser.parse_args(['b', '-y', 'pull', '-y'])
print parser.parse_args(['-x', 'b', '-y', 'push', '-x'])

Вывод

Namespace(command='a', sub_command='create', x=True, y=False)
Namespace(command='a', sub_command='create', x=True, y=False)
Namespace(command='b', sub_command='pull', x=False, y=True)
Namespace(command='b', sub_command='push', x=True, y=True)

Как видите,трудно определить, где в цепочке был задан каждый аргумент.Вы можете решить эту проблему, изменив имя для каждой переменной.Например, вы можете установить для dest значение «x», «a_x», «a_create_x», «b_push_x» и т. Д., Но это будет болезненно и трудно отделить.

Альтернативой будетостановить ArgumentParser, как только он достигнет подкоманды, и передать оставшиеся аргументы другому, независимому парсеру, чтобы он мог генерировать отдельные объекты.Вы можете попытаться добиться этого, используя parse_known_args () и не определяя аргументы для каждой подкоманды.Тем не менее, это было бы нехорошо, потому что любые непроверенные аргументы, которые были до этого, все равно будут присутствовать и могут запутать программу.

Мне кажется, что это немного дешево, но полезный обходной путь - это argparse интерпретировать следующие аргументы как строкив списке.Это можно сделать, установив для префикса нулевой терминатор «\ 0» (или какой-либо другой «сложный в использовании» символ) - если префикс пуст, код выдаст ошибку, по крайней мере, в Python 2.7.3.

Пример:

parser = ArgumentParser()
parser.add_argument('-x', action = 'store_true')
parser.add_argument('-y', action = 'store_true')
subparsers = parser.add_subparsers(dest = 'command')
parser_a = subparsers.add_parser('a' prefix_chars = '\0')
parser_a.add_argument('args', type = str, nargs = '*')

print parser.parse_args(['-xy', 'a', '-y', '12'])

Вывод:

Namespace(args=['-y', '12'], command='a', x=True, y=True)

Обратите внимание, что он не использует второй параметр -y.Затем вы можете передать результат 'args' другому ArgumentParser.

Недостатки:

  • Справка может быть обработана неправильно.С этим
  • придется искать обходные пути, которые трудно отследить и требуют дополнительных усилий для обеспечения правильной цепочки сообщений об ошибках.
  • Немного больше накладных расходов, связанных снесколько ArgumentParsers.

Если у кого-то есть дополнительные материалы по этому вопросу, пожалуйста, сообщите мне.

...