Python argparse: create timedelta object from argument?

I am trying to use argparse to convert an argument to a timedelta object. My program reads in strings provided by the user and converts them into various datetime objects for later use. I cannot get the filter_length argument to filter_length correctly. My code is:

 import datetime import time import argparse def mkdate(datestring): return datetime.datetime.strptime(datestring, '%Y-%m-%d').date() def mktime(timestring): return datetime.datetime.strptime(timestring, '%I:%M%p').time() def mkdelta(deltatuple): return datetime.timedelta(deltatuple) parser = argparse.ArgumentParser() parser.add_argument('start_date', type=mkdate, nargs=1) parser.add_argument('start_time', type=mktime, nargs=1, ) parser.add_argument('filter_length', type=mkdelta, nargs=1, default=datetime.timedelta(1))#default filter length is 1 day. 

I run the program by passing 1 as the value of timedelta (I want it to be only one day):

 > python program.py 2012-09-16 11:00am 1 

But I get the following error:

 >>> program.py: error: argument filter_length: invalid mkdelta value: '1' 

I do not understand why the value is not valid. If I call the mkdelta function myself, for example:

 mkdelta(1) print mkdelta(1) 

It returns:

 datetime.timedelta(1) 1 day, 0:00:00 

This is exactly the meaning I'm looking for. Can someone help me figure out how to do this conversion correctly using argparse ?

+4
source share
4 answers

Note the quotation marks around '1' in the error message? You pass a string to mkdelta, while in your test code you pass an integer.

+6
source

Your function does not process the string argument, and that is what argparse passes to it; call int() on it:

 def mkdelta(deltatuple): return datetime.timedelta(int(deltatuple)) 

If you need to support more than a few days, you will need to find a way to parse the argument passed in timedelta arguments.

You can, for example, support d , h , m or s postfixes to indicate days, hours, minutes or seconds:

 _units = dict(d=60*60*24, h=60*60, m=60, s=1) def mkdelta(deltavalue): seconds = 0 defaultunit = unit = _units['d'] # default to days value = '' for ch in list(str(deltavalue).strip()): if ch.isdigit(): value += ch continue if ch in _units: unit = _units[ch] if value: seconds += unit * int(value) value = '' unit = defaultunit continue if ch in ' \t': # skip whitespace continue raise ValueError('Invalid time delta: %s' % deltavalue) if value: seconds = unit * int(value) return datetime.timedelta(seconds=seconds) 

Now your mkdelta method accepts more complete deltas and even integers:

 >>> mkdelta('1d') datetime.timedelta(1) >>> mkdelta('10s') datetime.timedelta(0, 10) >>> mkdelta('5d 10h 3m 10s') datetime.timedelta(5, 36190) >>> mkdelta(5) datetime.timedelta(5) >>> mkdelta('1') datetime.timedelta(1) 

The default unit is days.

+4
source

You can use your own action to collect all other arguments and timedelta them in timedelta .

This will allow you to write CLI commands, such as

 % test.py 2012-09-16 11:00am 2 3 4 5 datetime.timedelta(2, 3, 5004) # args.filter_length 

You can also provide optional arguments for --days , --seconds , etc., so that you can write CLI commands such as

 % test.py 2012-09-16 11:00am --weeks 6 --days 0 datetime.timedelta(42) # args.filter_length % test.py 2012-09-16 11:00am --weeks 6.5 --days 0 datetime.timedelta(45, 43200) 

 import datetime as dt import argparse def mkdate(datestring): return dt.datetime.strptime(datestring, '%Y-%m-%d').date() def mktime(timestring): return dt.datetime.strptime(timestring, '%I:%M%p').time() class TimeDeltaAction(argparse.Action): def __call__(self, parser, args, values, option_string = None): # print '{n} {v} {o}'.format(n = args, v = values, o = option_string) setattr(args, self.dest, dt.timedelta(*map(float, values))) parser = argparse.ArgumentParser() parser.add_argument('start_date', type = mkdate) parser.add_argument('start_time', type = mktime) parser.add_argument('--days', type = float, default = 1) parser.add_argument('--seconds', type = float, default = 0) parser.add_argument('--microseconds', type = float, default = 0) parser.add_argument('--milliseconds', type = float, default = 0) parser.add_argument('--minutes', type = float, default = 0) parser.add_argument('--hours', type = float, default = 0) parser.add_argument('--weeks', type = float, default = 0) parser.add_argument('filter_length', nargs = '*', action = TimeDeltaAction) args = parser.parse_args() if not args.filter_length: args.filter_length = dt.timedelta( args.days, args.seconds, args.microseconds, args.milliseconds, args.minutes, args.hours, args.weeks) print(repr(args.filter_length)) 
+2
source

This problem seems to solve your problem: https://gist.github.com/jnothman/4057689

0
source

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


All Articles