In my python app, my users have a choice between specifying either:
I have created a nested argument_group
inside a mutually_exclusive_group
:
argument_group
+--- mutually_exclusive_group
+--- date (argument)
+--- argument_group
+--- start (argument)
+--- end (argument)
I want to indicate to my users that either date
or the group (start, end)
is required, and that if they choose the group, both (start, end
) are required:
This is what I have so far:
ap = argparse.ArgumentParser()
grp = ap.add_argument_group('test dates')
date_group = grp.add_mutually_exclusive_group(required=True)
date_group.add_argument('--date', help='date (YYYYMMDD)')
date_range = date_group.add_argument_group('date range')
date_range.add_argument('--start',help='start date (YYYYMMDD)', required=True)
date_range.add_argument('--end', help='end date (YYYYMMDD)', required=True)
Problem 1:
Specifying (start, end)
doesn't work, as argparse is (I guess) unable to see that I've specified the argument_group
part of the mutually_exclusive_group
instead of date
, and since the mutually_exclusive_group is required, it barfs:
$ ./app.py --start 20180101 --end 20180102
usage: app.py [-h] --date date --start date --end date
app.py: error: one of the arguments --date is required
I can work around this issue by making the mutually_exclusive_group not required, but this is obviously incorrect, and won't catch the case where nothing is specified (as it is, in fact, required)
Problem 2:
Unfortunately the help for the above does not reflect my intention:
There is no (option_1 | option_2)
around the options in the mutually_exclusive_group
which there would be if I didn't have a nested group.
In addition, the help for start
and end
isn't even shown.
$ ./app.py --help
usage: app.py [-h] --date date --start date --end date
optional arguments:
-h, --help show this help message and exit
test dates:
--date date date (YYYYMMDD) (default: None)
What I would like is something such as:
$ ./app.py --help
usage: app.py [-h] (--date date | --start date --end date)
optional arguments:
-h, --help show this help message and exit
test dates:
--date date date (YYYYMMDD) (default: None)
--start date start date (YYYYMMDD) (default: None)
--end date end date (YYYYMMDD) (default: None)
How can I express this required choice between an argument and an argument group?
Given your comment that start == end
if --date
is given, I'd keep things simple and stick with just the two arguments:
class DefaultEnd(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values)
setattr(namespace, 'end', values)
ap = argparse.ArgumentParser()
ap.add_argument('--start', help='date (YYYYMMDD) Mandatory', action=DefaultEnd, required=True)
ap.add_argument('--end', help='date (YYYYMMDD) Optional, defaulted to START if not provided')
Using the custom argparse.Action
it also defaults --end
to --start
if only the latter was provided:
optional arguments:
-h, --help show this help message and exit
--start START date (YYYYMMDD) Mandatory
--end END date (YYYYMMDD) Optional, defaulted to START if not provided
So if the params were given:
args = ap.parse_args(['--start','20100102', '--end', '20121123'])
# args.start = 20100102
# args.end = 20121123
If not:
args = ap.parse_args(['--start','20100102'])
# args.start = 20100102
# args.end = 20100102
You can of course, also keep things simple if you just manipulate args
after parsing and skip the custom argparse.Action
altogether:
ap.add_argument('--end', help='date (YYYYMMDD) Optional, defaulted to START if not provided', default=None)
...
args.end = args.end if args.end else args.start
Of course this might change how your post-args codes are being handled, so these solutions might not apply for you if your code must require args.date
.
I don't know how you can express the choice between an argument and an argument group, but a simple solution to your problem would be to use the following:
ap = argparse.ArgumentParser()
grp = ap.add_argument_group('test dates')
date_group = grp.add_mutually_exclusive_group(required=True)
date_group.add_argument('--date', help='date (YYYYMMDD)')
date_range = date_group.add_argument('--date-range',
nargs=2, help='Start and End')
Just specify date-range as single argument with two parameters.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With