diff options
author | agronholm <devnull@localhost> | 2011-04-11 02:19:36 +0300 |
---|---|---|
committer | agronholm <devnull@localhost> | 2011-04-11 02:19:36 +0300 |
commit | 0915f85b7a2047c2469adc69ddcba2b14b104be8 (patch) | |
tree | a81a2d1acdbf043d22e9971f8a4200c18838ba2c | |
parent | 379f7160e0fea545111e53fdf019e2ab7125bef2 (diff) | |
download | apscheduler-0915f85b7a2047c2469adc69ddcba2b14b104be8.tar.gz |
Readded Scheduler.unschedule_func(), which now raises a KeyError when no matching jobs are found; renamed max_concurrency to max_instances
-rw-r--r-- | apscheduler/job.py | 14 | ||||
-rw-r--r-- | apscheduler/jobstores/sqlalchemy_store.py | 2 | ||||
-rw-r--r-- | apscheduler/scheduler.py | 27 | ||||
-rw-r--r-- | docs/index.rst | 16 | ||||
-rw-r--r-- | docs/migration.rst | 8 | ||||
-rw-r--r-- | tests/testjob.py | 10 | ||||
-rw-r--r-- | tests/testscheduler.py | 24 |
7 files changed, 74 insertions, 27 deletions
diff --git a/apscheduler/job.py b/apscheduler/job.py index 9c3356a..a3998c2 100644 --- a/apscheduler/job.py +++ b/apscheduler/job.py @@ -11,7 +11,7 @@ from apscheduler.util import to_unicode, ref_to_obj, get_callable_name,\ class Job(object): """ - Encapsulates the actual Job along with its metadata. JobMeta instances + Encapsulates the actual Job along with its metadata. Job instances are created by the scheduler when adding jobs, and it should not be directly instantiated. @@ -26,14 +26,14 @@ class Job(object): that the job should be run more than once in succession :param max_runs: maximum number of times this job is allowed to be triggered - :param max_running_instances: maximum number of concurrently running - instances of this job + :param max_instances: maximum number of concurrently running + instances allowed for this job """ id = None next_run_time = None def __init__(self, trigger, func, args, kwargs, misfire_grace_time, - coalesce, name=None, max_runs=None, max_concurrency=1): + coalesce, name=None, max_runs=None, max_instances=1): if not trigger: raise ValueError('The trigger must not be None') if not hasattr(func, '__call__'): @@ -46,8 +46,8 @@ class Job(object): raise ValueError('misfire_grace_time must be a positive value') if max_runs is not None and max_runs <= 0: raise ValueError('max_runs must be a positive value') - if max_concurrency <= 0: - raise ValueError('max_concurrency must be a positive value') + if max_instances <= 0: + raise ValueError('max_instances must be a positive value') self._lock = Lock() @@ -59,7 +59,7 @@ class Job(object): self.misfire_grace_time = misfire_grace_time self.coalesce = coalesce self.max_runs = max_runs - self.max_concurrency = max_concurrency + self.max_instances = max_instances self.runs = 0 self.instances = 0 diff --git a/apscheduler/jobstores/sqlalchemy_store.py b/apscheduler/jobstores/sqlalchemy_store.py index 0e7e515..8ece7e2 100644 --- a/apscheduler/jobstores/sqlalchemy_store.py +++ b/apscheduler/jobstores/sqlalchemy_store.py @@ -43,7 +43,7 @@ class SQLAlchemyJobStore(JobStore): Column('misfire_grace_time', Integer, nullable=False), Column('coalesce', Boolean, nullable=False), Column('max_runs', Integer), - Column('max_concurrency', Integer), + Column('max_instances', Integer), Column('next_run_time', DateTime, nullable=False), Column('runs', BigInteger)) diff --git a/apscheduler/scheduler.py b/apscheduler/scheduler.py index a56520b..8aef748 100644 --- a/apscheduler/scheduler.py +++ b/apscheduler/scheduler.py @@ -194,7 +194,9 @@ class Scheduler(object): self._listeners_lock.release() def remove_listener(self, callback): - """Removes a previously added event listener.""" + """ + Removes a previously added event listener. + """ self._listeners_lock.acquire() try: for i, (cb, _) in enumerate(self._listeners): @@ -377,11 +379,26 @@ class Scheduler(object): Removes a job, preventing it from being run any more. """ for alias, jobstore in dict_items(self._jobstores): - if job in jobstore.jobs: + if job in list(jobstore.jobs): self._remove_job(job, alias, jobstore) return - raise ValueError('Job "%s" is not scheduled in any job store' % job) + raise KeyError('Job "%s" is not scheduled in any job store' % job) + + def unschedule_func(self, func): + """ + Removes all jobs that would execute the given function. + """ + found = False + for alias, jobstore in dict_items(self._jobstores): + for job in list(jobstore.jobs): + if job.func == func: + self._remove_job(job, alias, jobstore) + found = True + + if not found: + raise KeyError('The given function is not scheduled in this ' + 'scheduler') def print_jobs(self, out=None): """ @@ -417,11 +434,11 @@ class Scheduler(object): self._notify_listeners(JobEvent(EVENT_JOB_MISSED, job, run_time)) logger.warning('Run time of job "%s" was missed by %s', job, difference) - elif job.instances == job.max_concurrency: + elif job.instances == job.max_instances: # Notify listeners about a missed run self._notify_listeners(JobEvent(EVENT_JOB_MISSED, job, run_time)) logger.warning('Execution of job "%s" skipped: too many instances ' - 'running already (%d)', job, job.max_concurrency) + 'running already (%d)', job, job.max_instances) else: logger.info('Running job "%s" (scheduled at %s)', job, run_time) job.add_instance() diff --git a/docs/index.rst b/docs/index.rst index 12d03af..07e26a0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -196,6 +196,12 @@ scheduler by that name when the scheduler is started, a new job store of type :class:`~apscheduler.jobstores.ram_store.RAMJobStore` is created to serve as the default. +The built-in job stores are: + +* :class:`~apscheduler.jobstores.shelve_store.ShelveJobStore` +* :class:`~apscheduler.jobstores.sqlalchemy_store.SQLAlchemyJobStore` +* :class:`~apscheduler.jobstores.mongodb_store.MongoDBJobStore` + Job stores can be added either through configuration options or the :meth:`~apscheduler.scheduler.Scheduler.add_jobstore` method. The following are therefore equal:: @@ -213,8 +219,8 @@ and:: The example configuration above results in the scheduler having two job stores -- one -(:class:`~apscheduler.jobstores.ram_store.RAMJobStore`) and one -(:class:`~apscheduler.jobstores.shelve_store.ShelveJobStore`). +:class:`~apscheduler.jobstores.ram_store.RAMJobStore` and one +:class:`~apscheduler.jobstores.shelve_store.ShelveJobStore`. In addition to the built-in job stores, it is possible to extend APScheduler to support other persistence mechanisms as well. See the @@ -247,14 +253,14 @@ directly, since the scheduler has shortcut methods for these built-in triggers, as discussed in the next section. -Limiting the concurrently executing instances of a job ------------------------------------------------------- +Limiting the number of concurrently executing instances of a job +---------------------------------------------------------------- By default, no two instances of the same job will be run concurrently. This means that if the job is about to be run but the previous run hasn't finished yet, then the latest run is considered a misfire. It is possible to set the maximum number of instances for a particular job that the scheduler will let -run concurrently, by using the ``max_concurrency`` keyword argument when adding +run concurrently, by using the ``max_instances`` keyword argument when adding the job. diff --git a/docs/migration.rst b/docs/migration.rst index 5fce220..ccdb214 100644 --- a/docs/migration.rst +++ b/docs/migration.rst @@ -4,6 +4,7 @@ Migrating from APScheduler v1.x to 2.0 There have been some API changes since the 1.x series. This document explains the changes made to v2.0 that are incompatible with the v1.x API. + API changes ----------- @@ -14,9 +15,12 @@ API changes * dump_jobs() is now print_jobs() and prints directly to the given file or sys.stdout if none is given * The ``repeat`` parameter was removed from - :meth:`apscheduler.scheduler.Scheduler.add_interval_job` and - :meth:`apscheduler.scheduler.Scheduler.interval_schedule` in favor of the + :meth:`~apscheduler.scheduler.Scheduler.add_interval_job` and + :meth:`~apscheduler.scheduler.Scheduler.interval_schedule` in favor of the universal ``max_runs`` option +* :meth:`~apscheduler.scheduler.Scheduler.unschedule_func` now raises a + KeyError if the given function is not scheduled + Configuration changes --------------------- diff --git a/tests/testjob.py b/tests/testjob.py index 3ff43b3..9afcdb4 100644 --- a/tests/testjob.py +++ b/tests/testjob.py @@ -64,20 +64,20 @@ class TestJob(object): name='tests.testjob.dummyfunc', args=[], kwargs={}, misfire_grace_time=1, coalesce=False, max_runs=None, - max_concurrency=1, runs=0)) + max_instances=1, runs=0)) def test_setstate(self): trigger = SimpleTrigger('2010-12-14 13:05:00') state = dict(trigger=trigger, name='apschedulertests.testjob.dummyfunc', func_ref='tests.testjob:dummyfunc', args=[], kwargs={}, misfire_grace_time=2, max_runs=2, - coalesce=True, max_concurrency=2, runs=1) + coalesce=True, max_instances=2, runs=1) self.job.__setstate__(state) eq_(self.job.trigger, trigger) eq_(self.job.func, dummyfunc) eq_(self.job.max_runs, 2) eq_(self.job.coalesce, True) - eq_(self.job.max_concurrency, 2) + eq_(self.job.max_instances, 2) eq_(self.job.runs, 1) assert not hasattr(self.job, 'func_ref') assert isinstance(self.job._lock, lock_type) @@ -148,6 +148,6 @@ def test_create_job_invalid_maxruns(): @raises(ValueError) -def test_create_job_invalid_maxconcurrency(): +def test_create_job_invalid_maxinstances(): Job(SimpleTrigger(datetime.now()), lambda: None, [], {}, 1, False, - max_concurrency=0) + max_instances=0) diff --git a/tests/testscheduler.py b/tests/testscheduler.py index 06306cc..9ac75a5 100644 --- a/tests/testscheduler.py +++ b/tests/testscheduler.py @@ -161,17 +161,37 @@ class TestJobExecution(object): eq_(a.val, 2) def test_unschedule_job(self): - def increment(vals): + def increment(): vals[0] += 1 vals = [0] - job = self.scheduler.add_cron_job(increment, args=[vals]) + job = self.scheduler.add_cron_job(increment) self.scheduler._process_jobs(job.next_run_time) eq_(vals[0], 1) self.scheduler.unschedule_job(job) self.scheduler._process_jobs(job.next_run_time) eq_(vals[0], 1) + def test_unschedule_func(self): + def increment(): + vals[0] += 1 + + def increment2(): + vals[0] += 1 + + vals = [0] + job1 = self.scheduler.add_cron_job(increment) + job2 = self.scheduler.add_cron_job(increment2) + job3 = self.scheduler.add_cron_job(increment) + eq_(self.scheduler.get_jobs(), [job1, job2, job3]) + + self.scheduler.unschedule_func(increment) + eq_(self.scheduler.get_jobs(), [job2]) + + @raises(KeyError) + def test_unschedule_func_notfound(self): + self.scheduler.unschedule_func(copy) + def test_job_finished(self): def increment(): vals[0] += 1 |