def _reconstitute_job(self, job_state): schedule = job_state['schedule'] schedule.pop('coalesce', None) next_run_time = job_state['next_run_time'] if next_run_time == 1: # This is hacky. We need to subtract more than value of misfire_grace_time # so that the job will be missed right after loading it for the first time # after doing fresh install instead of being executed. next_run_time = int(time.time() - 1200) job = Job(id=job_state['id'], func="__main__:job", trigger=CronTrigger(**schedule), name=job_state['name'], args=[job_state['task']] + job_state['args'], scheduler=self._scheduler, executor='default', next_run_time=utc_timestamp_to_datetime(next_run_time), kwargs={ 'id': job_state['id'], 'name': job_state['name'], 'hidden': job_state.get('hidden', False), 'protected': job_state.get('protected', False) }) job.coalesce = True job.max_instances = 1 job.misfire_grace_time = 600 job._jobstore_alias = self._alias return job
def add_job(self, trigger, func, args, kwargs, jobstore='default', **options): """ Adds the given job to the job list and notifies the scheduler thread. Any extra keyword arguments are passed along to the constructor of the :class:`~apscheduler.job.Job` class (see :ref:`job_options`). :param trigger: trigger that determines when ``func`` is called :param func: callable to run at the given time :param args: list of positional arguments to call func with :param kwargs: dict of keyword arguments to call func with :param jobstore: alias of the job store to store the job in :rtype: :class:`~apscheduler.job.Job` """ job = Job(trigger, func, args or [], kwargs or {}, options.pop('misfire_grace_time', self.misfire_grace_time), options.pop('coalesce', self.coalesce), **options) if not self.running: self._pending_jobs.append((job, jobstore)) logger.info('Adding job tentatively -- it will be properly ' 'scheduled when the scheduler starts') else: self._real_add_job(job, jobstore, True) return job
def add_job(self, trigger, func, args, kwargs, jobstore='default', **options): """ Adds the given job to the job list and notifies the scheduler thread. The ``func`` argument can be given either as a callable object or a textual reference in the ``package.module:some.object`` format, where the first half (separated by ``:``) is an importable module and the second half is a reference to the callable object, relative to the module. Any extra keyword arguments are passed along to the constructor of the :class:`~apscheduler.job.Job` class (see :ref:`job_options`). :param trigger: trigger that determines when ``func`` is called :param func: callable (or a textual reference to one) to run at the given time :param args: list of positional arguments to call func with :param kwargs: dict of keyword arguments to call func with :param jobstore: alias of the job store to store the job in :rtype: :class:`~apscheduler.job.Job` """ job = Job(trigger, func, args or [], kwargs or {}, options.pop('misfire_grace_time', self.misfire_grace_time), options.pop('coalesce', self.coalesce), **options) if not self.running: self._pending_jobs.append((job, jobstore)) logger.info( 'Adding job tentatively -- it will be properly scheduled when the scheduler starts' ) else: self._real_add_job(job, jobstore, True) return job
def add(self): job_id = self._get_job_id() print(job_id) pid = self.pid if pid is not None: # if scheduler is running, stop it. self.stop() SCHEDULER.add_job( Job( get_forecast, # trigger=None, # args=None, # kwargs=None, id=job_id, # name=None, # misfire_grace_time=undefined, # coalesce=undefined, # max_instances=undefined, # next_run_time=undefined, # jobstore='default', # executor='default', # replace_existing=False, # **trigger_args )) if pid is not None: self.start() # fork and exit parent
def test_adding_a_jobstore_adds_all_jobs_in_it(self, mock_notify_jobstore_event, mock_notify_job_event, _): watcher = SchedulerWatcher(self.scheduler) jobstore = MemoryJobStore() jobstore.add_job(Job(scheduler=self.scheduler, id='job_1', next_run_time=datetime.now() + timedelta(days=1))) jobstore.add_job(Job(scheduler=self.scheduler, id='job_2', next_run_time=datetime.now() + timedelta(days=2))) self.assertEqual(0, len(watcher.jobs)) self.scheduler.add_jobstore(jobstore, alias='in_memory_2') self.assertIn('in_memory_2', watcher.jobstores, 'Watcher should have the new jobstore tracked') self.assertEqual(2, len(watcher.jobs), 'Watcher should add all jobs in the newly added jobstore') self.assertTrue(all([job_id in watcher.jobs for job_id in ['job_1', 'job_2']])) self.assertEqual(2, mock_notify_job_event.call_count) mock_notify_jobstore_event.assert_called_once()
def create(**kwargs): kwargs.setdefault("scheduler", Mock(BaseScheduler, timezone=timezone)) job_kwargs = job_defaults.copy() job_kwargs.update(kwargs) job_kwargs["trigger"] = BlockingScheduler()._create_trigger( job_kwargs.pop("trigger"), job_kwargs.pop("trigger_args")) job_kwargs.setdefault("next_run_time", None) return Job(**job_kwargs)
def test_one_job_fails_to_load(self): global dummy_job2, dummy_job_temp job1 = Job(self.trigger, dummy_job, [], {}, 1, False) job2 = Job(self.trigger, dummy_job2, [], {}, 1, False) job3 = Job(self.trigger, dummy_job3, [], {}, 1, False) for job in job1, job2, job3: job.next_run_time = self.trigger_date self.jobstore.add_job(job) dummy_job_temp = dummy_job2 del dummy_job2 try: self.jobstore.load_jobs() eq_(len(self.jobstore.jobs), 2) finally: dummy_job2 = dummy_job_temp del dummy_job_temp
def create(**kwargs): kwargs.setdefault('scheduler', Mock(BaseScheduler, timezone=timezone)) job_kwargs = job_defaults.copy() job_kwargs.update(kwargs) job_kwargs['trigger'] = BlockingScheduler()._create_trigger(job_kwargs.pop('trigger'), job_kwargs.pop('trigger_args')) job_kwargs.setdefault('next_run_time', None) return Job(**job_kwargs)
def test_jobs_equal(self): assert self.job == self.job job2 = Job(SimpleTrigger(self.RUNTIME), lambda: None, [], {}, 1, False) assert self.job != job2 job2.id = self.job.id = 123 eq_(self.job, job2) assert self.job != 'bleh'
def add_job(self, key, jobname, trigger, func, args, kwargs, jobstore='default', **options): job = Job(trigger, func, args or [], kwargs or {}, options.pop('misfire_grace_time', self.misfire_grace_time), options.pop('coalesce', self.coalesce), name=jobname, **options) job.key = key if not self.running: self._pending_jobs.append((job, jobstore)) else: self._real_add_job(job, jobstore, True) return job
def test_constructor(job_id): with patch('apscheduler.job.Job._modify') as _modify: scheduler_mock = MagicMock(BaseScheduler) job = Job(scheduler_mock, id=job_id) assert job._scheduler is scheduler_mock assert job._jobstore_alias is None modify_kwargs = _modify.call_args[1] if job_id is None: assert len(modify_kwargs['id']) == 32 else: assert modify_kwargs['id'] == job_id
def test_real_add_job(self, scheduler, job_exists, replace_existing, wakeup): job = Job(scheduler, id='foo', func=lambda: None, args=(), kwargs={}, next_run_time=None) jobstore = MagicMock( BaseJobStore, _alias='bar', add_job=MagicMock( side_effect=ConflictingIdError('foo') if job_exists else None)) scheduler.wakeup = MagicMock() scheduler._job_defaults = { 'misfire_grace_time': 3, 'coalesce': False, 'max_instances': 6 } scheduler._dispatch_event = MagicMock() scheduler._jobstores = {'bar': jobstore} # Expect and exception if the job already exists and we're not trying to replace it if job_exists and not replace_existing: pytest.raises(ConflictingIdError, scheduler._real_add_job, job, 'bar', replace_existing, wakeup) return scheduler._real_add_job(job, 'bar', replace_existing, wakeup) # Check that the undefined values were replaced with scheduler defaults assert job.misfire_grace_time == 3 assert job.coalesce is False assert job.max_instances == 6 assert job.next_run_time is None if job_exists: jobstore.update_job.assert_called_once_with(job) else: assert not jobstore.update_job.called if wakeup: scheduler.wakeup.assert_called_once_with() else: assert not scheduler.wakeup.called assert job._jobstore_alias == 'bar' assert scheduler._dispatch_event.call_count == 1 event = scheduler._dispatch_event.call_args[0][0] assert event.code == EVENT_JOB_ADDED assert event.job_id == 'foo'
def schedule_msg(text, schedule, group_id, job=None): from resourcess.update import Update group = GroupModel.find_by_id(group_id).json() func = None args = None if group['channel']['channel_name'] == 'telegram': func = Update.telegram_send_message args = [text, int(group['channel']['group_chat_id'])] if group['channel']['channel_name'] == 'whatsapp': func = Update.whatsapp_send_message args = [group['channel']['group_name'], text] if job: job.modify(func=func, args=args) if schedule != job.next_run_time: job.reschedule(trigger='date', run_date=schedule) return job else: _id = Job(scheduler).id job = scheduler.add_job(id=_id, func=func, run_date=schedule, trigger='date', args=args) return job
def test_create_job_invalid_func(): Job(SimpleTrigger(datetime.now()), 'bleh', [], {}, 1, False)
def add_job(self, func, trigger=None, args=None, kwargs=None, id=None, name=None, misfire_grace_time=undefined, coalesce=undefined, max_instances=undefined, next_run_time=undefined, jobstore='default', executor='default', replace_existing=False, **trigger_args): """ add_job(func, trigger=None, args=None, kwargs=None, id=None, \ name=None, misfire_grace_time=undefined, coalesce=undefined, \ max_instances=undefined, next_run_time=undefined, \ jobstore='default', executor='default', \ replace_existing=False, **trigger_args) Adds the given job to the job list and wakes up the scheduler if it's already running. Any option that defaults to ``undefined`` will be replaced with the corresponding default value when the job is scheduled (which happens when the scheduler is started, or immediately if the scheduler is already running). The ``func`` argument can be given either as a callable object or a textual reference in the ``package.module:some.object`` format, where the first half (separated by ``:``) is an importable module and the second half is a reference to the callable object, relative to the module. The ``trigger`` argument can either be: #. the alias name of the trigger (e.g. ``date``, ``interval`` or ``cron``), in which case any extra keyword arguments to this method are passed on to the trigger's constructor #. an instance of a trigger class :param func: callable (or a textual reference to one) to run at the given time :param str|apscheduler.triggers.base.BaseTrigger trigger: trigger that determines when ``func`` is called :param list|tuple args: list of positional arguments to call func with :param dict kwargs: dict of keyword arguments to call func with :param str|unicode id: explicit identifier for the job (for modifying it later) :param str|unicode name: textual description of the job :param int misfire_grace_time: seconds after the designated runtime that the job is still allowed to be run :param bool coalesce: run once instead of many times if the scheduler determines that the job should be run more than once in succession :param int max_instances: maximum number of concurrently running instances allowed for this job :param datetime next_run_time: when to first run the job, regardless of the trigger (pass ``None`` to add the job as paused) :param str|unicode jobstore: alias of the job store to store the job in :param str|unicode executor: alias of the executor to run the job with :param bool replace_existing: ``True`` to replace an existing job with the same ``id`` (but retain the number of runs from the existing one) :rtype: Job """ job_kwargs = { 'trigger': self._create_trigger(trigger, trigger_args), 'executor': executor, 'func': func, 'args': tuple(args) if args is not None else (), 'kwargs': dict(kwargs) if kwargs is not None else {}, 'id': id, 'name': name, 'misfire_grace_time': misfire_grace_time, 'coalesce': coalesce, 'max_instances': max_instances, 'next_run_time': next_run_time } job_kwargs = dict((key, value) for key, value in six.iteritems(job_kwargs) if value is not undefined) job = Job(self, **job_kwargs) # Don't really add jobs to job stores before the scheduler is up and running with self._jobstores_lock: if self.state == STATE_STOPPED: self._pending_jobs.append((job, jobstore, replace_existing)) self._logger.info( 'Adding job tentatively -- it will be properly scheduled when ' 'the scheduler starts') else: self._real_add_job(job, jobstore, replace_existing) return job
from randompicker import format as format_ def dummy_func(target, task): pass test_scheduler = AsyncIOScheduler() test_jobs = [ Job( id="xxx", scheduler=test_scheduler, func=dummy_func, args=(), kwargs={ "target": "C1234", "task": "play music" }, trigger=CronTrigger(day_of_week="mon", hour="9", minute="0", week="*"), ), Job( id="yyy", scheduler=test_scheduler, func=dummy_func, args=(), kwargs={ "target": "S1234", "task": "do groceries" }, trigger=DateTrigger(run_date=datetime(2020, 5, 4, 18, 0)),
def test_create_job_invalid_misfire(): Job(SimpleTrigger(datetime.now()), lambda: None, [], {}, 0, False)
def test_create_job_invalid_maxinstances(): Job(SimpleTrigger(datetime.now()), lambda: None, [], {}, 1, False, max_instances=0)
def setup(self): self.trigger_date = datetime(2999, 1, 1) self.earlier_date = datetime(2998, 12, 31) self.trigger = SimpleTrigger(self.trigger_date) self.job = Job(self.trigger, dummy_job, [], {}, 1, False) self.job.next_run_time = self.trigger_date
def setup(self): self.trigger = SimpleTrigger(self.RUNTIME) self.job = Job(self.trigger, dummyfunc, [], {}, 1, False)
def test_create_job_invalid_kwargs(): Job(SimpleTrigger(datetime.now()), lambda: None, [], None, 1, False)
def test_create_job_no_trigger(): Job(None, lambda: None, [], {}, 1, False)