Argparse conditional argument structure

I want to use argparse and have it so that the number of arguments provided determines which routine is executed.

For example, in the script below, I would like argparse to be able to do the following:

dmsconvert.py 12.5 >>> (12, 30, 0) dmsconvert.py 12 30 0.0 >>> 12.5 

Instead, the only way I was able to do with argparse is to have an explicit option, i.e.:

 dmsconvert.py 12.5 >>> (12, 30, 0) dmsconvert.py -a 12 30 0.0 >>> 12.5 

Can anyone suggest a way to achieve my preferred approach using argparse? Note. I want the text of the generated generated argument to look reasonable.

Full code example:

 import argparse import sys def dms_to_decimal(deg,min,sec): assert float(min) < 60.0, 'Mintue value: %s must be less than 60' % float(min) assert float(sec) < 60.0, 'Second value: %s must be less than 60' % float(sec) return float(deg)+float(min)/60.0+float(sec)/(60.0*60.0) def decimal_to_dms(deg): min = 60.0*(deg-int(deg)) sec = 60.0*(min-int(min)) return int(deg),int(min),sec parser = argparse.ArgumentParser(description = 'Convert decimal degrees to dms and visa versa') parser.add_argument('-a',dest='dms_args',nargs=3) parser.add_argument(dest='dec_arg',type=float,nargs='?') args = vars(parser.parse_args(sys.argv[1:])) if args['dms_args'] is not None: print dms_to_decimal(*args['dms_args']) if args['dec_arg'] is not None: print decimal_to_dms(args['dec_arg']) 
+4
source share
1 answer

This can be done using a special action:

 import argparse class OneOrThree(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): if len(values) == 1: setattr(namespace, self.dest, values) setattr(namespace, 'result', decimal_to_dms(*values)) elif len(values) == 3: setattr(namespace, self.dest, values) setattr(namespace, 'result', dms_to_decimal(*values)) else: raise parser.error('Expected 1 or 3 arguments') def dms_to_decimal(deg,minute,sec): assert float(minute) < 60.0, 'Mintue value: %s must be less than 60' % float(minute) assert float(sec) < 60.0, 'Second value: %s must be less than 60' % float(sec) return float(deg)+float(minute)/60.0+float(sec)/(60.0*60.0) def decimal_to_dms(deg): minute = 60.0*(deg-int(deg)) sec = 60.0*(minute-int(minute)) return int(deg),int(minute),sec parser = argparse.ArgumentParser( usage='%(prog)s deg [minute, sec]',) parser.add_argument('dms_args', nargs='+', type=float, action=OneOrThree, metavar='deg [minute, sec]', help='Given one arg, returns dms. Given three args, returns deg') args = parser.parse_args() print(args.result) 

Running above leads to the following:

 % test.py 12.5 (12, 30, 0.0) % test.py 12 30 0.0 12.5 

If the wrong number of arguments is specified, an error occurs:

 % test.py 12 30 usage: test.py [-h] dms_args [dms_args ...] test.py: error: Expected 1 or 3 arguments 

The help message is as follows:

 % test.py -h usage: test.py deg [minute, sec] positional arguments: deg [minute, sec] Given one arg, returns dms. Given three args, returns deg optional arguments: -h, --help show this help message and exit 
+1
source

Source: https://habr.com/ru/post/1495865/


All Articles