summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Grönholm <alex.gronholm@nextday.fi>2016-11-06 10:40:50 +0200
committerAlex Grönholm <alex.gronholm@nextday.fi>2016-11-06 10:40:50 +0200
commitf075d15c141ad9ae6ea7b4873db3e8acbc18627d (patch)
tree6b2a21bd73a933fee710f4d5eaeaa8dcde33e875
parent0802351ef10e37fa82ed2da76fa25779c5f87ee3 (diff)
downloadapscheduler-f075d15c141ad9ae6ea7b4873db3e8acbc18627d.tar.gz
Fixed @scheduled_job not playing nice with persistent job stores (fixes #150)
-rw-r--r--apscheduler/util.py23
-rw-r--r--docs/versionhistory.rst3
-rw-r--r--tests/test_job.py5
-rw-r--r--tests/test_util.py20
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),