diff options
author | Alex Grönholm <alex.gronholm@nextday.fi> | 2013-03-03 04:45:41 +0200 |
---|---|---|
committer | Alex Grönholm <alex.gronholm@nextday.fi> | 2013-03-03 04:45:41 +0200 |
commit | 363898e7cfd5e46b29512929c12cc796fbee8338 (patch) | |
tree | 22341ffc669a3d506de297d15fc38beb7c02a182 | |
parent | 31db56e97d35678805a0e54030ea7b48226328a1 (diff) | |
download | apscheduler-363898e7cfd5e46b29512929c12cc796fbee8338.tar.gz |
Added support for scheduling callables by textual references
-rw-r--r-- | apscheduler/__init__.py | 2 | ||||
-rw-r--r-- | apscheduler/job.py | 24 | ||||
-rw-r--r-- | apscheduler/scheduler.py | 10 | ||||
-rw-r--r-- | examples/reference.py | 16 | ||||
-rw-r--r-- | tests/testjob.py | 5 | ||||
-rw-r--r-- | tests/testscheduler.py | 10 |
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 |