|
|
@ -1,5 +1,6 @@
|
|
|
|
import inspect
|
|
|
|
import inspect
|
|
|
|
from uuid import uuid4
|
|
|
|
from uuid import uuid4
|
|
|
|
|
|
|
|
from functools import wraps
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
from cPickle import loads, dumps, UnpicklingError
|
|
|
|
from cPickle import loads, dumps, UnpicklingError
|
|
|
|
except ImportError: # noqa
|
|
|
|
except ImportError: # noqa
|
|
|
@ -62,9 +63,28 @@ def get_current_job():
|
|
|
|
return Job.fetch(job_id)
|
|
|
|
return Job.fetch(job_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def lazy(f):
|
|
|
|
|
|
|
|
"""Decorator for a lazy data property."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
attr = "_" + f.__name__
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@wraps(f)
|
|
|
|
|
|
|
|
def decorator(job):
|
|
|
|
|
|
|
|
if job.data is not None:
|
|
|
|
|
|
|
|
job._func_name, job._instance, job._args, job._kwargs = unpickle(job.data)
|
|
|
|
|
|
|
|
del job.data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return getattr(job, attr)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return property(decorator)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Job(object):
|
|
|
|
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,
|
|
|
@ -97,7 +117,7 @@ class Job(object):
|
|
|
|
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
|
|
|
|
return job
|
|
|
|
return job
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@lazy
|
|
|
|
def func_name(self):
|
|
|
|
def func_name(self):
|
|
|
|
return self._func_name
|
|
|
|
return self._func_name
|
|
|
|
|
|
|
|
|
|
|
@ -152,15 +172,15 @@ class Job(object):
|
|
|
|
|
|
|
|
|
|
|
|
return import_attribute(self.func_name)
|
|
|
|
return import_attribute(self.func_name)
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@lazy
|
|
|
|
def instance(self):
|
|
|
|
def instance(self):
|
|
|
|
return self._instance
|
|
|
|
return self._instance
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@lazy
|
|
|
|
def args(self):
|
|
|
|
def args(self):
|
|
|
|
return self._args
|
|
|
|
return self._args
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
@lazy
|
|
|
|
def kwargs(self):
|
|
|
|
def kwargs(self):
|
|
|
|
return self._kwargs
|
|
|
|
return self._kwargs
|
|
|
|
|
|
|
|
|
|
|
@ -179,15 +199,6 @@ class Job(object):
|
|
|
|
job.refresh()
|
|
|
|
job.refresh()
|
|
|
|
return job
|
|
|
|
return job
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
|
|
def safe_fetch(cls, id, connection=None):
|
|
|
|
|
|
|
|
"""Fetches a persisted job from its corresponding Redis key, but does
|
|
|
|
|
|
|
|
not instantiate it, making it impossible to get UnpickleErrors.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
job = cls(id, connection=connection)
|
|
|
|
|
|
|
|
job.refresh(safe=True)
|
|
|
|
|
|
|
|
return job
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, id=None, connection=None):
|
|
|
|
def __init__(self, id=None, connection=None):
|
|
|
|
self.connection = resolve_connection(connection)
|
|
|
|
self.connection = resolve_connection(connection)
|
|
|
|
self._id = id
|
|
|
|
self._id = id
|
|
|
@ -279,7 +290,7 @@ class Job(object):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Persistence
|
|
|
|
# Persistence
|
|
|
|
def refresh(self, safe=False): # 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
|
|
|
|
corresponding Redis key.
|
|
|
|
corresponding Redis key.
|
|
|
|
|
|
|
|
|
|
|
@ -301,11 +312,6 @@ class Job(object):
|
|
|
|
except KeyError:
|
|
|
|
except KeyError:
|
|
|
|
raise NoSuchJobError('Unexpected job format: {0}'.format(obj))
|
|
|
|
raise NoSuchJobError('Unexpected job format: {0}'.format(obj))
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
self._func_name, self._instance, self._args, self._kwargs = unpickle(self.data)
|
|
|
|
|
|
|
|
except UnpickleError:
|
|
|
|
|
|
|
|
if not safe:
|
|
|
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
self.created_at = to_date(as_text(obj.get('created_at')))
|
|
|
|
self.created_at = to_date(as_text(obj.get('created_at')))
|
|
|
|
self.origin = as_text(obj.get('origin'))
|
|
|
|
self.origin = as_text(obj.get('origin'))
|
|
|
|
self.description = as_text(obj.get('description'))
|
|
|
|
self.description = as_text(obj.get('description'))
|
|
|
@ -324,8 +330,11 @@ class Job(object):
|
|
|
|
obj = {}
|
|
|
|
obj = {}
|
|
|
|
obj['created_at'] = utcformat(self.created_at or utcnow())
|
|
|
|
obj['created_at'] = utcformat(self.created_at or utcnow())
|
|
|
|
|
|
|
|
|
|
|
|
if self.func_name is not None:
|
|
|
|
if self.data is not None:
|
|
|
|
|
|
|
|
obj['data'] = self.data
|
|
|
|
|
|
|
|
elif self.func_name is not None:
|
|
|
|
obj['data'] = dumps(self.job_tuple)
|
|
|
|
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
|
|
|
|
if self.description is not None:
|
|
|
|
if self.description is not None:
|
|
|
|