I'm looking for a Pythonic way to validate arguments when their validation logically depends on the value(s) parsed from other argument(s).
Here's a simple example:
parser.add_argument(
'--animal',
choices=['raccoon', 'giraffe', 'snake'],
default='raccoon',
)
parser.add_argument(
'--with-shoes',
action='store_true',
)
In this case, parsing this command should cause an error:
my_script.py --animal snake --with-shoes
Adding a mutually exclusive group doesn't seem to help here, as the other combos are OK:
my_script.py --animal raccoon --with-shoes
my_script.py --animal raccoon
my_script.py --animal snake
my_script.py --animal giraffe --with-shoes
my_script.py --animal giraffe
The validation error should ideally not be tied to --animal
argument nor to --with-shoes
argument, since the interface can not tell you which value needs to change here. Each value is valid, yet they can't be used in combination.
We can do this with post-processing the args
namespace, but I'm looking for a solution which would cause the parser.parse_args()
call to fail, i.e. we actually fail during argument parsing, not afterwards.
Checking values after parsing is simplest. You can even use
parser.error('...')
to produce an error message in the standardargparse
format.argparse
handles each argument independently, and tries to do so in a way that doesn't care about the order (except forpositionals
). Each input value is added to theargs
namespace by the correspondingAction
object (its__call__
method). A defaultstore
action simply usessetattr(args, dest, value)
; astore_true
doessetattr(args, dest, True)
.Mutually exclusive groups are handled by keeping a
set
ofseen_actions
, and checking that against the group's own list of Actions. I've explored generalizing the groups to allow for other logical combinations of actions. As complicated as that has gotten (especially when displaying theusage
), I didn't envisage testing for values as well as occurrence.It would be possible to write custom
Action
classes, that check for co-occurrence, but that gets more complicated.We could give
with-shoes
an Action class that checks the values of theargs.animal
attribute, and raises an error if that value issnake
. But what if the user provides thewith-shoes
option first? We'd have to giveanimal
a custom class that checks theargs.with_shoes
value, and raise an error if that isTrue
, etc. Soshoes
has to know aboutanimals
andanimals
has to know aboutshoes
. Testing after parsing allows you to put the logic in one place.One of the big advantages to using a parser like
argparse
is it generates the usage, help, and error messages for you. But validation logic like this is hard to express automatically. As it is, the usage formatting for the relatively simple mutually-exclusive logic is brittle and easily broken.An earlier attempt at answering this kind of question:
Parameter dependencies in Python - can't make it work