Package buildbot :: Package util :: Module croniter
[frames] | no frames]

Source Code for Module buildbot.util.croniter

  1  # Copied from croniter 
  2  # https://github.com/taichino/croniter 
  3  # Licensed under MIT license 
  4  # Pyflakes warnings corrected 
  5   
  6  #!/usr/bin/python 
  7  # -*- coding: utf-8 -*- 
  8   
  9  import re 
 10  from time import time, mktime 
 11  from datetime import datetime 
 12  from dateutil.relativedelta import relativedelta 
 13   
 14  search_re = re.compile(r'^([^-]+)-([^-/]+)(/(.*))?$') 
 15  only_int_re = re.compile(r'^\d+$') 
 16  any_int_re = re.compile(r'^\d+') 
 17  star_or_int_re = re.compile(r'^(\d+|\*)$') 
 18   
 19  __all__ = ('croniter',) 
 20   
 21   
22 -class croniter(object):
23 RANGES = ( 24 (0, 59), 25 (0, 23), 26 (1, 31), 27 (1, 12), 28 (0, 6), 29 (0, 59) 30 ) 31 DAYS = ( 32 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 33 ) 34 35 ALPHACONV = ( 36 { }, 37 { }, 38 { }, 39 { 'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6, 40 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12 }, 41 { 'sun':0, 'mon':1, 'tue':2, 'wed':3, 'thu':4, 'fri':5, 'sat':0 }, 42 { } 43 ) 44 45 LOWMAP = ( 46 {}, 47 {}, 48 {0: 1}, 49 {0: 1}, 50 {7: 0}, 51 {}, 52 ) 53 54 bad_length = 'Exactly 5 or 6 columns has to be specified for iterator' \ 55 'expression.' 56
57 - def __init__(self, expr_format, start_time=time()):
58 if isinstance(start_time, datetime): 59 start_time = mktime(start_time.timetuple()) 60 61 self.cur = start_time 62 self.exprs = expr_format.split() 63 64 if len(self.exprs) != 5 and len(self.exprs) != 6: 65 raise ValueError(self.bad_length) 66 67 expanded = [] 68 69 for i, expr in enumerate(self.exprs): 70 e_list = expr.split(',') 71 res = [] 72 73 while len(e_list) > 0: 74 e = e_list.pop() 75 t = re.sub(r'^\*(/.+)$', r'%d-%d\1' % (self.RANGES[i][0], 76 self.RANGES[i][1]), 77 str(e)) 78 m = search_re.search(t) 79 80 if m: 81 (low, high, step) = m.group(1), m.group(2), m.group(4) or 1 82 83 if not any_int_re.search(low): 84 low = self.ALPHACONV[i][low.lower()] 85 86 if not any_int_re.search(high): 87 high = self.ALPHACONV[i][high.lower()] 88 89 if (not low or not high or int(low) > int(high) 90 or not only_int_re.search(str(step))): 91 raise ValueError("[%s] is not acceptable" %expr_format) 92 93 for j in xrange(int(low), int(high)+1): 94 if j % int(step) == 0: 95 e_list.append(j) 96 else: 97 if not star_or_int_re.search(t): 98 t = self.ALPHACONV[i][t.lower()] 99 100 try: 101 t = int(t) 102 except: 103 pass 104 105 if t in self.LOWMAP[i]: 106 t = self.LOWMAP[i][t] 107 108 if t != '*' and (int(t) < self.RANGES[i][0] or 109 int(t) > self.RANGES[i][1]): 110 raise ValueError("[%s] is not acceptable, out of range" % expr_format) 111 112 res.append(t) 113 114 res.sort() 115 expanded.append(['*'] if (len(res) == 1 and res[0] == '*') else res) 116 self.expanded = expanded
117
118 - def get_next(self, ret_type=float):
119 return self._get_next(ret_type, is_prev=False)
120
121 - def get_prev(self, ret_type=float):
122 return self._get_next(ret_type, is_prev=True)
123
124 - def _get_next(self, ret_type=float, is_prev=False):
125 expanded = self.expanded[:] 126 127 if ret_type not in (float, datetime): 128 raise TypeError("Invalid ret_type, only 'float' or 'datetime' " \ 129 "is acceptable.") 130 131 if expanded[2][0] != '*' and expanded[4][0] != '*': 132 bak = expanded[4] 133 expanded[4] = ['*'] 134 t1 = self._calc(self.cur, expanded, is_prev) 135 expanded[4] = bak 136 expanded[2] = ['*'] 137 138 t2 = self._calc(self.cur, expanded, is_prev) 139 if not is_prev: 140 result = t1 if t1 < t2 else t2 141 else: 142 result = t1 if t1 > t2 else t2 143 else: 144 result = self._calc(self.cur, expanded, is_prev) 145 self.cur = result 146 147 if ret_type == datetime: 148 result = datetime.fromtimestamp(result) 149 return result
150
151 - def _calc(self, now, expanded, is_prev):
152 if is_prev: 153 nearest_diff_method = self._get_prev_nearest_diff 154 sign = -1 155 else: 156 nearest_diff_method = self._get_next_nearest_diff 157 sign = 1 158 159 offset = len(expanded) == 6 and 1 or 60 160 dst = now = datetime.fromtimestamp(now + sign * offset) 161 162 day, month, year = dst.day, dst.month, dst.year 163 current_year = now.year 164 DAYS = self.DAYS 165 166 def proc_month(d): 167 if expanded[3][0] != '*': 168 diff_month = nearest_diff_method(d.month, expanded[3], 12) 169 days = DAYS[month - 1] 170 if month == 2 and self.is_leap(year) == True: 171 days += 1 172 173 reset_day = days if is_prev else 1 174 175 if diff_month != None and diff_month != 0: 176 if is_prev: 177 d += relativedelta(months=diff_month) 178 else: 179 d += relativedelta(months=diff_month, day=reset_day, 180 hour=0, minute=0, second=0) 181 return True, d 182 return False, d
183 184 def proc_day_of_month(d): 185 if expanded[2][0] != '*': 186 days = DAYS[month - 1] 187 if month == 2 and self.is_leap(year) == True: 188 days += 1 189 190 diff_day = nearest_diff_method(d.day, expanded[2], days) 191 192 if diff_day != None and diff_day != 0: 193 if is_prev: 194 d += relativedelta(days=diff_day) 195 else: 196 d += relativedelta(days=diff_day, hour=0, minute=0, second=0) 197 return True, d 198 return False, d
199 200 def proc_day_of_week(d): 201 if expanded[4][0] != '*': 202 diff_day_of_week = nearest_diff_method(d.isoweekday() % 7, expanded[4], 7) 203 if diff_day_of_week != None and diff_day_of_week != 0: 204 if is_prev: 205 d += relativedelta(days=diff_day_of_week) 206 else: 207 d += relativedelta(days=diff_day_of_week, hour=0, minute=0, second=0) 208 return True, d 209 return False, d 210 211 def proc_hour(d): 212 if expanded[1][0] != '*': 213 diff_hour = nearest_diff_method(d.hour, expanded[1], 24) 214 if diff_hour != None and diff_hour != 0: 215 if is_prev: 216 d += relativedelta(hours = diff_hour) 217 else: 218 d += relativedelta(hours = diff_hour, minute=0, second=0) 219 return True, d 220 return False, d 221 222 def proc_minute(d): 223 if expanded[0][0] != '*': 224 diff_min = nearest_diff_method(d.minute, expanded[0], 60) 225 if diff_min != None and diff_min != 0: 226 if is_prev: 227 d += relativedelta(minutes = diff_min) 228 else: 229 d += relativedelta(minutes = diff_min, second=0) 230 return True, d 231 return False, d 232 233 def proc_second(d): 234 if len(expanded) == 6: 235 if expanded[5][0] != '*': 236 diff_sec = nearest_diff_method(d.second, expanded[5], 60) 237 if diff_sec != None and diff_sec != 0: 238 d += relativedelta(seconds = diff_sec) 239 return True, d 240 else: 241 d += relativedelta(second = 0) 242 return False, d 243 244 if is_prev: 245 procs = [proc_second, 246 proc_minute, 247 proc_hour, 248 proc_day_of_week, 249 proc_day_of_month, 250 proc_month] 251 else: 252 procs = [proc_month, 253 proc_day_of_month, 254 proc_day_of_week, 255 proc_hour, 256 proc_minute, 257 proc_second] 258 259 while abs(year - current_year) <= 1: 260 next = False 261 for proc in procs: 262 (changed, dst) = proc(dst) 263 if changed: 264 next = True 265 break 266 if next: 267 continue 268 return mktime(dst.timetuple()) 269 270 raise "failed to find prev date" 271
272 - def _get_next_nearest(self, x, to_check):
273 small = [item for item in to_check if item < x] 274 large = [item for item in to_check if item >= x] 275 large.extend(small) 276 return large[0]
277
278 - def _get_prev_nearest(self, x, to_check):
279 small = [item for item in to_check if item <= x] 280 large = [item for item in to_check if item > x] 281 small.reverse() 282 large.reverse() 283 small.extend(large) 284 return small[0]
285
286 - def _get_next_nearest_diff(self, x, to_check, range_val):
287 for i, d in enumerate(to_check): 288 if d >= x: 289 return d - x 290 return to_check[0] - x + range_val
291
292 - def _get_prev_nearest_diff(self, x, to_check, range_val):
293 candidates = to_check[:] 294 candidates.reverse() 295 for d in candidates: 296 if d <= x: 297 return d - x 298 return (candidates[0]) - x - range_val
299
300 - def is_leap(self, year):
301 if year % 400 == 0 or (year % 4 == 0 and year % 100 != 0): 302 return True 303 else: 304 return False
305 306 if __name__ == '__main__': 307 308 base = datetime(2010, 1, 25) 309 itr = croniter('0 0 1 * *', base) 310 n1 = itr.get_next(datetime) 311 print n1 312