|
|
@ -19,6 +19,10 @@ Status = enum('Status',
|
|
|
|
QUEUED='queued', FINISHED='finished', FAILED='failed',
|
|
|
|
QUEUED='queued', FINISHED='finished', FAILED='failed',
|
|
|
|
STARTED='started')
|
|
|
|
STARTED='started')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Sentinel value to mark that some of our lazily evaluated properties have not
|
|
|
|
|
|
|
|
# yet been evaluated.
|
|
|
|
|
|
|
|
UNEVALUATED = object()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def unpickle(pickled_string):
|
|
|
|
def unpickle(pickled_string):
|
|
|
|
"""Unpickles a string, but raises a unified UnpickleError in case anything
|
|
|
|
"""Unpickles a string, but raises a unified UnpickleError in case anything
|
|
|
@ -66,8 +70,6 @@ class Job(object):
|
|
|
|
"""A Job is just a convenient datastructure to pass around job (meta) data.
|
|
|
|
"""A Job is just a convenient datastructure to pass around job (meta) data.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
data = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Job construction
|
|
|
|
# Job construction
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def create(cls, func, args=None, kwargs=None, connection=None,
|
|
|
|
def create(cls, func, args=None, kwargs=None, connection=None,
|
|
|
@ -86,6 +88,9 @@ class Job(object):
|
|
|
|
raise TypeError('{0!r} is not a valid kwargs dict.'.format(kwargs))
|
|
|
|
raise TypeError('{0!r} is not a valid kwargs dict.'.format(kwargs))
|
|
|
|
|
|
|
|
|
|
|
|
job = cls(connection=connection)
|
|
|
|
job = cls(connection=connection)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Set the core job tuple properties
|
|
|
|
|
|
|
|
job._instance = None
|
|
|
|
if inspect.ismethod(func):
|
|
|
|
if inspect.ismethod(func):
|
|
|
|
job._instance = func.__self__
|
|
|
|
job._instance = func.__self__
|
|
|
|
job._func_name = func.__name__
|
|
|
|
job._func_name = func.__name__
|
|
|
@ -95,10 +100,13 @@ class Job(object):
|
|
|
|
job._func_name = func
|
|
|
|
job._func_name = func
|
|
|
|
job._args = args
|
|
|
|
job._args = args
|
|
|
|
job._kwargs = kwargs
|
|
|
|
job._kwargs = kwargs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Extra meta data
|
|
|
|
job.description = description or job.get_call_string()
|
|
|
|
job.description = description or job.get_call_string()
|
|
|
|
job.result_ttl = result_ttl
|
|
|
|
job.result_ttl = result_ttl
|
|
|
|
job.timeout = timeout
|
|
|
|
job.timeout = timeout
|
|
|
|
job._status = status
|
|
|
|
job._status = status
|
|
|
|
|
|
|
|
|
|
|
|
# dependency could be job instance or id
|
|
|
|
# dependency could be job instance or id
|
|
|
|
if depends_on is not None:
|
|
|
|
if depends_on is not None:
|
|
|
|
job._dependency_id = depends_on.id if isinstance(depends_on, Job) else depends_on
|
|
|
|
job._dependency_id = depends_on.id if isinstance(depends_on, Job) else depends_on
|
|
|
@ -155,30 +163,79 @@ class Job(object):
|
|
|
|
|
|
|
|
|
|
|
|
return import_attribute(self.func_name)
|
|
|
|
return import_attribute(self.func_name)
|
|
|
|
|
|
|
|
|
|
|
|
def _get_lazy(self, name):
|
|
|
|
def _unpickle_data(self):
|
|
|
|
if self.data is not None:
|
|
|
|
self._func_name, self._instance, self._args, self._kwargs = unpickle(self.data)
|
|
|
|
self._func_name, self._instance, self._args, self._kwargs = \
|
|
|
|
|
|
|
|
unpickle(self.data)
|
|
|
|
@property
|
|
|
|
|
|
|
|
def data(self):
|
|
|
|
|
|
|
|
if self._data is UNEVALUATED:
|
|
|
|
|
|
|
|
if self._func_name is UNEVALUATED:
|
|
|
|
|
|
|
|
raise ValueError('Cannot build the job data.')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self._instance is UNEVALUATED:
|
|
|
|
|
|
|
|
self._instance = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self._args is UNEVALUATED:
|
|
|
|
|
|
|
|
self._args = ()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self._kwargs is UNEVALUATED:
|
|
|
|
|
|
|
|
self._kwargs = {}
|
|
|
|
|
|
|
|
|
|
|
|
del self.data
|
|
|
|
job_tuple = self._func_name, self._instance, self._args, self._kwargs
|
|
|
|
|
|
|
|
self._data = dumps(job_tuple)
|
|
|
|
|
|
|
|
return self._data
|
|
|
|
|
|
|
|
|
|
|
|
return getattr(self, "_" + name)
|
|
|
|
@data.setter
|
|
|
|
|
|
|
|
def data(self, value):
|
|
|
|
|
|
|
|
self._data = value
|
|
|
|
|
|
|
|
self._func_name = UNEVALUATED
|
|
|
|
|
|
|
|
self._instance = UNEVALUATED
|
|
|
|
|
|
|
|
self._args = UNEVALUATED
|
|
|
|
|
|
|
|
self._kwargs = UNEVALUATED
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@property
|
|
|
|
def func_name(self):
|
|
|
|
def func_name(self):
|
|
|
|
return self._get_lazy('func_name')
|
|
|
|
if self._func_name is UNEVALUATED:
|
|
|
|
|
|
|
|
self._unpickle_data()
|
|
|
|
|
|
|
|
return self._func_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@func_name.setter
|
|
|
|
|
|
|
|
def func_name(self, value):
|
|
|
|
|
|
|
|
self._func_name = value
|
|
|
|
|
|
|
|
self._data = UNEVALUATED
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@property
|
|
|
|
def instance(self):
|
|
|
|
def instance(self):
|
|
|
|
return self._get_lazy('instance')
|
|
|
|
if self._instance is UNEVALUATED:
|
|
|
|
|
|
|
|
self._unpickle_data()
|
|
|
|
|
|
|
|
return self._instance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@instance.setter
|
|
|
|
|
|
|
|
def instance(self, value):
|
|
|
|
|
|
|
|
self._instance = value
|
|
|
|
|
|
|
|
self._data = UNEVALUATED
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@property
|
|
|
|
def args(self):
|
|
|
|
def args(self):
|
|
|
|
return self._get_lazy('args')
|
|
|
|
if self._args is UNEVALUATED:
|
|
|
|
|
|
|
|
self._unpickle_data()
|
|
|
|
|
|
|
|
return self._args
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@args.setter
|
|
|
|
|
|
|
|
def args(self, value):
|
|
|
|
|
|
|
|
self._args = value
|
|
|
|
|
|
|
|
self._data = UNEVALUATED
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@property
|
|
|
|
def kwargs(self):
|
|
|
|
def kwargs(self):
|
|
|
|
return self._get_lazy('kwargs')
|
|
|
|
if self._kwargs is UNEVALUATED:
|
|
|
|
|
|
|
|
self._unpickle_data()
|
|
|
|
|
|
|
|
return self._kwargs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@kwargs.setter
|
|
|
|
|
|
|
|
def kwargs(self, value):
|
|
|
|
|
|
|
|
self._kwargs = value
|
|
|
|
|
|
|
|
self._data = UNEVALUATED
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def exists(cls, job_id, connection=None):
|
|
|
|
def exists(cls, job_id, connection=None):
|
|
|
@ -199,10 +256,11 @@ class Job(object):
|
|
|
|
self.connection = resolve_connection(connection)
|
|
|
|
self.connection = resolve_connection(connection)
|
|
|
|
self._id = id
|
|
|
|
self._id = id
|
|
|
|
self.created_at = utcnow()
|
|
|
|
self.created_at = utcnow()
|
|
|
|
self._func_name = None
|
|
|
|
self._data = UNEVALUATED
|
|
|
|
self._instance = None
|
|
|
|
self._func_name = UNEVALUATED
|
|
|
|
self._args = None
|
|
|
|
self._instance = UNEVALUATED
|
|
|
|
self._kwargs = None
|
|
|
|
self._args = UNEVALUATED
|
|
|
|
|
|
|
|
self._kwargs = UNEVALUATED
|
|
|
|
self.description = None
|
|
|
|
self.description = None
|
|
|
|
self.origin = None
|
|
|
|
self.origin = None
|
|
|
|
self.enqueued_at = None
|
|
|
|
self.enqueued_at = None
|
|
|
@ -215,7 +273,6 @@ class Job(object):
|
|
|
|
self._dependency_id = None
|
|
|
|
self._dependency_id = None
|
|
|
|
self.meta = {}
|
|
|
|
self.meta = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Data access
|
|
|
|
# Data access
|
|
|
|
def get_id(self): # noqa
|
|
|
|
def get_id(self): # noqa
|
|
|
|
"""The job ID for this job instance. Generates an ID lazily the
|
|
|
|
"""The job ID for this job instance. Generates an ID lazily the
|
|
|
@ -251,12 +308,6 @@ class Job(object):
|
|
|
|
"""The Redis key that is used to store job hash under."""
|
|
|
|
"""The Redis key that is used to store job hash under."""
|
|
|
|
return self.dependents_key_for(self.id)
|
|
|
|
return self.dependents_key_for(self.id)
|
|
|
|
|
|
|
|
|
|
|
|
@property # noqa
|
|
|
|
|
|
|
|
def job_tuple(self):
|
|
|
|
|
|
|
|
"""Returns the job tuple that encodes the actual function call that
|
|
|
|
|
|
|
|
this job represents."""
|
|
|
|
|
|
|
|
return (self.func_name, self.instance, self.args, self.kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@property
|
|
|
|
def result(self):
|
|
|
|
def result(self):
|
|
|
|
"""Returns the return value of the job.
|
|
|
|
"""Returns the return value of the job.
|
|
|
@ -284,7 +335,6 @@ class Job(object):
|
|
|
|
"""Backwards-compatibility accessor property `return_value`."""
|
|
|
|
"""Backwards-compatibility accessor property `return_value`."""
|
|
|
|
return_value = result
|
|
|
|
return_value = result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Persistence
|
|
|
|
# Persistence
|
|
|
|
def refresh(self): # noqa
|
|
|
|
def refresh(self): # noqa
|
|
|
|
"""Overwrite the current instance's properties with the values in the
|
|
|
|
"""Overwrite the current instance's properties with the values in the
|
|
|
@ -325,11 +375,7 @@ class Job(object):
|
|
|
|
"""Returns a serialization of the current job instance"""
|
|
|
|
"""Returns a serialization of the current job instance"""
|
|
|
|
obj = {}
|
|
|
|
obj = {}
|
|
|
|
obj['created_at'] = utcformat(self.created_at or utcnow())
|
|
|
|
obj['created_at'] = utcformat(self.created_at or utcnow())
|
|
|
|
|
|
|
|
|
|
|
|
if self.data is not None:
|
|
|
|
|
|
|
|
obj['data'] = self.data
|
|
|
|
obj['data'] = self.data
|
|
|
|
elif self.func_name is not None:
|
|
|
|
|
|
|
|
obj['data'] = dumps(self.job_tuple)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.origin is not None:
|
|
|
|
if self.origin is not None:
|
|
|
|
obj['origin'] = self.origin
|
|
|
|
obj['origin'] = self.origin
|
|
|
@ -443,7 +489,6 @@ class Job(object):
|
|
|
|
def __str__(self):
|
|
|
|
def __str__(self):
|
|
|
|
return '<Job %s: %s>' % (self.id, self.description)
|
|
|
|
return '<Job %s: %s>' % (self.id, self.description)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Job equality
|
|
|
|
# Job equality
|
|
|
|
def __eq__(self, other): # noqa
|
|
|
|
def __eq__(self, other): # noqa
|
|
|
|
return self.id == other.id
|
|
|
|
return self.id == other.id
|
|
|
|