argparse: отчеты об использовании для отдельных вариантов позиционного аргумента? - PullRequest
4 голосов
/ 11 марта 2012

Я пытаюсь реорганизовать программу для использования нового стандартного модуля argparse.Учитывая структуру ниже, я должен использовать выборы для основного позиционного аргумента, подкоманды или какой-либо другой метод?

foo [OPTION]... COMMAND [PACKAGE(S)]...

Commands:
    available -  show packages available to be installed
    install   -  download and install packages, including dependencies
    remove    -  uninstall packages
    ...snip
    version   -  display installed version of package

Options:
    -d,--download          download only
    -i,--ini=FILE          use alternate setup.ini
    ...snip

Пример выбора, краткий и понятный, но никакой помощи по использованию для каждой команды:

parser = argparse.ArgumentParser()
commands = "available install remove ... version".split()
parser.add_argument('command', choices=commands, 
    help="generic help for all 12 commands goes here")

Выбор логически больше соответствует программе, чем то, что следует после команды накомандная строка в значительной степени одинакова для всех из них.В то время как подкоманды, кажется, хотят, чтобы я воспринимал каждую команду как мир в основном для себя и включает в себя 3 раза код.Однако, похоже, нет способа отобразить использование для каждого варианта выбора (?) Возможно, есть третий маршрут, который я не вижу?

Я использую Python 2.7.

1 Ответ

3 голосов
/ 11 марта 2012

Я бы пошел с опцией подкоманд .

Вам не обязательно нужно больше кодировать: создавать классы команд, каждый из которых имеет свою собственную настройку (используя наследование, где это необходимо), в вашем Main классе Cli создать экземпляр основного синтаксического анализатора, а затем передать его командам, которые будут заботиться добавить / установить собственный подпарсер .

Ответ короткий, но если неясно, скажите мне, и я добавлю пример кода.


Грубый пример

Вы должны строить свои команды как классы:

class Install(BaseCommand):

    help = "download and install packages"

    @classmethod
    def interface(cls, cmd_parser):
        cmd_parser.add_argument('--foo')
        cmd_parser.set_defaults(cmd=cls)    # this line is very important

    def start(self, foo=None):
        # command execution

И ваш интерфейс командной строки тоже должен быть классом:

class Main(BaseCli):

    def __init__(self):
        self.commands = [Install]   # just the command classes

        self._parser = argparse.ArgumentParser()
        self._subparsers = self._parser.add_subparsers()

    def load_interface(self):
        for cmd in self.commands:
            cmd_parser = self.add_command_parser(help=cmd.help)
            cmd.interface(cmd_parser)

    def add_command_parser(self, *args, **kwargs):
        return self._subparsers.add_parser(*args, **kwargs)

    def parse_args(self, args=None, namespace=None):
        return self._parser.parse_args(args, namespace)

    def start_session(self, namespace):
        # this will instantiate the appropriate command class
        cmd = namespace.cmd()
        # and call its start method with the user args
        cmd.start(**namespace.__dict__)

Для использования следующим образом:

cli = Main()
cli.load_interface()
args = cli.parse_args()
cli.start_session(args)

Обратите внимание, что при таком подходе у вас будет вся сила наследования на вашей стороне! :)

...