summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Grönholm <alex.gronholm@nextday.fi>2013-03-03 04:45:41 +0200
committerAlex Grönholm <alex.gronholm@nextday.fi>2013-03-03 04:45:41 +0200
commit363898e7cfd5e46b29512929c12cc796fbee8338 (patch)
tree22341ffc669a3d506de297d15fc38beb7c02a182
parent31db56e97d35678805a0e54030ea7b48226328a1 (diff)
downloadapscheduler-363898e7cfd5e46b29512929c12cc796fbee8338.tar.gz
Added support for scheduling callables by textual references
-rw-r--r--apscheduler/__init__.py2
-rw-r--r--apscheduler/job.py24
-rw-r--r--apscheduler/scheduler.py10
-rw-r--r--examples/reference.py16
-rw-r--r--tests/testjob.py5
-rw-r--r--tests/testscheduler.py10
6 files changed, 48 insertions, 19 deletions
diff --git a/apscheduler/__init__.py b/apscheduler/__init__.py
index 2101a1a..d5b383d 100644
--- a/apscheduler/__init__.py
+++ b/apscheduler/__init__.py
@@ -1,3 +1,3 @@
-version_info = (2, 1, 0)
+version_info = (3, 0, 0, 'dev1')
version = '.'.join(str(n) for n in version_info[:3])
release = '.'.join(str(n) for n in version_info)
diff --git a/apscheduler/job.py b/apscheduler/job.py
index cfc09a2..7f10ad5 100644
--- a/apscheduler/job.py
+++ b/apscheduler/job.py
@@ -5,8 +5,8 @@ Jobs represent scheduled tasks.
from threading import Lock
from datetime import timedelta
-from apscheduler.util import to_unicode, ref_to_obj, get_callable_name,\
- obj_to_ref
+from apscheduler.util import (to_unicode, ref_to_obj, obj_to_ref,
+ get_callable_name)
class MaxInstancesReachedError(Exception):
@@ -43,8 +43,6 @@ class Job(object):
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__'):
- raise TypeError('func must be callable')
if not hasattr(args, '__getitem__'):
raise TypeError('args must be a list-like object')
if not hasattr(kwargs, '__getitem__'):
@@ -56,13 +54,26 @@ class Job(object):
if max_instances <= 0:
raise ValueError('max_instances must be a positive value')
+ if isinstance(func, str):
+ self.func = ref_to_obj(func)
+ self.func_ref = func
+ elif callable(func):
+ self.func = func
+ try:
+ self.func_ref = obj_to_ref(func)
+ except ValueError:
+ # If this happens, this Job won't be serializable
+ self.func_ref = None
+ else:
+ raise TypeError('func must be a callable or a textual '
+ 'reference to one')
+
self._lock = Lock()
self.trigger = trigger
- self.func = func
self.args = args
self.kwargs = kwargs
- self.name = to_unicode(name or get_callable_name(func))
+ self.name = to_unicode(name or get_callable_name(self.func))
self.misfire_grace_time = misfire_grace_time
self.coalesce = coalesce
self.max_runs = max_runs
@@ -115,7 +126,6 @@ class Job(object):
state.pop('instances', None)
state.pop('func', None)
state.pop('_lock', None)
- state['func_ref'] = obj_to_ref(self.func)
return state
def __setstate__(self, state):
diff --git a/apscheduler/scheduler.py b/apscheduler/scheduler.py
index fbdf785..0504270 100644
--- a/apscheduler/scheduler.py
+++ b/apscheduler/scheduler.py
@@ -267,11 +267,19 @@ class Scheduler(object):
**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 to run at the given time
+ :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
diff --git a/examples/reference.py b/examples/reference.py
new file mode 100644
index 0000000..a31c543
--- /dev/null
+++ b/examples/reference.py
@@ -0,0 +1,16 @@
+"""
+Basic example showing how to schedule a callable using a textual
+reference.
+"""
+
+from apscheduler.scheduler import Scheduler
+
+
+if __name__ == '__main__':
+ scheduler = Scheduler(standalone=True)
+ scheduler.add_interval_job('sys:stdout.write', args=['tick\n'], seconds=3)
+ print('Press Ctrl+C to exit')
+ try:
+ scheduler.start()
+ except (KeyboardInterrupt, SystemExit):
+ pass
diff --git a/tests/testjob.py b/tests/testjob.py
index c8d0f15..fa42647 100644
--- a/tests/testjob.py
+++ b/tests/testjob.py
@@ -133,11 +133,6 @@ def test_create_job_no_trigger():
@raises(TypeError)
-def test_create_job_invalid_func():
- Job(SimpleTrigger(datetime.now()), 'bleh', [], {}, 1, False)
-
-
-@raises(TypeError)
def test_create_job_invalid_args():
Job(SimpleTrigger(datetime.now()), lambda: None, None, {}, 1, False)
diff --git a/tests/testscheduler.py b/tests/testscheduler.py
index a7d04e4..3ae2d73 100644
--- a/tests/testscheduler.py
+++ b/tests/testscheduler.py
@@ -38,6 +38,11 @@ class TestOfflineScheduler(object):
assert isinstance(job, Job)
eq_(self.scheduler.get_jobs(), [])
+ def test_add_job_by_reference(self):
+ job = self.scheduler.add_date_job('copy:copy', datetime(2200, 7, 24))
+ eq_(job.func, copy)
+ eq_(job.func_ref, 'copy:copy')
+
def test_configure_jobstore(self):
conf = {'apscheduler.jobstore.ramstore.class':
'apscheduler.jobstores.ram_store:RAMJobStore'}
@@ -134,11 +139,6 @@ class TestJobExecution(object):
scheduler.datetime = datetime
FakeDateTime._now = original_now
- @raises(TypeError)
- def test_noncallable(self):
- date = datetime.now() + timedelta(days=1)
- self.scheduler.add_date_job('wontwork', date)
-
def test_job_name(self):
def my_job():
pass