Python argparse : mutually exclusive arguments with optional and positional argument

匿名 (未验证) 提交于 2019-12-03 02:33:02

问题:

I would like to get this with argparse library :

PROG --yesterday | begin-date [end-date] 

I tried to combine mutual exclusion and argument groups but I didn't succeed.

This program should only accept that :

PROG --yesterday PROG 2015-11-12 PROG 2015-11-12 2015-11-15 

Is it possible to do this with argparse ?


Thanks hpaulj. See the final result :

import argparse from datetime import datetime import pytz   def argument_date(str_date):     try:         return datetime.strptime(str_date, "%Y-%m-%d").replace(tzinfo=pytz.utc)     except ValueError as e:         raise argparse.ArgumentTypeError(e)  parser = argparse.ArgumentParser(prog='PROG') parser.usage = """PROG [-h] [--yesterday | start [end]]""" parser.add_argument('start', type=argument_date, nargs='?', help='Start date (format YYYY-MM-DD)') parser.add_argument('end', type=argument_date, nargs='?', help='End date (format YYYY-MM-DD)') parser.add_argument('--yesterday', action='store_true', help='Only yesterday')  args = parser.parse_args()  if args.yesterday and args.start:     raise parser.error("--yesterday option is not incompatible with start argument")  if not args.yesterday and not args.start:     raise parser.error("--yesterday option or start argument should be filled")  if args.end and (args.start >= args.end):     raise parser.error("end argument should be granter than start") 

回答1:

Your best choice is to test values after parsing, and if needed, provide your own custom usage.

A mutually_exclusive_group can work with one optional positional, e.g.

group = parser.add_mutually_exclusive_group() group.add_argument('-y','--yesterday', action='store_true') group.add_argument('dates',nargs='?') 

I was thinking it would work with nargs='*', but I get ValueError: mutually exclusive arguments must be optional error.

So one optional positional value works, but there's no way of using this test with 2 optional positional values.

parser.add_argument('--yesterday',action='store_true') parser.add_argument('start',nargs='?') parser.add_argument('end',nargs='?') 

And then test for args.yesterday, args.start is None and args.end is None. If some combination of those is wrong, then raise parser.error('....').

As long as you can distinguish between the default values and the user given ones, testing after parsing is just as good as anything that you might force the parser to do.

It is also a good idea to think about what usage message makes sense to your users. e.g.

For example:

PROG  [--yesterday | [start [end]]] 

is not something that argparse can generate automatically.



回答2:

--yesterday is redundant, since it is just a shortcut for setting start_date to yesterday's day. Instead, let "yesterday" be an allowable value for start_date. In fact, you can generalize datetime to allow other abbreviations, for either argument, as desired. For example:

def argument_date(str_date):     # Not the most efficient to roundtrip like this, but     # fits well with your existing code     now = datetime.datetime.utcnow().date()     if str_date == "yesterday":         str_date = str(now - datetime.timedelta(1))     elif str_date == "today"         str_date = str(now)      try:         return datetime.strptime(str_date, "%Y-%m-%d").replace(tzinfo=pytz.utc)     except ValueError as e:         raise argparse.ArgumentTypeError(e) 

Once you've done this, your code simply becomes:

parser = argparse.ArgumentParser(prog='PROG') parser.add_argument('start', type=argument_date, help='Start date (YYYY-MM-DD, yesterday, today)') parser.add_argument('end', type=argument_date, nargs='?', help='End date (YYYY-MM-DD, yesterday, today)') 


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!