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.
上一篇: 需要对此经常性问题ANR keyDispatchingTimedOut有所了解
下一篇: 不同的选项集