diff --git a/rq/job.py b/rq/job.py index e5317454..362265d7 100644 --- a/rq/job.py +++ b/rq/job.py @@ -111,8 +111,11 @@ class Job(object): job._func_name = '%s.%s' % (func.__module__, func.__name__) elif isinstance(func, string_types): job._func_name = as_text(func) + elif not inspect.isclass(func) and hasattr(func, '__call__'): # a callable class instance + job._instance = func + job._func_name = '__call__' else: - raise TypeError('Expected a function/method/string, but got: {}'.format(func)) + raise TypeError('Expected a callable or a string, but got: {}'.format(func)) job._args = args job._kwargs = kwargs diff --git a/tests/fixtures.py b/tests/fixtures.py index a375562b..7a9ac58a 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -69,6 +69,11 @@ class Number(object): return self.value / y +class CallableObject(object): + def __call__(self): + return u"I'm callable" + + with Connection(): @job(queue='default') def decorated_job(x, y): diff --git a/tests/test_job.py b/tests/test_job.py index d13ab7a8..a9580a89 100644 --- a/tests/test_job.py +++ b/tests/test_job.py @@ -11,7 +11,8 @@ from rq.queue import Queue from rq.utils import utcformat from tests import RQTestCase -from tests.fixtures import access_self, Number, say_hello, some_calculation +from tests.fixtures import (access_self, CallableObject, Number, say_hello, + some_calculation) from tests.helpers import strip_microseconds try: @@ -84,6 +85,14 @@ class TestJob(RQTestCase): self.assertIsNone(job.instance) self.assertEquals(job.args, ('World',)) + def test_create_job_from_callable_class(self): + """Creation of jobs using a callable class specifier.""" + kallable = CallableObject() + job = Job.create(func=kallable) + + self.assertEquals(job.func, kallable.__call__) + self.assertEquals(job.instance, kallable) + def test_job_properties_set_data_property(self): """Data property gets derived from the job tuple.""" job = Job()