Python argparse : mutually exclusive arguments with optional and positional argument

前端 未结 2 1077
梦如初夏
梦如初夏 2020-12-11 22:24

I would like to get this with argparse library :

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

I tried to combine mutual exclusion and argument

相关标签:
2条回答
  • 2020-12-11 22:35

    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.

    0 讨论(0)
  • 2020-12-11 22:44

    --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)')
    
    0 讨论(0)
提交回复
热议问题