summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoragronholm <devnull@localhost>2011-04-11 02:19:36 +0300
committeragronholm <devnull@localhost>2011-04-11 02:19:36 +0300
commit0915f85b7a2047c2469adc69ddcba2b14b104be8 (patch)
treea81a2d1acdbf043d22e9971f8a4200c18838ba2c
parent379f7160e0fea545111e53fdf019e2ab7125bef2 (diff)
downloadapscheduler-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.py14
-rw-r--r--apscheduler/jobstores/sqlalchemy_store.py2
-rw-r--r--apscheduler/scheduler.py27
-rw-r--r--docs/index.rst16
-rw-r--r--docs/migration.rst8
-rw-r--r--tests/testjob.py10
-rw-r--r--tests/testscheduler.py24
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