In my python script, I want to be able to use an optional input parameter only when another optional parameter has been specified. Example:
$ python
You can use parse_known_args() to check if --parameter1
was given before adding --parameter2
to the parser.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--parameter1', dest='p1')
args = parser.parse_known_args()[0]
if args.p1:
parser.add_argument('--parameter2', dest='p2')
args = parser.parse_args()
if args.p1:
print args.p1
if args.p2:
print args.p2
Results:
$ python myScript.py --parameter1 a
a
$ python myScript.py --parameter1 a --parameter2 b
a
b
$ python myScript.py --parameter2 b
usage: myScript.py [-h] [--parameter1 P1]
myScript.py: error: unrecognized arguments: --parameter2 b
Use a custom action:
import argparse
foo_default=None
class BarAction(argparse.Action):
def __call__(self,parser,namespace,values,option_string=None):
didfoo=getattr(namespace,'foo',foo_default)
if(didfoo == foo_default):
parser.error( "foo before bar!")
else:
setattr(namespace,self.dest,values)
parser=argparse.ArgumentParser()
parser.add_argument('--foo',default=foo_default)
parser.add_argument('--bar',action=BarAction,help="Only use this if --foo is set")
#testing.
print parser.parse_args('--foo baz'.split())
print parser.parse_args('--foo baz --bar cat'.split())
print parser.parse_args('--bar dog'.split())
This can even be done in a little easier to maintain way if you're OK with relying on some undocumented behavior of argparse:
import argparse
parser=argparse.ArgumentParser()
first_action=parser.add_argument('--foo',dest='cat',default=None)
class BarAction(argparse.Action):
def __call__(self,parser,namespace,values,option_string=None):
didfoo=getattr(namespace,first_action.dest,first_action.default)
if(didfoo == first_action.default):
parser.error( "foo before bar!")
else:
setattr(namespace,self.dest,values)
parser.add_argument('--bar',action=BarAction,
help="Only use this if --foo is set")
#testing.
print parser.parse_args('--foo baz'.split())
print parser.parse_args('--foo baz --bar cat'.split())
print parser.parse_args('--bar dog'.split())
In this example, we get the default for foo
and it's destination from the action object returned by add_argument
(add_argument's return value isn't documented anywhere that I can find). This is still a little fragile (If you want to specify a type=
keyword to the --foo
argument for example).
Finally, you can check sys.argv
before parsing.
import sys
if ("--parameter2" in sys.argv) and ("--parameter1" not in sys.argv):
parser.error("parameter1 must be given if parameter2 is given")
This gets a little more tricky if --parameter1
could also be triggered by --p1
, but you get the idea. Then you could use
if (set(sys.argv).intersection(('--p2',...)) and
not set(sys.argv).intersection(('--p1',...)))
The advantage here is that it doesn't require any particular order. (--p2
doesn't need to follow --p1
on the commandline). And, as before, you can get the list of command strings that will trigger your particular action via the option_strings
attribute returned by parser.add_argument(...)
. e.g.
import argparse
import sys
parser=argparse.ArgumentParser()
action1=parser.add_argument('--foo')
action2=parser.add_argument('--bar',
help="Only use this if --foo is set")
argv=set(sys.argv)
if (( argv & set(action2.option_strings) ) and
not ( argv & set(action1.option_strings) )):
#^ set intersection
parser.error(' or '.join(action1.option_strings)+
' must be given with '+
' or '.join(action2.option_strings))
One idea would be to manually check the output of calling parser.parse_args()
and raise an exception or otherwise fail if --parameter2
is used but --parameter1
isn't.
You could also try setting a custom action (scroll down a bit) to --parameter2
check if --parameter1
exists inside the namespace.
Finally, you could try using subparsers, although I don't know if it'll let you specify a value for --parameter1
.