Argparse: Required Argument 'y' If 'x' Is Present
Solution 1:
No, there isn't any option in argparse to make mutually inclusive sets of options.
The simplest way to deal with this would be:
if args.prox and (args.lport isNoneor args.rport isNone):
parser.error("--prox requires --lport and --rport.")
Solution 2:
You're talking about having conditionally required arguments. Like @borntyping said you could check for the error and do parser.error()
, or you could just apply a requirement related to --prox
when you add a new argument.
A simple solution for your example could be:
non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', required='--prox'in sys.argv, type=int)
non_int.add_argument('--rport', required='--prox'in sys.argv, type=int)
This way required
receives either True
or False
depending on whether the user as used --prox
. This also guarantees that -lport
and -rport
have an independent behavior between each other.
Solution 3:
How about using parser.parse_known_args()
method and then adding the --lport
and --rport
args as required args if --prox
is present.
# just add --prox arg now
non_int = argparse.ArgumentParser(description="stackoverflow question",
usage="%(prog)s [-h] [--prox --lport port --rport port]")
non_int.add_argument('--prox', action='store_true',
help='Flag to turn on proxy, requires additional args lport and rport')
opts, rem_args = non_int.parse_known_args()
if opts.prox:
non_int.add_argument('--lport', required=True, type=int, help='Listen Port.')
non_int.add_argument('--rport', required=True, type=int, help='Proxy port.')
# use options and namespace from first parsing
non_int.parse_args(rem_args, namespace = opts)
Also keep in mind that you can supply the namespace opts
generated after the first parsing while parsing the remaining arguments the second time. That way, in the the end, after all the parsing is done, you'll have a single namespace with all the options.
Drawbacks:
- If
--prox
is not present the other two dependent options aren't even present in the namespace. Although based on your use-case, if--prox
is not present, what happens to the other options is irrelevant. - Need to modify usage message as parser doesn't know full structure
--lport
and--rport
don't show up in help message
Solution 4:
Do you use lport
when prox
is not set. If not, why not make lport
and rport
arguments of prox
? e.g.
parser.add_argument('--prox', nargs=2, type=int, help='Prox: listen and proxy ports')
That saves your users typing. It is just as easy to test if args.prox is not None:
as if args.prox:
.
Solution 5:
The accepted answer worked great for me! Since all code is broken without tests here is how I tested the accepted answer. parser.error()
does not raise an argparse.ArgumentError
error it instead exits the process. You have to test for SystemExit
.
with pytest
import pytest
from . import parse_arguments # code that rasises parse.error()deftest_args_parsed_raises_error():
with pytest.raises(SystemExit):
parse_arguments(["argument that raises error"])
with unittests
from unittest import TestCase
from . import parse_arguments # code that rasises parse.error()classTestArgs(TestCase):
deftest_args_parsed_raises_error():
with self.assertRaises(SystemExit) as cm:
parse_arguments(["argument that raises error"])
inspired from: Using unittest to test argparse - exit errors
Post a Comment for "Argparse: Required Argument 'y' If 'x' Is Present"