Example #1
0
 def do_crondtc(self, author, channel, serv, message):
     """Cron feature for DTC printing, max 1 minute per loop with no end
     start : lanch cron for DTC, if is not
     stop : cancel cron for DTC, if is start
     next : print time for next cron DTC loop
     update <expression> : change cron DTC frequency, if <expression> is valid"""
     items = message.split()
     if channel in self._cronDTC:
         if message == 'start':
             if self._cronDTC[channel].is_alive():
                 serv.privmsg(channel, 'cronDTC : already start !')
                 self.__prompt('host', '{%alert%}cronDTC : already start !{%default%}')
             else:
                 serv.privmsg(channel, 'cronDTC : start !')
                 self.__prompt('host', '{%alert%}cronDTC : start !{%default%}')
         elif message == 'stop':
             if self._cronDTC[channel].is_alive():
                 serv.privmsg(channel, 'cronDTC : stopped !')
                 self.__prompt('host', '{%alert%}cronDTC : stopped !{%default%}')
             else:
                 serv.privmsg(channel, 'cronDTC : already stopped !')
                 self.__prompt('host', '{%alert%}cronDTC : already stopped !{%default%}')
         elif items[0] == 'update':
             items.pop(0)
             if len(items) > 5:
                 serv.privmsg(channel, 'cronDTC : update refuse ! (possible abuse frequency)')
                 self.__prompt('host', '{%alert%}cronDTC : update refuse ! (possible abuse frequency){%default%}')
             elif croniter.is_valid(' '.join(items[:5])):
                 self._cronDTC[channel].expanded, self._cronDTC[channel].nth_weekday_of_month = croniter.expand(' '.join(items[:5]))
             else:
                 serv.privmsg(channel, 'cronDTC : update refuse !')
                 self.__prompt('host', '{%alert%}cronDTC : update refuse !{%default%}')
         elif message == 'next':
             if self._cronDTC[channel].is_alive():
                 _next = delai(datetime.fromtimestamp(self._cronDTC[channel].get_current()))
                 serv.privmsg(channel, 'cronDTC : next in {next} !'.format(next = _next))
                 self.__prompt('host', '{%alert%}cronDTC : next in {next} !{%default%}', next = _next)
         else:
             return False
     else:
         if not message:
             self._cronDTC[channel] = cron(target = self.do_dtc, expr_format = '0 * * * *', args = (self._name, channel, serv, ''))
         elif croniter.is_valid(' '.join(items[:5])):
             self._cronDTC[channel] = cron(target = self.do_dtc, expr_format = ' '.join(items[:5]), args = (self._name, channel, serv, ''))
         else:
             return False
         self._cronDTC[channel].start()
         serv.privmsg(channel, 'cronDTC : start !')
         self.__prompt('host', '{%alert%}cronDTC : start !{%default%}')
     return True
Example #2
0
def schedule_execution_time_iterator(
        start_timestamp: float, cron_schedule: str,
        execution_timezone: Optional[str]) -> Iterator[datetime.datetime]:
    timezone_str = execution_timezone if execution_timezone else "UTC"

    utc_datetime = pytz.utc.localize(
        datetime.datetime.utcfromtimestamp(start_timestamp))
    start_datetime = utc_datetime.astimezone(pytz.timezone(timezone_str))

    date_iter = croniter(cron_schedule, start_datetime)

    # Go back one iteration so that the next iteration is the first time that is >= start_datetime
    # and matches the cron schedule
    next_date = date_iter.get_prev(datetime.datetime)

    check.invariant(is_valid_cron_string(cron_schedule))

    cron_parts, _ = croniter.expand(cron_schedule)

    is_numeric = [len(part) == 1 and part[0] != "*" for part in cron_parts]
    is_wildcard = [len(part) == 1 and part[0] == "*" for part in cron_parts]

    delta_fn = None
    should_hour_change = False

    # Special-case common intervals (hourly/daily/weekly/monthly) since croniter iteration can be
    # much slower than adding a fixed interval
    if all(is_numeric[0:3]) and all(is_wildcard[3:]):  # monthly
        delta_fn = lambda d, num: d.add(months=num)
        should_hour_change = False
    elif all(is_numeric[0:2]) and is_numeric[4] and all(
            is_wildcard[2:4]):  # weekly
        delta_fn = lambda d, num: d.add(weeks=num)
        should_hour_change = False
    elif all(is_numeric[0:2]) and all(is_wildcard[2:]):  # daily
        delta_fn = lambda d, num: d.add(days=num)
        should_hour_change = False
    elif is_numeric[0] and all(is_wildcard[1:]):  # hourly
        delta_fn = lambda d, num: d.add(hours=num)
        should_hour_change = True
    else:
        delta_fn = None
        should_hour_change = False

    if delta_fn:
        # Use pendulums for intervals when possible
        next_date = to_timezone(pendulum.instance(next_date), timezone_str)
        while True:
            curr_hour = next_date.hour

            next_date_cand = delta_fn(next_date, 1)
            new_hour = next_date_cand.hour

            if not should_hour_change and new_hour != curr_hour:
                # If the hour changes during a daily/weekly/monthly schedule, it
                # indicates that the time shifted due to falling in a time that doesn't
                # exist due to a DST transition (for example, 2:30AM CST on 3/10/2019).
                # Instead, execute at the first time that does exist (the start of the hour),
                # but return to the original hour for all subsequent executions so that the
                # hour doesn't stay different permanently.

                check.invariant(new_hour == curr_hour + 1)
                yield next_date_cand.replace(minute=0)

                next_date_cand = delta_fn(next_date, 2)
                check.invariant(next_date_cand.hour == curr_hour)

            next_date = next_date_cand
            yield next_date
    else:
        # Otherwise fall back to croniter
        while True:
            next_date = to_timezone(
                pendulum.instance(date_iter.get_next(datetime.datetime)),
                timezone_str)

            yield next_date
Example #3
0
 def expand(cls, expr_format, *args, **kwargs):
     if len(expr_format.split()) == 6:
         raise CroniterBadCronError(
             "Expected 'min hour day mon dow'")
     return croniter.expand(expr_format, *args, **kwargs)
Example #4
0
def is_valid_cron_string(cron_string: str) -> bool:
    if not croniter.is_valid(cron_string):
        return False
    expanded, _ = croniter.expand(cron_string)
    # dagster only recognizes cron strings that resolve to 5 parts (e.g. not seconds resolution)
    return len(expanded) == 5