argparse参数依赖

时间:2014-02-19 11:51:04

标签: python python-3.x 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())

3 个答案:

答案 0 :(得分:9)

在您的情况下,由于选项必须始终一起指定,或者不能同时指定,您可以使用--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之类的内容,这可能是您想要允许的内容,也可能不是。

答案 1 :(得分:4)

定义包含用户名和密码的自定义用户类型。

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')

答案 2 :(得分:2)

感谢您的回答。我会接受Bakuriu的那个。这是一个解决方案(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