web-dev-qa-db-ja.com

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())
14

あなたの場合、オプションは常に一緒に指定するか、指定しない必要があるため、--user-and-passwordを使用して2つの引数を使用して一意のnargs=2オプションに結合できます。これにより、値の処理が大幅に簡素化されます。

実際、複数のペアを提供できるようにしたいのですが、最初のオプションが見つかったときにrequired=Trueが満たされるため、設定で必要なものを確認するのにはほとんど役に立ちません。

これを行うもう1つの方法は、カスタムアクションを使用することです。例えば:

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のようなものが許可されますが、これは許可したいものである場合とそうでない場合があります。

12
Bakuriu

ユーザー名とパスワードの両方を保持するカスタムユーザータイプを定義します。

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')
3
Jayanth Koushik

回答ありがとうございます。バクリウからのものを受け入れます。これが1つの解決策(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.py)を次に示します。

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
2