@@ -3452,12 +3452,114 @@ def test_resource_warning(self):
34523452 pool = None
34533453 support .gc_collect ()
34543454
3455+ class CallbackError (Exception ): pass
3456+
3457+ class CallbackBaseException (BaseException ): pass
3458+
34553459def raising ():
34563460 raise KeyError ("key" )
34573461
3462+ def raising_map (x ):
3463+ raise KeyError ("key" )
3464+
3465+ def reraise (exc ):
3466+ raise exc
3467+
3468+ def raise_with_context (exc ):
3469+ try :
3470+ raise ZeroDivisionError
3471+ except ZeroDivisionError :
3472+ raise CallbackError ('callback failed' )
3473+
34583474def unpickleable_result ():
34593475 return lambda : 42
34603476
3477+ class _TestPoolCallbackErrors (BaseTestCase ):
3478+ ALLOWED_TYPES = ('processes' , )
3479+
3480+ @staticmethod
3481+ def _raise (value ):
3482+ raise CallbackError ('callback failed' )
3483+
3484+ @warnings_helper .ignore_fork_in_thread_deprecation_warnings ()
3485+ def test_apply_async_callback_raises (self ):
3486+ with multiprocessing .Pool (1 ) as p :
3487+ res = p .apply_async (sqr , (7 ,), callback = self ._raise )
3488+ with self .assertRaises (CallbackError ):
3489+ res .get (support .SHORT_TIMEOUT )
3490+ # the pool is still usable
3491+ self .assertEqual (p .apply (sqr , (3 ,)), 9 )
3492+ self .assertTrue (p ._result_handler .is_alive ())
3493+
3494+ @warnings_helper .ignore_fork_in_thread_deprecation_warnings ()
3495+ def test_apply_async_callback_raises_base_exception (self ):
3496+ def raise_base (value ):
3497+ raise CallbackBaseException
3498+ with multiprocessing .Pool (1 ) as p :
3499+ res = p .apply_async (sqr , (7 ,), callback = raise_base )
3500+ with self .assertRaises (CallbackBaseException ):
3501+ res .get (support .SHORT_TIMEOUT )
3502+ # the pool did not hang
3503+ self .assertEqual (p .apply (sqr , (3 ,)), 9 )
3504+
3505+ @warnings_helper .ignore_fork_in_thread_deprecation_warnings ()
3506+ def test_apply_async_error_callback_raises (self ):
3507+ with multiprocessing .Pool (1 ) as p :
3508+ res = p .apply_async (raising , error_callback = self ._raise )
3509+ with self .assertRaises (CallbackError ) as cm :
3510+ res .get (support .SHORT_TIMEOUT )
3511+ # the original error is not lost
3512+ self .assertIsInstance (cm .exception .__context__ , KeyError )
3513+ self .assertEqual (p .apply (sqr , (3 ,)), 9 )
3514+
3515+ @warnings_helper .ignore_fork_in_thread_deprecation_warnings ()
3516+ def test_apply_async_error_callback_reraises (self ):
3517+ with multiprocessing .Pool (1 ) as p :
3518+ res = p .apply_async (raising , error_callback = reraise )
3519+ with self .assertRaises (KeyError ) as cm :
3520+ res .get (support .SHORT_TIMEOUT )
3521+ # the error is not its own context
3522+ self .assertIsNone (cm .exception .__context__ )
3523+ self .assertEqual (p .apply (sqr , (3 ,)), 9 )
3524+
3525+ @warnings_helper .ignore_fork_in_thread_deprecation_warnings ()
3526+ def test_map_async_error_callback_reraises (self ):
3527+ with multiprocessing .Pool (1 ) as p :
3528+ res = p .map_async (raising_map , [0 ], error_callback = reraise )
3529+ with self .assertRaises (KeyError ) as cm :
3530+ res .get (support .SHORT_TIMEOUT )
3531+ self .assertIsNone (cm .exception .__context__ )
3532+ self .assertEqual (p .apply (sqr , (3 ,)), 9 )
3533+
3534+ @warnings_helper .ignore_fork_in_thread_deprecation_warnings ()
3535+ def test_apply_async_error_callback_raises_with_context (self ):
3536+ # the original error is kept at the end of the context chain
3537+ with multiprocessing .Pool (1 ) as p :
3538+ res = p .apply_async (raising , error_callback = raise_with_context )
3539+ with self .assertRaises (CallbackError ) as cm :
3540+ res .get (support .SHORT_TIMEOUT )
3541+ context = cm .exception .__context__
3542+ self .assertIsInstance (context , ZeroDivisionError )
3543+ self .assertIsInstance (context .__context__ , KeyError )
3544+ self .assertEqual (p .apply (sqr , (3 ,)), 9 )
3545+
3546+ @warnings_helper .ignore_fork_in_thread_deprecation_warnings ()
3547+ def test_map_async_callback_raises (self ):
3548+ with multiprocessing .Pool (1 ) as p :
3549+ res = p .map_async (sqr , list (range (3 )), callback = self ._raise )
3550+ with self .assertRaises (CallbackError ):
3551+ res .get (support .SHORT_TIMEOUT )
3552+ self .assertEqual (p .apply (sqr , (3 ,)), 9 )
3553+
3554+ @warnings_helper .ignore_fork_in_thread_deprecation_warnings ()
3555+ def test_map_async_error_callback_raises (self ):
3556+ with multiprocessing .Pool (1 ) as p :
3557+ res = p .map_async (raising_map , [0 ], error_callback = self ._raise )
3558+ with self .assertRaises (CallbackError ) as cm :
3559+ res .get (support .SHORT_TIMEOUT )
3560+ self .assertIsInstance (cm .exception .__context__ , KeyError )
3561+ self .assertEqual (p .apply (sqr , (3 ,)), 9 )
3562+
34613563class _TestPoolWorkerErrors (BaseTestCase ):
34623564 ALLOWED_TYPES = ('processes' , )
34633565
0 commit comments