argparse argument dependency

前端 未结 3 1553
無奈伤痛
無奈伤痛 2021-01-01 20:22

If I call the script below with these options:

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

Then it will print:

相关标签:
3条回答
  • 2021-01-01 20:34

    In your case, since the options must always be specified together, or none of them, you could join them into a unique --user-and-password option with two arguments using nargs=2. This would simplify a lot the handling of the values.

    In fact you want to be able to provide multiple pairs, but required=True is satisfied when the first option is found, so it's pretty much useless for checking what you want in your setting.

    The other way to do this is to use a custom action. For example:

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

    Used as:

    $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
    

    And:

    $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
    

    (Note that this solution requires --user to come before --password, otherwise the lengths of the lists don't provide enough information to understand when an option is missing.)

    The last solution would be to simply use action='append' and test at the end the lists of values. However this would allow things like --user A --user B --password A --password B which may or may not be something you want to allow.

    0 讨论(0)
  • 2021-01-01 20:39

    Define a custom user type which holds both username and password.

    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 讨论(0)
  • 2021-01-01 20:55

    Thank you for the answers. I will accept the one from Bakuriu. Here is one solution (test_argparse2.py):

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

    Correct usage:

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

    Here is another solution (test_argparse3.py) allowing random order of the input arguments:

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

    Incorrect usage:

    $python3 ./test_argparse3.py --upf user:u1,pass:p1,foo:bar1 foo:bar,pass:p2
    {'user'} missing for foo:bar,pass:p2
    
    0 讨论(0)
提交回复
热议问题