Python Argparse conditionally required arguments

后端 未结 6 540
梦谈多话
梦谈多话 2020-11-28 09:35

I have done as much research as possible but I haven\'t found the best way to make certain cmdline arguments necessary only under certain conditions, in this case only if ot

6条回答
  •  醉梦人生
    2020-11-28 10:21

    Your post parsing test is fine, especially if testing for defaults with is None suits your needs.

    http://bugs.python.org/issue11588 'Add "necessarily inclusive" groups to argparse' looks into implementing tests like this using the groups mechanism (a generalization of mutuall_exclusive_groups).

    I've written a set of UsageGroups that implement tests like xor (mutually exclusive), and, or, and not. I thought those where comprehensive, but I haven't been able to express your case in terms of those operations. (looks like I need nand - not and, see below)

    This script uses a custom Test class, that essentially implements your post-parsing test. seen_actions is a list of Actions that the parse has seen.

    class Test(argparse.UsageGroup):
        def _add_test(self):
            self.usage = '(if --argument then -a and -b are required)'
            def testfn(parser, seen_actions, *vargs, **kwargs):
                "custom error"
                actions = self._group_actions
                if actions[0] in seen_actions:
                    if actions[1] not in seen_actions or actions[2] not in seen_actions:
                        msg = '%s - 2nd and 3rd required with 1st'
                        self.raise_error(parser, msg)
                return True
            self.testfn = testfn
            self.dest = 'Test'
    p = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)
    g1 = p.add_usage_group(kind=Test)
    g1.add_argument('--argument')
    g1.add_argument('-a')
    g1.add_argument('-b')
    print(p.parse_args())
    

    Sample output is:

    1646:~/mypy/argdev/usage_groups$ python3 issue25626109.py --arg=1 -a1
    usage: issue25626109.py [-h] [--argument ARGUMENT] [-a A] [-b B]
                            (if --argument then -a and -b are required)
    issue25626109.py: error: group Test: argument, a, b - 2nd and 3rd required with 1st
    

    usage and error messages still need work. And it doesn't do anything that post-parsing test can't.


    Your test raises an error if (argument & (!a or !b)). Conversely, what is allowed is !(argument & (!a or !b)) = !(argument & !(a and b)). By adding a nand test to my UsageGroup classes, I can implement your case as:

    p = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)
    g1 = p.add_usage_group(kind='nand', dest='nand1')
    arg = g1.add_argument('--arg', metavar='C')
    g11 = g1.add_usage_group(kind='nand', dest='nand2')
    g11.add_argument('-a')
    g11.add_argument('-b')
    

    The usage is (using !() to mark a 'nand' test):

    usage: issue25626109.py [-h] !(--arg C & !(-a A & -b B))
    

    I think this is the shortest and clearest way of expressing this problem using general purpose usage groups.


    In my tests, inputs that parse successfully are:

    ''
    '-a1'
    '-a1 -b2'
    '--arg=3 -a1 -b2'
    

    Ones that are supposed to raise errors are:

    '--arg=3'
    '--arg=3 -a1'
    '--arg=3 -b2'
    

提交回复
热议问题