开发者

How to code argparse combinational options in python

I have been troubled with this small piece of activity to be completed. I did do some experiment, but was not able to achieve the result.

Requirement:

   test2.py [-c/-v] -f

Usage or Rules:

  1. -c (compare) takes 2 parameter.

    -v (verify) takes 1 parameter.

    Either of these two must be present, but not both.

  2. -f is a mandatory开发者_StackOverflow中文版 parameter (output file name).

Output:

I am able to get the desired output as shown below

kp@kp:~/Study/scripts$ ./test.py -c P1 P2 -f p
kp@kp:~/Study/scripts$ ./test.py -v P1 -f p
kp@kp:~/Study/scripts$ ./test.py -v P1 
usage: test.py <functional argument> <ouput target argument>
test.py: error: argument -f/--file is required
kp@kp:~/Study/scripts$ ./test.py -c P1 P2 
usage: test.py <functional argument> <ouput target argument>
test.py: error: argument -f/--file is required
kp@kp:~/Study/scripts$ 

Problem is:

When you use, test.py -h,

1. The output will not indicate that -c/-v either of them is mandatory but not both . It indicates all the arguments are optional.

2. The output will indicate -f option under optional arguments which is incorrect. -f is mandatory argument, and I want to display outside - optional arguments.

How to change the script so that -h option output will be more user friendly (without any external validation)

usage: test.py <functional argument> <ouput target argument>

Package Compare/Verifier tool.

optional arguments:
  -h, --help            show this help message and exit
  -f outFileName, --file outFileName
                        File Name where result is stored.
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.
kiran@kiran-laptop:~/Study/scripts$ 

Code:

I am using the below code to achieve the output,

#!/usr/bin/python

import sys
import argparse

def main():
    usage='%(prog)s <functional argument> <ouput target argument>'
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(usage=usage,description=description)

    parser.add_argument('-f','--file',action='store',nargs=1,dest='outFileName',help='File Name where result is stored.',metavar="outFileName",required=True)


    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-c','--compare',action='store',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',action='store',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')
    args = parser.parse_args()

if __name__ == "__main__":
    main()


Set the filename to be a positional argument, and let argparse set its own usage message:

$ python so.py --help
usage: so.py [-h] [-c Package1 Package2 | -v Package] outFileName

The filename should be positional, and you should let argparse write its own usage message.

Code

#!/usr/bin/python

import sys
import argparse

def main():
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(description=description,
                                     epilog='--compare and --verify are mutually exclusive')

    parser.add_argument('f',action='store',nargs=1,
                        help='File Name where result is stored.',
                        metavar="outFileName")

    group = parser.add_mutually_exclusive_group(required=False)
    group.add_argument('-c','--compare',action='store',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',action='store',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')

    args = parser.parse_args()

if __name__ == "__main__":
    main()

Help message

$ python so.py --help
usage: so.py [-h] [-c Package1 Package2 | -v Package] outFileName

Package Compare/Verifier tool.

positional arguments:
  outFileName           File Name where result is stored.

optional arguments:
  -h, --help            show this help message and exit
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.

--compare and --verify are mutually exclusive


What exact output are you looking for? I am not aware of any standard syntax for denoting mutual exclusitivity in a --help output, and it would likely be confusing for your users if you made one up. Also I assume that argparse doesn't support a syntax for it (since if it did, it would already be working).

I suggest you keep it simple and just explain to your users the mutual exclusion in the help for each of the arguments. So change their help strings as follows:

-c Package1 Package2, --compare Package1 Package2
                      Compare two packages (may not be used with -v).
-v Package, --verify Package
                      Verify Content of package (may not be used with -c).

That is extremely obvious and reasonably concise.

Another alternative would be just to not mention it, and have the user discover that they are mutually exclusive by trying to use them simultaneously (argparse automatically generates a user-friendly error such as "PROG: error: argument -c: not allowed with argument -v").


I think the basic complaint is with the default positional arguments and optional arguements group names. In the help, optional arguments means: requires a flag like -f or --file; positional arguments means it is identified by position. With default values, positionals are indeed required, and optionals are indeed optional (not required). But the user can change that with a required attribute, giving rise to confusing terminology.

A way around this is to define your own argument groups. These groups affect the layout of the help, but have no effect on parsing. They also don't affect the usage line.

def main():
    description='Package Compare/Verifier tool.'
    parser = argparse.ArgumentParser(usage=None,description=description)

    maingroup = parser.add_argument_group(title='required')
    maingroup.add_argument('-f','--file',nargs=1,dest='outFileName',help='File Name where result is stored.',metavar="outFileName",required=True)
    exgroup = parser.add_argument_group(title='one or the other')

    group = exgroup.add_mutually_exclusive_group(required=True)
    group.add_argument('-c','--compare',nargs=2,dest='packageInfo',help='Compare two packages.',metavar=("Package1","Package2"))
    group.add_argument('-v','--verify',nargs=1,dest='packageName',help='Verify Content of package.',metavar='Package')
    args = parser.parse_args()

produces:

usage: stack5603364.py [-h] -f outFileName (-c Package1 Package2 | -v Package)

Package Compare/Verifier tool.

optional arguments:
  -h, --help            show this help message and exit

required:
  -f outFileName, --file outFileName
                        File Name where result is stored.

one or the other:
  -c Package1 Package2, --compare Package1 Package2
                        Compare two packages.
  -v Package, --verify Package
                        Verify Content of package.

The mutually_exclusive_group affects only the usage line.

(-c Package1 Package2 | -v Package)

displays a group where one of the choices is required.

[-c Package1 Package2 | -v Package]

would be an optional group. [] are used to mark optional (in the 'not required' sense) arguments. Note how -h continues to be labeled.

http://bugs.python.org/issue9694 is the related Python issue, where the argparse author favors this argument_group approach.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜