diff options
author | Alex Grönholm <alex.gronholm@nextday.fi> | 2016-11-06 10:40:50 +0200 |
---|---|---|
committer | Alex Grönholm <alex.gronholm@nextday.fi> | 2016-11-06 10:40:50 +0200 |
commit | f075d15c141ad9ae6ea7b4873db3e8acbc18627d (patch) | |
tree | 6b2a21bd73a933fee710f4d5eaeaa8dcde33e875 | |
parent | 0802351ef10e37fa82ed2da76fa25779c5f87ee3 (diff) | |
download | apscheduler-f075d15c141ad9ae6ea7b4873db3e8acbc18627d.tar.gz |
Fixed @scheduled_job not playing nice with persistent job stores (fixes #150)
-rw-r--r-- | apscheduler/util.py | 23 | ||||
-rw-r--r-- | docs/versionhistory.rst | 3 | ||||
-rw-r--r-- | tests/test_job.py | 5 | ||||
-rw-r--r-- | tests/test_util.py | 20 |
4 files changed, 35 insertions, 16 deletions
diff --git a/apscheduler/util.py b/apscheduler/util.py index d6d5c47..9139789 100644 --- a/apscheduler/util.py +++ b/apscheduler/util.py @@ -4,6 +4,7 @@ from __future__ import division from datetime import date, datetime, time, timedelta, tzinfo from calendar import timegm import re +from functools import partial from pytz import timezone, utc import six @@ -229,20 +230,24 @@ def get_callable_name(func): def obj_to_ref(obj): """ - Returns the path to the given object. + Returns the path to the given callable. :rtype: str + :raises TypeError: if the given object is not callable + :raises ValueError: if the given object is a :class:`~functools.partial`, lambda or a nested + function """ - try: - ref = '%s:%s' % (obj.__module__, get_callable_name(obj)) - obj2 = ref_to_obj(ref) - if obj != obj2: - raise ValueError - except Exception: - raise ValueError('Cannot determine the reference to %r' % obj) + if isinstance(obj, partial): + raise ValueError('Cannot create a reference to a partial()') + + name = get_callable_name(obj) + if '<lambda>' in name: + raise ValueError('Cannot create a reference to a lambda') + if '<locals>' in name: + raise ValueError('Cannot create a reference to a nested function') - return ref + return '%s:%s' % (obj.__module__, name) def ref_to_obj(ref): diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 703198d..86e3f64 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -19,6 +19,9 @@ APScheduler, see the :doc:`migration section <migration>`. * Fixed job store failure (``get_due_jobs()``) causing the scheduler main loop to exit (it now waits a configurable number of seconds before retrying) +* Fixed ``@scheduled_job`` not working when serialization is required (persistent job stores and + ``ProcessPoolScheduler``) + * Improved import logic in ``ref_to_obj()`` to avoid errors in cases where traversing the path with ``getattr()`` would not work (thanks to Jarek Glowacki for the patch) diff --git a/tests/test_job.py b/tests/test_job.py index f13c612..9dc9210 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -1,5 +1,6 @@ # coding: utf-8 from datetime import datetime, timedelta +from functools import partial import pytest import six @@ -120,9 +121,7 @@ def test_private_modify_func_ref(job): def test_private_modify_unreachable_func(job): """Tests that func_ref remains None if no reference to the target callable can be found.""" - def func(): - pass - + func = partial(dummyfunc) job._modify(func=func) assert job.func is func assert job.func_ref is None diff --git a/tests/test_util.py b/tests/test_util.py index 44ebd4e..75f6e42 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -190,10 +190,22 @@ class TestGetCallableName(object): class TestObjToRef(object): - @pytest.mark.parametrize('input', [partial(DummyClass.meth)], ids=['partial/bound method']) - def test_no_ref_found(self, input): - exc = pytest.raises(ValueError, obj_to_ref, input) - assert 'Cannot determine the reference to ' in str(exc.value) + @pytest.mark.parametrize('obj, error', [ + (partial(DummyClass.meth), 'Cannot create a reference to a partial()'), + (lambda: None, 'Cannot create a reference to a lambda') + ], ids=['partial', 'lambda']) + def test_errors(self, obj, error): + exc = pytest.raises(ValueError, obj_to_ref, obj) + assert str(exc.value) == error + + @pytest.mark.skipif(sys.version_info[:2] < (3, 3), + reason='Requires __qualname__ (Python 3.3+)') + def test_nested_function_error(self): + def nested(): + pass + + exc = pytest.raises(ValueError, obj_to_ref, nested) + assert str(exc.value) == 'Cannot create a reference to a nested function' @pytest.mark.parametrize('input,expected', [ pytest.mark.skipif(sys.version_info[:2] == (3, 2), |