2014-02-19 3 views
7

Если я вызываю ниже сценарий с этими параметрами:argparse зависимость аргументов

--user u1 --password p1 --foo f1 --user u2 --user u3 --password p3 

Тогда он будет печатать:

Namespace(foo=['bar', 'f1'], password=['p1', 'p3'], user=['u1', 'u2', 'u3']) 

Вопрос: Есть ли способ для меня, чтобы установить зависимость между пользователем и пароль, поэтому он выдает ошибку, потому что пароль для пользователя u2 не указан?

Меньше актуальный вопрос: Как указать значение foo по умолчанию для всех пользователей? С данным вводом я хотел бы foo равным ['f1', 'bar', 'bar'].

Решение для моего основного вопроса состояло бы в том, чтобы проверить, что пользователь и пароль списков имеют одинаковую длину, но это не совсем то, что я ищу.

Вот сценарий:

import argparse 
parser = argparse.ArgumentParser() 
group = parser.add_argument_group('authentication') 
group.add_argument('--user', action='append', required=True) 
group.add_argument('--password', action='append', required=True) 
group.add_argument('--foo', action='append', default=['bar']) 
print(parser.parse_args()) 
+0

Typing пароли в командной строке может быть угрозой безопасности. Например, они могут быть украдены кем-то, кто работает под управлением «ps». – unutbu

+1

Спасибо @unutbu. «Реальные» аргументы на самом деле не являются именами пользователей и паролями. Я абсолютно ничего не знаю о безопасности. –

+0

@unutbu, можете ли вы объяснить, что такое риск безопасности и как ps используется злонамеренно? Как, кстати, я ничего не знаю о безопасности. – RyanDay

ответ

8

В вашем случае, поскольку параметры должны всегда быть указаны вместе, или ни один из них, вы не могли бы присоединиться к ним в уникальный --user-and-password вариант с двумя аргументами, используя nargs=2. Это упростило бы обработку значений.

Фактически вы хотите иметь несколько пар, но required=True удовлетворен, когда найден первый параметр, поэтому он практически бесполезен для проверки того, что вы хотите в настройках.

Другой способ сделать это - использовать пользовательское действие. Например:

import argparse 


class UserAction(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     if len(namespace.passwords) < len(namespace.users): 
      parser.error('Missing password') 
     else: 
      namespace.users.append(values) 


class PasswordAction(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     if len(namespace.users) <= len(namespace.passwords): 
      parser.error('Missing user') 
     else: 
      namespace.passwords.append(values) 


parser = argparse.ArgumentParser() 
parser.add_argument('--password', dest='passwords', default=[], action=PasswordAction, required=True) 
parser.add_argument('--user', dest='users', default=[], action=UserAction, required=True) 

print(parser.parse_args()) 

Используется как:

$python3 ./test_argparse.py --user 1 --password 2 --password 2 --user 3 --password 3 
usage: test_argparse.py [-h] --password PASSWORDS --user USERS 
test_argparse.py: error: Missing user 

И:

$python3 ./test_argparse.py --user 1 --password 2 --user 2 --user 3 --password 3 
usage: test_argparse.py [-h] --password PASSWORDS --user USERS 
test_argparse.py: error: Missing password 

(Обратите внимание, что это решение требует --user придти до --password, иначе длины списков не предоставить достаточную информацию, чтобы понять, когда отсутствует опция.)

Последним решением было бы просто использовать action='append' и проверить в конце списки значений. Однако это позволит использовать такие вещи, как --user A --user B --password A --password B, которые могут быть или не быть чем-то, что вы хотите разрешить.

+0

Пойду для вашей простой опции -user-and-password, так как все аргументы/ключевые слова используются для каждого пользователя в моем случае. Но это было бы здорово, если бы аргументная зависимость была добавлена ​​в argparse. Благодарю. –

2

Определить пользовательский тип пользователя, который держит как имя пользователя и пароль.

def user(s): 
    try: 
     username, password = s.split() 
    except: 
     raise argparse.ArgumentTypeError('user must be (username, password)') 

group.add_argument('--user', type=user, action='append') 
+0

Отличное и простое решение. –

+3

Это потребует от пользователя использования кавычек: 'python script.py --user 'name passwd''. В противном случае оболочка разделит эти 2 слова на отдельные строки. – hpaulj

2

Благодарим за ответы. Я соглашусь с Бакуриу. Вот одно решение (test_argparse2.py):

import argparse 
parser = argparse.ArgumentParser() 
parser.add_argument('--upf', action='append', nargs=3) 
print(parser.parse_args()) 

Правильное использование:

$python3 ./test_argparse2.py --upf u1 p1 bar1 --upf u2 p2 bar2 
Namespace(upf=[['u1', 'p1', 'bar1'], ['u2', 'p2', 'bar2']]) 

Вот еще одно решение (test_argparse3.р), что позволяет произвольный порядок входных аргументов:

import argparse 
import sys 
parser = argparse.ArgumentParser() 
parser.add_argument('--upf', nargs='+') 
set_required = set(['user','pass','foo',]) 
for s in parser.parse_args().upf: 
    set_present = set(argval.split(':')[0] for argval in s.split(',')) 
    set_miss = set_required-set_present 
    bool_error = False 
    if len(set_miss)>0: 
     print(set_miss, 'missing for', s) 
     bool_error = True 
    if bool_error: 
     sys.exit() 

Неправильное использование:

$python3 ./test_argparse3.py --upf user:u1,pass:p1,foo:bar1 foo:bar,pass:p2 
{'user'} missing for foo:bar,pass:p2 
Смежные вопросы