|  | @@ -55,6 +55,21 @@ def soft_timeout_sighandler(signum, frame):
 | 
											
												
													
														|  |      raise SoftTimeLimitExceeded()
 |  |      raise SoftTimeLimitExceeded()
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +class MaybeEncodingError(Exception):
 | 
											
												
													
														|  | 
 |  | +    """Wraps unpickleable object."""
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def __init__(self, exc, value):
 | 
											
												
													
														|  | 
 |  | +        self.exc = str(exc)
 | 
											
												
													
														|  | 
 |  | +        self.value = repr(value)
 | 
											
												
													
														|  | 
 |  | +        super(MaybeEncodingError, self).__init__(self.exc, self.value)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    def __str__(self):
 | 
											
												
													
														|  | 
 |  | +        return "Error sending result: '%s'. Reason: '%s'." % (self.value,
 | 
											
												
													
														|  | 
 |  | +                                                              self.exc)
 | 
											
												
													
														|  | 
 |  | +    def __repr__(self):
 | 
											
												
													
														|  | 
 |  | +        return "<MaybeEncodingError: %s>" % str(self)
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  def worker(inqueue, outqueue, ackqueue, initializer=None, initargs=(),
 |  |  def worker(inqueue, outqueue, ackqueue, initializer=None, initargs=(),
 | 
											
												
													
														|  |          maxtasks=None):
 |  |          maxtasks=None):
 | 
											
												
													
														|  |      assert maxtasks is None or (type(maxtasks) == int and maxtasks > 0)
 |  |      assert maxtasks is None or (type(maxtasks) == int and maxtasks > 0)
 | 
											
										
											
												
													
														|  | @@ -90,7 +105,13 @@ def worker(inqueue, outqueue, ackqueue, initializer=None, initargs=(),
 | 
											
												
													
														|  |              result = (True, func(*args, **kwds))
 |  |              result = (True, func(*args, **kwds))
 | 
											
												
													
														|  |          except Exception, e:
 |  |          except Exception, e:
 | 
											
												
													
														|  |              result = (False, e)
 |  |              result = (False, e)
 | 
											
												
													
														|  | -        put((job, i, result))
 |  | 
 | 
											
												
													
														|  | 
 |  | +        try:
 | 
											
												
													
														|  | 
 |  | +            put((job, i, result))
 | 
											
												
													
														|  | 
 |  | +        except Exception, exc:
 | 
											
												
													
														|  | 
 |  | +            wrapped = MaybeEncodingError(exc, result[1])
 | 
											
												
													
														|  | 
 |  | +            debug('Got possible encoding error while sending result: %s' % wrapped)
 | 
											
												
													
														|  | 
 |  | +            put((job, i, (False, wrapped)))
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |          completed += 1
 |  |          completed += 1
 | 
											
												
													
														|  |      debug('worker exiting after %d tasks' % completed)
 |  |      debug('worker exiting after %d tasks' % completed)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -589,7 +610,7 @@ class Pool(object):
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def apply_async(self, func, args=(), kwds={},
 |  |      def apply_async(self, func, args=(), kwds={},
 | 
											
												
													
														|  |              callback=None, accept_callback=None, timeout_callback=None,
 |  |              callback=None, accept_callback=None, timeout_callback=None,
 | 
											
												
													
														|  | -            waitforslot=False):
 |  | 
 | 
											
												
													
														|  | 
 |  | +            waitforslot=False, error_callback=None):
 | 
											
												
													
														|  |          '''
 |  |          '''
 | 
											
												
													
														|  |          Asynchronous equivalent of `apply()` builtin.
 |  |          Asynchronous equivalent of `apply()` builtin.
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -607,7 +628,8 @@ class Pool(object):
 | 
											
												
													
														|  |          '''
 |  |          '''
 | 
											
												
													
														|  |          assert self._state == RUN
 |  |          assert self._state == RUN
 | 
											
												
													
														|  |          result = ApplyResult(self._cache, callback,
 |  |          result = ApplyResult(self._cache, callback,
 | 
											
												
													
														|  | -                             accept_callback, timeout_callback)
 |  | 
 | 
											
												
													
														|  | 
 |  | +                             accept_callback, timeout_callback,
 | 
											
												
													
														|  | 
 |  | +                             error_callback)
 | 
											
												
													
														|  |          if waitforslot:
 |  |          if waitforslot:
 | 
											
												
													
														|  |              self._putlock.acquire()
 |  |              self._putlock.acquire()
 | 
											
												
													
														|  |          self._taskqueue.put(([(result._job, None, func, args, kwds)], None))
 |  |          self._taskqueue.put(([(result._job, None, func, args, kwds)], None))
 | 
											
										
											
												
													
														|  | @@ -742,7 +764,7 @@ DynamicPool = Pool
 | 
											
												
													
														|  |  class ApplyResult(object):
 |  |  class ApplyResult(object):
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      def __init__(self, cache, callback, accept_callback=None,
 |  |      def __init__(self, cache, callback, accept_callback=None,
 | 
											
												
													
														|  | -            timeout_callback=None):
 |  | 
 | 
											
												
													
														|  | 
 |  | +            timeout_callback=None, error_callback=None):
 | 
											
												
													
														|  |          self._cond = threading.Condition(threading.Lock())
 |  |          self._cond = threading.Condition(threading.Lock())
 | 
											
												
													
														|  |          self._job = job_counter.next()
 |  |          self._job = job_counter.next()
 | 
											
												
													
														|  |          self._cache = cache
 |  |          self._cache = cache
 | 
											
										
											
												
													
														|  | @@ -751,6 +773,7 @@ class ApplyResult(object):
 | 
											
												
													
														|  |          self._time_accepted = None
 |  |          self._time_accepted = None
 | 
											
												
													
														|  |          self._ready = False
 |  |          self._ready = False
 | 
											
												
													
														|  |          self._callback = callback
 |  |          self._callback = callback
 | 
											
												
													
														|  | 
 |  | +        self._errback = error_callback
 | 
											
												
													
														|  |          self._accept_callback = accept_callback
 |  |          self._accept_callback = accept_callback
 | 
											
												
													
														|  |          self._timeout_callback = timeout_callback
 |  |          self._timeout_callback = timeout_callback
 | 
											
												
													
														|  |          cache[self._job] = self
 |  |          cache[self._job] = self
 | 
											
										
											
												
													
														|  | @@ -786,6 +809,8 @@ class ApplyResult(object):
 | 
											
												
													
														|  |          self._success, self._value = obj
 |  |          self._success, self._value = obj
 | 
											
												
													
														|  |          if self._callback and self._success:
 |  |          if self._callback and self._success:
 | 
											
												
													
														|  |              self._callback(self._value)
 |  |              self._callback(self._value)
 | 
											
												
													
														|  | 
 |  | +        if self._errback and not self._success:
 | 
											
												
													
														|  | 
 |  | +            self._errback(self._value)
 | 
											
												
													
														|  |          self._cond.acquire()
 |  |          self._cond.acquire()
 | 
											
												
													
														|  |          try:
 |  |          try:
 | 
											
												
													
														|  |              self._ready = True
 |  |              self._ready = True
 |