argparse и взаимоисключающие группы, каждая со своими собственными необходимыми настройками - PullRequest
0 голосов
/ 16 октября 2018

У меня есть программа, которая должна иметь возможность либо протестировать список идентификаторов серверов. ИЛИ выдать команду на сервер.Это значит, что если я выдаю --test, то больше ничего не требуется.Он запускает всю гамму тестов на каждом сервере и печатает результаты.

Однако, если я НЕ УКАЗУЮ --test, тогда потребуется несколько параметров, таких как --id и --command.

Однако я не уверен, что argparse может обрабатывать обязательные опции во взаимоисключающих группах.Код (модифицированный для простоты) выглядит следующим образом.Я изменил параметры, поэтому, если вы укажете -a, тогда вам НЕОБХОДИМО пройти, и никакие другие параметры не понадобятся.

import argparse

parser = argparse.ArgumentParser()

test_or_not = parser.add_mutually_exclusive_group(required=True)
test_or_not.add_argument('-a', action='store_true')
or_not = test_or_not.add_argument_group()
target = or_not.add_mutually_exclusive_group(required=True)
target.add_argument('-b',action="store_true")
target.add_argument('-c',action="store_true")
target.add_argument('-d',action="store_true")
target.add_argument('-e',action="store_true")
group = or_not.add_mutually_exclusive_group(required=True)
group.add_argument('-f',action="store_true")
group.add_argument('-g',action="store_true")
or_not.add_argument('-i',action="store_true")
or_not.add_argument('-j',action="store_true")
or_not.add_argument('-k',action="store_true")
or_not.add_argument('-l',action="store_true")

args = parser.parse_args()

Полученная ошибка вызвана тем, что argparse по-прежнему требует отдельноговарианты, даже если они находятся во взаимоисключающей группе.Есть ли способ, которым argparse может приспособиться к этому набору опций, или мне нужно добавить немного программирования помимо argparse?

$ python3 ~/tmp/groups.py -a
usage: groups.py [-h] -a (-b | -c | -d | -e) (-f | -g) [-i] [-j] [-k] [-l]
groups.py: error: one of the arguments -b -c -d -e is required

Редактировать: я мог бы добавить новую опцию, которая полностью работаетВНЕ argparse, как показано ниже, но я бы хотел, чтобы это структурировалось в пределах argparse, если это вообще возможно.

import argparse
import sys

if '--test' in sys.argv:
    go_do_testing()
    sys.exit(0)

parser = argparse.ArgumentParser()
<snip>

1 Ответ

0 голосов
/ 18 октября 2018

Как предложено в комментариях, если вы хотите использовать взаимоисключающие логики test и run, лучше всего использовать подпарсеры.Следующее является иллюстрацией идеи:

#!/usr/bin/env python3
"""
Script to test or run commands on given servers.
./the_script.py test  # To test all servers
./the_script.py run --id 127.0.0.1 --command "echo hello world"
"""
from argparse import ArgumentParser, RawDescriptionHelpFormatter as RDHF


def test_servers(servers):
    """
    Given a list of servers, let's test them!
    """
    for server in servers:
        print('Just tested server {s}'.format(s=server))

def do_actual_work(server_id, command):
    """
    Given a server ID and a command, let's run the command on that server!
    """
    print('Connected to server {s}'.format(s=server_id))
    print('Ran command {c} successfully'.format(c=command))


if __name__ == '__main__':
    parser = ArgumentParser(description=__doc__, formatter_class=RDHF)
    subs = parser.add_subparsers()
    subs.required = True
    subs.dest = 'run or test'
    test_parser = subs.add_parser('test', help='Test all servers')
    test_parser.set_defaults(func=test_servers)
    run_parser = subs.add_parser('run', help='Run a command on the given server')
    run_parser.add_argument('-i', '--id',
                            help='The ID of the server to connect to and run commands',
                            required=True)
    run_parser.add_argument('-c', '--command',
                            help='The command to run',
                            required=True)
    run_parser.set_defaults(func=do_actual_work)
    args = parser.parse_args()

    if args.func.__name__ == 'test_servers':
        all_servers = ['127.0.0.1', '127.0.0.2']
        test_servers(all_servers)
    else:
        do_actual_work(args.id, args.command)

Сценарий устанавливает как взаимоисключающие, так и требуемые подпарсеры test и run.Для подпарсера test больше ничего не требуется.Однако для подпапки run потребуются и --id, и --command.Каждый из этих подпарасеров связан со своей назначенной целевой функцией.Для простоты я привязал test_parser к test_servers;в то время как run_parser связан с do_actual_work.

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

./the_script.py test

Чтобы выполнить определенную команду надля конкретного сервера вы вызываете скрипт следующим образом:

./the_script.py run --id 127 --command "echo hello world"

Надеюсь, это окажется полезным.

...