Ejemplo n.º 1
0
def schedule_every_monday_at(process, str_time, run_at_start=True):
    scheduler1 = Scheduler()
    scheduler1.every().monday.at(str_time).do(process)

    if run_at_start:
        # Run the job now
        scheduler1.run_all()

    while True:
        scheduler1.run_pending()
        time.sleep(1)
Ejemplo n.º 2
0
def test_exception_handling():
    # The normal scheduler will fail when a job raises an exception and the job
    # will be marked as if it was never run.
    normal_scheduler = Scheduler()
    normal_scheduler.every(1).hour.do(_failjob)

    with pytest.raises(Exception) as excinfo:
        normal_scheduler.run_all()
    assert "I will always fail" in str(excinfo)
    assert normal_scheduler.jobs[0].last_run is None
    assert normal_scheduler.jobs[0].next_run > datetime.now()

    # The Safe scheduler can deal with this and just schedules the next
    # execution of this job
    safe_scheduler = SafeScheduler()
    safe_scheduler.every(1).hour.do(_failjob)
    safe_scheduler.run_all()

    assert safe_scheduler.jobs[0].last_run < datetime.now()
    assert safe_scheduler.jobs[0].next_run > datetime.now()
Ejemplo n.º 3
0
class Crontab:
    def __init__(self):
        self.scheduler = Scheduler()
        self.funcs_time_attrs = []
        self.timer_type_map = {
            's': 'seconds',
            'm': 'minutes',
            'h': 'hours',
            'd': 'days',
            'w': 'weeks',
            'mon': 'monday',
            'tue': 'tuesday',
            'wed': 'wednesday',
            'thu': 'thursday',
            'fri': 'friday',
            'sat': 'saturday',
            'sun': 'sunday'
        }

    def every(self, timer_value, timer_type, timer_concrete=None):
        """
        Decorate function change to time function.
        """
        def decorator(func):
            nonlocal timer_value
            nonlocal timer_type

            if isinstance(timer_value, str):
                if '-' not in timer_value:
                    timer_value = int(timer_value)
                else:
                    timer_value = timer_value
            else:
                raise Exception("timer_value should be str!")

            timer_type_list = list(chain(*self.timer_type_map.items()))

            if timer_type in timer_type_list:
                if isinstance(timer_value, int) and timer_concrete is None:
                    func_time_dict = {func: timer_value}
                    if self.timer_type_map.get(timer_type):
                        attr_name = 'funcs_%s_timer' % self.timer_type_map[
                            timer_type]
                    else:
                        attr_name = 'funcs_%s_timer' % timer_type
                elif isinstance(timer_value,
                                int) and timer_concrete is not None:
                    func_time_dict = {func: [timer_value, timer_concrete]}
                    if self.timer_type_map.get(timer_type):
                        attr_name = 'funcs_%s_concrete_timer' % self.timer_type_map[
                            timer_type]
                    else:
                        attr_name = 'funcs_%s_concrete_timer' % timer_type
                elif isinstance(timer_value, str) and timer_concrete is None:
                    func_time_dict = {func: timer_value}
                    if self.timer_type_map.get(timer_type):
                        attr_name = 'funcs_%s_random_timer' % self.timer_type_map[
                            timer_type]
                    else:
                        attr_name = 'funcs_%s_random_timer' % timer_type
                elif isinstance(timer_value,
                                str) and timer_concrete is not None:
                    func_time_dict = {func: [timer_value, timer_concrete]}
                    if self.timer_type_map.get(timer_type):
                        attr_name = 'funcs_%s_random_concrete_timer' % self.timer_type_map[
                            timer_type]
                    else:
                        attr_name = 'funcs_%s_random_concrete_timer' % timer_type
                else:
                    raise Exception('parameter error!')

                if hasattr(self, attr_name):
                    attr_name_func_dict = getattr(self, attr_name)
                    attr_name_func_dict.update(func_time_dict)
                    setattr(self, attr_name, attr_name_func_dict)
                else:
                    setattr(self, attr_name, func_time_dict)
                self.funcs_time_attrs.append(attr_name)
            else:
                raise Exception('timer_type error!')

        return decorator

    def load_time_funcs(self, attr_name):
        """
        Load the time type but not specific exact time function.
        """
        for k, v in zip(
                getattr(self, attr_name).keys(),
                getattr(self, attr_name).values()):
            time_type = attr_name.split('_')[1]
            getattr(self.scheduler.every(v), time_type).do(job_func=k)

    def load_concrete_time_funcs(self, attr_name):
        """
        Load the time type and specific exact time function.
        """
        for k, v in zip(
                getattr(self, attr_name).keys(),
                getattr(self, attr_name).values()):
            time_type = attr_name.split('_')[1]
            getattr(self.scheduler.every(v[0]),
                    time_type).at(v[1]).do(job_func=k)

    def load_random_time_funcs(self, attr_name):
        """
        Load the time range but not specific exact time function.
        """
        for k, v in zip(
                getattr(self, attr_name).keys(),
                getattr(self, attr_name).values()):
            v_list = v.split('-')
            left_value = int(v_list[0])
            right_value = int(v_list[-1])
            time_type = attr_name.split('_')[1]
            getattr(self.scheduler.every(left_value),
                    time_type).to(right_value).do(job_func=k)

    def load_random_concrete_time_funcs(self, attr_name):
        """
        Load the time range and specific exact time function.
        """
        for k, v in zip(
                getattr(self, attr_name).keys(),
                getattr(self, attr_name).values()):
            v_list = v.split('-')
            left_value = int(v_list[0])
            right_value = int(v_list[-1])
            time_type = attr_name.split('_')[1]
            getattr(self.scheduler.every(left_value),
                    time_type).to(right_value).at(v[1]).do(job_func=k)

    def load_scheduler_funcs(self):
        """
        Load all function decorated.
        """
        for attr_name in self.funcs_time_attrs:
            if attr_name.endswith('_random_concrete_timer'):
                self.load_random_concrete_time_funcs(attr_name)
            elif attr_name.endswith('_random_timer'):
                self.load_random_time_funcs(attr_name)
            elif attr_name.endswith('_concrete_timer'):
                self.load_concrete_time_funcs(attr_name)
            elif attr_name.endswith('_timer'):
                self.load_time_funcs(attr_name)

    def run_all_jobs(self):
        """
        Run all function once immediately.
        """
        self.load_scheduler_funcs()
        self.scheduler.run_all()

    def job_start(self):
        """
        Scheduler into pending.
        """
        while True:
            self.scheduler.run_pending()
            time.sleep(self.interval)

    def run(self, interval=0):
        """
        Run all tasks.
        """
        if not isinstance(interval, (int, float)):
            raise TypeError('interval should be int or float.')
        self.interval = interval
        self.load_scheduler_funcs()
        self.job_start()
Ejemplo n.º 4
0
class Polling(AsyncPull, AsyncPullNowMixin):
    """
    Base class for polling plugins.

    You may specify duration literals such as 60 (60 secs), 1m, 1h (...) to realize a periodic
    polling or cron expressions (*/1 * * * * > every min) to realize cron like behaviour.
    """
    __REPR_FIELDS__ = ['interval', 'is_cron']

    def __init__(
        self, interval: Optional[DurationLiteral] = 60, instant_run: bool = False, **kwargs: Any
    ):
        super().__init__(**kwargs)

        self._assert_polling_compat()

        if interval is None:
            # No scheduled execution. Use endpoint `/trigger` of api to execute.
            self._poll_interval = None
            self.interval = None
            self.is_cron = False
        else:
            try:
                # Literals such as 60s, 1m, 1h, ...
                self._poll_interval = parse_duration_literal(interval)
                self.interval = interval
                self.is_cron = False
            except TypeError:
                # ... or a cron-like expression is valid
                from cronex import CronExpression  # type: ignore
                self._cron_interval = CronExpression(interval)
                self.interval = self._cron_interval
                self.is_cron = True

        self._is_running = False
        self._scheduler: Optional[Scheduler] = None
        self._instant_run = try_parse_bool(instant_run, False)

    def _assert_polling_compat(self) -> None:
        self._assert_abstract_compat((SyncPolling, AsyncPolling))
        self._assert_fun_compat('_poll')

    async def _pull(self) -> None:
        def _callback() -> None:
            loop = asyncio.get_event_loop()
            if loop.is_running():
                asyncio.ensure_future(self._run_schedule())

        self._scheduler = Scheduler()
        self._configure_scheduler(self._scheduler, _callback)

        if self._instant_run:
            self._scheduler.run_all()

        while not self.stopped:
            self._scheduler.run_pending()
            await self._sleep(0.5)

        while self._is_running:  # Keep the loop alive until the job is finished
            await asyncio.sleep(0.1)

    async def _pull_now(self) -> None:
        await self._run_now()

    async def _run_now(self) -> Payload:
        """Runs the poll right now. It will not run, if the last poll is still running."""
        if self._is_running:
            self.logger.warning("Polling job is still running. Skipping current run")
            return

        self._is_running = True
        try:
            payload = await self.poll()

            if payload is not None:
                self.notify(payload)

            return payload
        finally:
            self._is_running = False

    async def _run_schedule(self) -> None:
        try:
            if self.is_cron:
                dtime = datetime.now()
                if not self._cron_interval.check_trigger((
                        dtime.year, dtime.month, dtime.day,
                        dtime.hour, dtime.minute
                )):
                    return  # It is not the time for the cron to trigger

            await self._run_now()
        except StopPollingError:
            await self._stop()
        except Exception:  # pragma: no cover, pylint: disable=broad-except
            self.logger.exception("Polling of '%s' failed", self.name)

    def _configure_scheduler(self, scheduler: Scheduler, callback: Callable[[], None]) -> None:
        """
        Configures the scheduler. You have to differ between "normal" intervals and
        cron like expressions by checking `self.is_cron`.

        Override in subclasses to fir the behaviour to your needs.

        Args:
            scheduler (schedule.Scheduler): The actual scheduler.
            callback (callable): The callback to call when the time is right.

        Returns:
            None
        """
        if self.is_cron:
            # Scheduler always executes at the exact minute to check for cron triggering
            scheduler.every().minute.at(":00").do(callback)
        else:
            # Only activate when an interval is specified
            # If not the only way is to trigger the poll by the api `trigger` endpoint
            if self._poll_interval:
                # Scheduler executes every interval seconds to execute the poll
                scheduler.every(self._poll_interval).seconds.do(callback)

    async def poll(self) -> Payload:
        """Performs polling."""
        poll_fun = getattr(self, '_poll')
        if inspect.iscoroutinefunction(poll_fun):
            return await poll_fun()
        return await run_sync(poll_fun)