Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Argparse conditionally required arguments

I have done as much research as possible but I haven't found the best way to make certain cmdline arguments necessary only under certain conditions, in this case only if other arguments have been given. Here's what I want to do at a very basic level:

p = argparse.ArgumentParser(description='...') p.add_argument('--argument', required=False) p.add_argument('-a', required=False) # only required if --argument is given p.add_argument('-b', required=False) # only required if --argument is given 

From what I have seen, other people seem to just add their own check at the end:

if args.argument and (args.a is None or args.b is None):     # raise argparse error here 

Is there a way to do this natively within the argparse package?

like image 894
DJMcCarthy12 Avatar asked Sep 02 '14 14:09

DJMcCarthy12


2 Answers

I've been searching for a simple answer to this kind of question for some time. All you need to do is check if '--argument' is in sys.argv, so basically for your code sample you could just do:

import argparse import sys  if __name__ == '__main__':     p = argparse.ArgumentParser(description='...')     p.add_argument('--argument', required=False)     p.add_argument('-a', required='--argument' in sys.argv) #only required if --argument is given     p.add_argument('-b', required='--argument' in sys.argv) #only required if --argument is given     args = p.parse_args() 

This way required receives either True or False depending on whether the user as used --argument. Already tested it, seems to work and guarantees that -a and -b have an independent behavior between each other.

like image 62
Mira Avatar answered Sep 20 '22 14:09

Mira


You can implement a check by providing a custom action for --argument, which will take an additional keyword argument to specify which other action(s) should become required if --argument is used.

import argparse  class CondAction(argparse.Action):     def __init__(self, option_strings, dest, nargs=None, **kwargs):         x = kwargs.pop('to_be_required', [])         super(CondAction, self).__init__(option_strings, dest, **kwargs)         self.make_required = x      def __call__(self, parser, namespace, values, option_string=None):         for x in self.make_required:             x.required = True         try:             return super(CondAction, self).__call__(parser, namespace, values, option_string)         except NotImplementedError:             pass  p = argparse.ArgumentParser() x = p.add_argument("--a") p.add_argument("--argument", action=CondAction, to_be_required=[x]) 

The exact definition of CondAction will depend on what, exactly, --argument should do. But, for example, if --argument is a regular, take-one-argument-and-save-it type of action, then just inheriting from argparse._StoreAction should be sufficient.

In the example parser, we save a reference to the --a option inside the --argument option, and when --argument is seen on the command line, it sets the required flag on --a to True. Once all the options are processed, argparse verifies that any option marked as required has been set.

like image 32
chepner Avatar answered Sep 16 '22 14:09

chepner