different sets of options

I have a program called program.py, and I am using argparse to do the argument parsing.

I have two modes I want to run this binary with: 1) Simulation, which requires no arguments 2) Non-simulation, which requires many arguments

I want the program to either accept

python program --simulation

or

python program arg1 arg2 arg3 arg4

Where all of the 'args' are required.

The only way I thought to do this is to add 'required = False' to all fields and check the logic manually, but I was wondering if there is a more elegant way.

Here's a cut down version of the code I have

def get_args():
    parser = argparse.ArgumentParser(description = "program")
    parser.add_argument("arg1", type = bool)
    parser.add_argument("arg2" ,type = str)
    parser.add_argument("arg3", type = int)
    parser.add_argument("arg4", type = str)
    parser.add_argument("--simulation")
    args = parser.parse_args()
    return args

argparse cannot be that clever. However, in your simple case, you could "help" it to choose the correct options to parse:

def get_args(args=sys.argv[1:]):
    parser = argparse.ArgumentParser(description = "program")
    if args and args[0].startswith("--"):
        parser.add_argument("--simulation")
    else:
        parser.add_argument("arg1", type = bool)
        parser.add_argument("arg2" ,type = str)
        parser.add_argument("arg3", type = int)
        parser.add_argument("arg4", type = str)
    args = parser.parse_args(args=args)
    return args

so print(get_args("--simulation xx".split())) yields:

Namespace(simulation='xx')

because first argument starts with -- . Any other options fails command line parsing as expected.

and print(get_args("True foo 3 bar".split())) yields:

Namespace(arg1=True, arg2='foo', arg3=3, arg4='bar')

forgetting one of the 4 positional parameters fails command line parsing as expected.

BTW, I have added a default parameter, which if is omitted reads from system arguments (like it did in your code). Else you can read from a text file and pass the args tokens. So it's easier to test, and to create modules that can be called with parameters from other modules without the need to hack through sys.argv .


This is clearly an awkward specification for argparse , and I suspect most other POSIX style parsers.

Scanning sys.argv and adjusting the parser definition is one possible way.

Another is to use a 2 stage parser, with parse_known_args :

import argparse
usage = 'prog [-h] [--simulation] [arg1 arg2 arg3 arg4]'
parser1 = argparse.ArgumentParser(usage=usage)
parser1.add_argument('--simulation', action='store_true')
# or omit the `store_true` if it just takes one argument
# other possible optionals

parser2 = argparse.ArgumentParser()
#parser2.add_argument("arg1", type = bool)  # not a valid type parameter
parser2.add_argument("arg2" )
parser2.add_argument("arg3", type = int)
parser2.add_argument("arg4")
# positionals are required, unless nargs=? or *

args, extras = parser1.parse_known_args()
if not args.simulation:
    args = parser2.parse_args(extras, namespace=args)
elif extras:
    parser1.error('cannot use --simulation with args')
print(args)

Possible runs include:

1526:~/mypy$ python stack41556997.py -h
usage: prog [-h] [--simulation] [arg1 arg2 arg3 arg4]

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

1526:~/mypy$ python stack41556997.py --simulation
Namespace(simulation=True)

1527:~/mypy$ python stack41556997.py 1 2 3
Namespace(arg2='1', arg3=2, arg4='3', simulation=False)

1527:~/mypy$ python stack41556997.py 1 2 3 --sim
usage: prog [-h] [--simulation] [arg1 arg2 arg3 arg4]
stack41556997.py: error: cannot use --simulation with args

Note that the help does not include both sets. I included some information in the custom usage, but there are not help lines for the arg# . Generating a good help message is going to be awkward with your specification.

I skipped your arg1 . type=bool is not a valid type argument. See my explanation at Parsing boolean values with argparse

I changed --simulation to store_true since you said it didn't take any arguments. That's the normal way of accepting True/False.

Subparsers is often the best tool for accepting different patterns of arguments. In this case you could have one subparser called 'simulate' that doesn't require any arguments, and another call 'somethingelse' that requires the 4 positionals.

I was going to suggest a mutually_exclusive_group with --simulation and --other optionals. But a store_true argument does not work in such a group.

=============

The subparser route:

parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='cmd')
sp.add_parser('simulate')
parser2 = sp.add_parser('other')
parser2.add_argument("arg2" )
parser2.add_argument("arg3", type = int)
parser2.add_argument("arg4")
print(parser.parse_args())

testing:

1552:~/mypy$ python stack41556997.py -h
usage: stack41556997.py [-h] {simulate,other} ...

positional arguments:
  {simulate,other}

optional arguments:
  -h, --help        show this help message and exit
1557:~/mypy$ python stack41556997.py simulate
Namespace(cmd='simulate')
1557:~/mypy$ python stack41556997.py other -h
usage: stack41556997.py other [-h] arg2 arg3 arg4

positional arguments:
  arg2
  arg3
  arg4

optional arguments:
  -h, --help  show this help message and exit
1557:~/mypy$ python stack41556997.py other 1 2 3
Namespace(arg2='1', arg3=2, arg4='3', cmd='other')

Note that the arg3 type converted the input to an integer. The others are left as strings. With this set up args.cmd will be the name of the subparser, not quite the same as a boolean args.simulation attribute.

==================

A flagged argument is not-required by default. Positional arguments are required unless the nargs value is '?' or '*'. You can't provide a 'required' parameter to a positional.

链接地址: http://www.djcxy.com/p/61972.html

上一篇: 需要对此经常性问题ANR keyDispatchingTimedOut有所了解

下一篇: 不同的选项集