def test_dow_substitution(self): """ Verify that shortened days-of-the-week are correctly translated numeric indexes. """ testex1 = cronex.CronExpression("* * * * sun,mon,tue,wed,thu,fri,sat") testex2 = cronex.CronExpression("* * * * 0,1,2,3,4,5,6") self.assertEqual(repr(testex1), repr(testex2))
def test_vixie_cron_wildcard_flags(self): """ Test the arguments do_wild and do_non_wild, which can be used to implement the Vixie cron local time jump tracking algorithm, where non-wildcard and wildcard job execution behavior is changed intelligently in the presence of DST transitions and most forward and reverse clock skew occurrences. """ # note: normal cron behavior when do_wild=True, do_non_wild=True # other combinations are used during time jump errors # matches when do_wild=True testex1 = cronex.CronExpression("* * * * *") # matches when do_non_wild=True testex2 = cronex.CronExpression("0 1 * * *") # matches testex1 when do_wild=True and do_non_wild=True tuple1 = (2017, 9, 11, 1, 0) # matches testex1 when do_wild=True only and not testex2 tuple2 = (2017, 9, 11, 2, 1) self.assertTrue( testex1.check_trigger(tuple1, do_wild=True, do_non_wild=True)) self.assertTrue( testex1.check_trigger(tuple1, do_wild=True, do_non_wild=False)) self.assertFalse( testex1.check_trigger(tuple1, do_wild=False, do_non_wild=True)) self.assertFalse( testex1.check_trigger(tuple1, do_wild=False, do_non_wild=False)) self.assertTrue( testex2.check_trigger(tuple1, do_wild=True, do_non_wild=True)) self.assertTrue( testex2.check_trigger(tuple1, do_wild=False, do_non_wild=True)) self.assertFalse( testex2.check_trigger(tuple1, do_wild=True, do_non_wild=False)) self.assertFalse( testex2.check_trigger(tuple1, do_wild=False, do_non_wild=False)) self.assertTrue( testex1.check_trigger(tuple2, do_wild=True, do_non_wild=True)) self.assertTrue( testex1.check_trigger(tuple2, do_wild=True, do_non_wild=False)) self.assertFalse( testex1.check_trigger(tuple2, do_wild=False, do_non_wild=True)) self.assertFalse( testex1.check_trigger(tuple2, do_wild=False, do_non_wild=False)) self.assertFalse( testex2.check_trigger(tuple2, do_wild=True, do_non_wild=True)) self.assertFalse( testex2.check_trigger(tuple2, do_wild=False, do_non_wild=True)) self.assertFalse( testex2.check_trigger(tuple2, do_wild=True, do_non_wild=False)) self.assertFalse( testex2.check_trigger(tuple2, do_wild=False, do_non_wild=False))
def test_dom_substitution(self): """ Verify that shortened month names are correctly translated numeric indexes. """ testex1 = cronex.CronExpression( "* * * jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec *") testex2 = cronex.CronExpression("* * * 1,2,3,4,5,6,7,8,9,10,11,12 *") self.assertEqual(repr(testex1), repr(testex2))
def test_compute_numtab(self): testex1 = cronex.CronExpression("*/7 5-10 5 * *") testex2 = cronex.CronExpression("*/5 23-2 5 8 *") self.assertNotEqual(testex1.string_tab, testex2.string_tab) self.assertNotEqual(testex1.numerical_tab, testex2.numerical_tab) testex1.string_tab = testex2.string_tab testex1.compute_numtab() self.assertEqual(testex1.string_tab, testex2.string_tab) self.assertEqual(testex1.numerical_tab, testex2.numerical_tab)
def test_compute_numtab(self): """ Verify that calling compute_numtab after modifying the string-version of an expression results in the static trigger-value cache being updated. """ testex1 = cronex.CronExpression("*/7 5-10 5 * *") testex2 = cronex.CronExpression("*/5 23-2 5 8 *") self.assertNotEqual(testex1.string_tab, testex2.string_tab) self.assertNotEqual(testex1.numerical_tab, testex2.numerical_tab) testex1.string_tab = testex2.string_tab testex1.compute_numtab() self.assertEqual(testex1.string_tab, testex2.string_tab) self.assertEqual(testex1.numerical_tab, testex2.numerical_tab)
def test_dom_either_or_dow(self): testex = cronex.CronExpression("0 0 5 * mon") for e in xrange(1,30): if e in (1,5,8,15,22,29): self.assertTrue(testex.check_trigger((2010,11,e,0,0))) else: self.assertFalse(testex.check_trigger((2010,11,e,0,0))) testex = cronex.CronExpression("0 0 * * wed") for d in xrange(1,32): if not(d % 7): self.assertTrue(testex.check_trigger((2010,7,d,0,0))) else: self.assertFalse(testex.check_trigger((2010,7,d,0,0)))
def clean_cron_expression(self): try: _ = cronex.CronExpression(str( self.cleaned_data["cron_expression"])) except: raise forms.ValidationError("Invalid cron expression.") return self.cleaned_data["cron_expression"]
def cron_check(cron_line, t, execute=True): """ Check the given crontab line for execution at the given time. If triggered, execute the specified command. """ job = cronex.CronExpression(cron_line) # time up to minute precision t = t[:5] #log.debug('check t={0} {1}'.format(t, job)) if not job.check_trigger(t): return False if job.comment.startswith('system:'): if execute: cmd = job.comment[7:] log.info('executing system job {0}'.format(cmd)) for subcmd in cmd.split(','): os.system(subcmd) return True elif job.comment.startswith('iobroker:'): if execute: cmd = job.comment[9:] log.info('executing iobroker job {0}'.format(cmd)) set_iobroker_values(args.iobroker, cmd.split(',')) return True elif job.comment.startswith('python:'): if execute: cmd = job.comment[7:] log.info('executing python job {0}'.format(cmd)) eval(cmd) return True else: log.error('unknown job {0}'.format(job.comment)) return False
def process(self): if self.last_task_queued_tstamp and time.time( ) - self.last_task_queued_tstamp < 60: # finest granularity is one minute return cur_tstamp = int(time.time()) dt = datetime.datetime.fromtimestamp(cur_tstamp) cur_dt_tuple = (dt.year, dt.month, dt.day, dt.hour, dt.minute) cex = cronex.CronExpression(self.cronstring) needs_to_run = cex.check_trigger(cur_dt_tuple) if not needs_to_run and self.last_task_queued_tstamp and cur_tstamp - self.last_task_queued_tstamp < 86400 * 3: # make sure we didn't miss last run if last queuing was less than three days ago s_tstamp = self.last_task_queued_tstamp + 60 while s_tstamp < cur_tstamp: last_qdt = datetime.datetime.fromtimestamp(int(s_tstamp)) last_qdt_tuple = (last_qdt.year, last_qdt.month, last_qdt.day, last_qdt.hour, last_qdt.minute) if cex.check_trigger(last_qdt_tuple): needs_to_run = True break s_tstamp += 60 if needs_to_run: self.create_tasks([str(cur_tstamp)])
def test_periodics_month(self): """ Verify that arbitrary-period repeaters in the month field work as expected. More specifically, it verifies that the repeaters are triggered every "N" calendar months regardless of the day for triggers set to go offer every 1 month, ever 2 months, etc... through 18 months from 1970 through 1980. """ for period in range(2, 18): calendar_months = 0 description = "* * * %%%i *" % period cron_expression = cronex.CronExpression(description) for year in range(1970, 1980): for month in range(1, 13): days_in_month = calendar.monthrange(year, month)[-1] should_trigger = not (calendar_months % period) for day in range(1, days_in_month + 1): time_tuple = (year, month, day, 0, 0) triggered = cron_expression.check_trigger(time_tuple) self.assertEqual( should_trigger, triggered, "%s: expected trigger to be %r for %r" % (description, should_trigger, time_tuple)) calendar_months += 1
def evalaute_script_cron_triggers(tick, interval): second = tick * interval / 1000 for script in session.query(ScriptModel).all(): s = Script(script.uid) if script.enabled: for trigger in script.triggers: if trigger["type"] == "cron": # Evaluate cron expression try: cron = cronex.CronExpression( trigger["options"]["expression"]) time = tuple(list(datetime.now().timetuple())[:5]) if cron.check_trigger(time): # Execute script in seperate thread, such that the loop is not affected dispatch_task( "execute_script", {"script_uid": script.uid}, task_priority="NOW", ) except (ValueError): logger.error("CRON expression yielded error: {}", trigger["options"]["expression"])
def test_dow_occurence(self): """ Verify that using "#" to find the Nth occurrence of a given day of the week works correctly. """ for dow in xrange(0, 7): for occurence in (1, 6): day = (7 * (occurence - 1)) + dow + 1 expression = "0 0 * * %i#%i" % (dow, occurence) if occurence > 5: # There can never be more than 5 occurrences of a given day # of the week in one month. self.failUnlessRaises(ValueError, cronex.CronExpression, expression) elif day < 32: testex = cronex.CronExpression(expression) self.assertTrue(testex.check_trigger((2011, 5, day, 0, 0))) if day > 8: self.assertFalse( testex.check_trigger((2011, 5, max(day - 7, 1), 0, 0))) elif day < 25: self.assertFalse( testex.check_trigger((2011, 5, max(day + 7, 1), 0, 0)))
def test_nearest_weekday(self): """ Verify that using "W" to find the nearest weekday works correctly. """ month = 4 year = 1991 lastdom = calendar.monthrange(year, month)[-1] for day in xrange(1, 31): dow = (datetime.date.weekday(datetime.date(year, month, day)) + 1) % 7 testex = cronex.CronExpression("0 0 %iW * *" % day) if dow == 0 or dow == 6: self.assertFalse(testex.check_trigger( (year, month, day, 0, 0))) at_least_one_of_them = ( testex.check_trigger((year, month, max(day - 1, 1), 0, 0)) or testex.check_trigger( (year, month, max(day - 2, 1), 0, 0)) or testex.check_trigger( (year, month, min(day + 1, lastdom), 0, 0)) or testex.check_trigger( (year, month, min(day + 2, lastdom), 0, 0))) self.assertTrue(at_least_one_of_them) else: self.assertTrue(testex.check_trigger((year, month, day, 0, 0)))
def test_calendar_change_vs_hour_change(self): # epoch and local differ by < 48 hours but it should be reported based # on calendar days, not 24 hour days epoch = (2010, 11, 16, 23, 59) local_time = (2010, 11, 18, 0, 0) testex = cronex.CronExpression("0 0 %2 * *",epoch, -6) self.assertTrue(testex.check_trigger(local_time, -6))
def should_run(self) -> bool: """ Verify if the backup should run based on todays date and the frequency value set. Returns: True if the frequency matches today, False if it does not. """ # Our configuration is just the last 3 values of a cron pattern, prepend hour/minute as wild-cards. cron_frequency = f"* * {self.frequency}" try: job = cronex.CronExpression(cron_frequency) except ValueError as e: logger.error( f"Frequency for remote strategy [{self.name}] is not valid [{self.frequency}]. [{e}]" ) return False if not job.check_trigger(time.gmtime(time.time())[:5]): logger.debug( f"Backup strategy [{self.name}] will not run due to frequency [{self.frequency}] not matching today." ) return False return True
def test_str_and_repr(self): CronExpression = cronex.CronExpression testex1 = cronex.CronExpression("*/15 4 1-7 * * TEST___TEST") testex2 = eval(repr(testex1)) self.assertEqual(testex1.string_tab, testex2.string_tab) self.assertEqual(testex1.numerical_tab, testex2.numerical_tab) self.assertEqual(testex1.comment, testex2.comment) self.assertEqual(repr(testex1), str(testex1))
def checkIfRunIsNeeded(eventinfo): try : job = cronex.CronExpression(eventinfo) if job.check_trigger(time.gmtime(time.time())[:5]): #print(job.comment) return True except : pass
def test_periodics_hours(self): now = int(time.time()) then = time.gmtime(now - 9001 * HOUR) now_tuple = time.gmtime(now) testex = cronex.CronExpression("* %9001 * * *") self.assertFalse(testex.check_trigger(now_tuple[:5])) testex.epoch = tuple(list(then[:5]) + [0]) self.assertTrue(testex.check_trigger(now_tuple[:5])) self.assertTrue(testex.check_trigger(then[:5]))
def test_periodics_minutes(self): now = int(time.time()) then = time.gmtime(now - 814075 * MINUTE) now_tuple = time.gmtime(now) testex = cronex.CronExpression("%814075 * * * *") self.assertFalse(testex.check_trigger(now_tuple[:5])) testex.epoch = tuple(list(then[:5]) + [0]) self.assertTrue(testex.check_trigger(now_tuple[:5])) self.assertTrue(testex.check_trigger(then[:5]))
def loadTasks(self, file_name): result = [] real_file = '{}/{}'.format(self.cfg_dir, file_name) with open(real_file) as fp: data = fp.read().split('\n') data.remove('') for line in data: result.append(cronex.CronExpression(line.strip())) return(result)
def test_L_in_dom(self): testex = cronex.CronExpression("0 0 L * *") import calendar for y in xrange(2000, 2009): for v in xrange(1,13): lastdom = calendar.monthrange(y,v)[-1] for d in xrange(1, lastdom + 1): if d < lastdom: self.assertFalse(testex.check_trigger((y,v,d,0,0))) else: self.assertTrue(testex.check_trigger((y,v,d,0,0)))
def test_substitution(self): testcases = [("@yearly", "0 0 1 1 *"), ("@annually", "0 0 1 1 *"), ("@monthly", "0 0 1 * *"), ("@weekly", "0 0 * * 0"), ("@daily", "0 0 * * *"), ("@midnight", "0 0 * * *"), ("@hourly", "0 * * * *")] for a, b in testcases: obj = cronex.CronExpression(a) self.assertTrue(b in repr(obj))
def test_calendar_change_vs_hour_change(self): """ Verify that a periodic trigger for the day of the month is based on calendar days, not 24-hour days. """ # epoch and local differ by < 48 hours but it should be reported based # on calendar days, not 24 hour days epoch = (2010, 11, 16, 23, 59) local_time = (2010, 11, 18, 0, 0) testex = cronex.CronExpression("0 0 %2 * *", epoch, -6) self.assertTrue(testex.check_trigger(local_time, -6))
def test_substitution(self): """ Verify that the special substitutions are replaced by the correct, expanded cron expressions. """ testcases = [("@yearly", "0 0 1 1 *"), ("@annually", "0 0 1 1 *"), ("@monthly", "0 0 1 * *"), ("@weekly", "0 0 * * 0"), ("@daily", "0 0 * * *"), ("@midnight", "0 0 * * *"), ("@hourly", "0 * * * *")] for a, b in testcases: obj = cronex.CronExpression(a) self.assertTrue(b in repr(obj))
def test_L_in_dow(self): """ Verify that having L in the day-of-the-week field with a number always triggers on last occurrence of the corresponding day of the week in any given month. """ testex = cronex.CronExpression("0 0 * * 6L") # The numbers are the dates of the last Saturday in each month of the # year 2010. tv = [30, 27, 27, 24, 29, 26, 31, 28, 25, 30, 27, 25] for v in xrange(0, 12): self.assertTrue((testex.check_trigger((2010, v + 1, tv[v], 0, 0))))
def test_str_and_repr(self): """ Verify that the __repr__ and __str__ return values can be passed to eval to generate an identical CronExpression. """ CronExpression = cronex.CronExpression testex1 = cronex.CronExpression("*/15 4 1-7 * * TEST___TEST") testex2 = eval(repr(testex1)) self.assertEqual(testex1.string_tab, testex2.string_tab) self.assertEqual(testex1.numerical_tab, testex2.numerical_tab) self.assertEqual(testex1.comment, testex2.comment) self.assertEqual(repr(testex1), str(testex1))
def __loop(cls): cls.sh.log.info("Loop") if cls.running: wait = (int(time.time() / 60) + 1) * 60 - time.time() cls.sh.log.debug("wait: " + str(wait)) cls.timer = Timer(wait, cls.__loop) cls.timer.start() for cron in cls.cron: job = cronex.CronExpression(cron["pattern"]) if job.check_trigger(time.localtime(time.time())[:5]): cls.sh.log.debug("call: " + str(cron)) cron['job']()
def test_periodics_minutes(self): """ Verify that arbitrary-period repeaters in the minutes field work as expected. """ now = int(time.time()) then = time.gmtime(now - 814075 * MINUTE) now_tuple = time.gmtime(now) testex = cronex.CronExpression("%814075 * * * *") self.assertFalse(testex.check_trigger(now_tuple[:5])) testex.epoch = tuple(list(then[:5]) + [0]) self.assertTrue(testex.check_trigger(now_tuple[:5])) self.assertTrue(testex.check_trigger(then[:5]))
def test_periodics_dom(self): """ Verify that arbitrary-period repeaters in the day-of-the-month field work as expected. """ now = int(time.time()) then = time.gmtime(now - 491 * DAY) now_tuple = time.gmtime(now) testex = cronex.CronExpression("* * %491 * *") self.assertFalse(testex.check_trigger(now_tuple[:5])) testex.epoch = tuple(list(then[:5]) + [0]) self.assertTrue(testex.check_trigger(now_tuple[:5])) self.assertTrue(testex.check_trigger(then[:5]))
def test_periodics_month(self): now = int(time.time()) then = time.gmtime(now - 1337 * DAY) now_tuple = time.gmtime(now) curmon = now_tuple[1] thnmon = then[1] if curmon < thnmon: curmon += 12 per = 36 + curmon - thnmon testex = cronex.CronExpression("* * * %%%i *" % per) self.assertFalse(testex.check_trigger(now_tuple[:5])) testex.epoch = tuple(list(then[:5]) + [0]) self.assertTrue(testex.check_trigger(now_tuple[:5])) self.assertTrue(testex.check_trigger(then[:5]))