@@ -42,6 +42,23 @@ def mapstar(args):
4242# Code run by worker processes
4343#
4444
45+ class MaybeEncodingError (Exception ):
46+ """Wraps possible unpickleable errors, so they can be
47+ safely sent through the socket."""
48+
49+ def __init__ (self , exc , value ):
50+ self .exc = repr (exc )
51+ self .value = repr (value )
52+ super (MaybeEncodingError , self ).__init__ (self .exc , self .value )
53+
54+ def __str__ (self ):
55+ return "Error sending result: '%s'. Reason: '%s'" % (self .value ,
56+ self .exc )
57+
58+ def __repr__ (self ):
59+ return "<MaybeEncodingError: %s>" % str (self )
60+
61+
4562def worker (inqueue , outqueue , initializer = None , initargs = (), maxtasks = None ):
4663 assert maxtasks is None or (type (maxtasks ) == int and maxtasks > 0 )
4764 put = outqueue .put
@@ -70,7 +87,13 @@ def worker(inqueue, outqueue, initializer=None, initargs=(), maxtasks=None):
7087 result = (True , func (* args , ** kwds ))
7188 except Exception as e :
7289 result = (False , e )
73- put ((job , i , result ))
90+ try :
91+ put ((job , i , result ))
92+ except Exception as e :
93+ wrapped = MaybeEncodingError (e , result [1 ])
94+ debug ("Possible encoding error while sending result: %s" % (
95+ wrapped ))
96+ put ((job , i , (False , wrapped )))
7497 completed += 1
7598 debug ('worker exiting after %d tasks' % completed )
7699
@@ -235,16 +258,18 @@ def imap_unordered(self, func, iterable, chunksize=1):
235258 for i , x in enumerate (task_batches )), result ._set_length ))
236259 return (item for chunk in result for item in chunk )
237260
238- def apply_async (self , func , args = (), kwds = {}, callback = None ):
261+ def apply_async (self , func , args = (), kwds = {}, callback = None ,
262+ error_callback = None ):
239263 '''
240264 Asynchronous version of `apply()` method.
241265 '''
242266 assert self ._state == RUN
243- result = ApplyResult (self ._cache , callback )
267+ result = ApplyResult (self ._cache , callback , error_callback )
244268 self ._taskqueue .put (([(result ._job , None , func , args , kwds )], None ))
245269 return result
246270
247- def map_async (self , func , iterable , chunksize = None , callback = None ):
271+ def map_async (self , func , iterable , chunksize = None , callback = None ,
272+ error_callback = None ):
248273 '''
249274 Asynchronous version of `map()` method.
250275 '''
@@ -260,7 +285,8 @@ def map_async(self, func, iterable, chunksize=None, callback=None):
260285 chunksize = 0
261286
262287 task_batches = Pool ._get_tasks (func , iterable , chunksize )
263- result = MapResult (self ._cache , chunksize , len (iterable ), callback )
288+ result = MapResult (self ._cache , chunksize , len (iterable ), callback ,
289+ error_callback = error_callback )
264290 self ._taskqueue .put ((((result ._job , i , mapstar , (x ,), {})
265291 for i , x in enumerate (task_batches )), None ))
266292 return result
@@ -459,12 +485,13 @@ def _terminate_pool(cls, taskqueue, inqueue, outqueue, pool,
459485
460486class ApplyResult (object ):
461487
462- def __init__ (self , cache , callback ):
488+ def __init__ (self , cache , callback , error_callback ):
463489 self ._cond = threading .Condition (threading .Lock ())
464490 self ._job = next (job_counter )
465491 self ._cache = cache
466492 self ._ready = False
467493 self ._callback = callback
494+ self ._error_callback = error_callback
468495 cache [self ._job ] = self
469496
470497 def ready (self ):
@@ -495,6 +522,8 @@ def _set(self, i, obj):
495522 self ._success , self ._value = obj
496523 if self ._callback and self ._success :
497524 self ._callback (self ._value )
525+ if self ._error_callback and not self ._success :
526+ self ._error_callback (self ._value )
498527 self ._cond .acquire ()
499528 try :
500529 self ._ready = True
@@ -509,8 +538,9 @@ def _set(self, i, obj):
509538
510539class MapResult (ApplyResult ):
511540
512- def __init__ (self , cache , chunksize , length , callback ):
513- ApplyResult .__init__ (self , cache , callback )
541+ def __init__ (self , cache , chunksize , length , callback , error_callback ):
542+ ApplyResult .__init__ (self , cache , callback ,
543+ error_callback = error_callback )
514544 self ._success = True
515545 self ._value = [None ] * length
516546 self ._chunksize = chunksize
@@ -535,10 +565,11 @@ def _set(self, i, success_result):
535565 self ._cond .notify ()
536566 finally :
537567 self ._cond .release ()
538-
539568 else :
540569 self ._success = False
541570 self ._value = result
571+ if self ._error_callback :
572+ self ._error_callback (self ._value )
542573 del self ._cache [self ._job ]
543574 self ._cond .acquire ()
544575 try :
0 commit comments