1212
1313from test import support
1414from test .support import hashlib_helper
15+ from test .test_importlib .metadata .fixtures import parameterize
1516
1617from .executor import ExecutorTest , mul
1718from .util import (
@@ -26,6 +27,11 @@ def __init__(self, mgr):
2627 def __del__ (self ):
2728 self .event .set ()
2829
30+ TERMINATE_OR_KILL_PARAMS = [
31+ dict (function_name = 'terminate_workers' ),
32+ dict (function_name = 'kill_workers' ),
33+ ]
34+
2935def _put_sleep_put (queue ):
3036 """ Used as part of test_process_pool_executor_terminate_workers """
3137 queue .put ('started' )
@@ -228,80 +234,80 @@ def mock_start_new_thread(func, *args, **kwargs):
228234 list (executor .map (mul , [(2 , 3 )] * 10 ))
229235 executor .shutdown ()
230236
231- def test_process_pool_executor_terminate_kill_workers (self ):
232- for function_name in ('terminate_workers' , 'kill_workers' ):
233- with self .subTest (function_name ):
234- manager = multiprocessing .Manager ()
235- q = manager .Queue ()
236-
237- with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
238- executor .submit (_put_sleep_put , q )
239-
240- # We should get started, but not finished since we'll terminate the workers just after
241- self .assertEqual (q .get (timeout = 5 ), 'started' )
237+ def test_process_pool_executor_terminate_workers (self ):
238+ with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
239+ executor ._terminate_or_kill_workers = unittest .mock .Mock ()
240+ executor .terminate_workers ()
242241
243- worker_process = list (executor ._processes .values ())[0 ]
244- getattr (executor , function_name )()
245- worker_process .join ()
242+ executor ._terminate_or_kill_workers .assert_called_once_with (operation = futures .process ._TERMINATE )
246243
247- if function_name == 'terminate_workers' or sys .platform == 'win32' :
248- # On windows, kill and terminate both send SIGTERM
249- self .assertEqual (worker_process .exitcode , - signal .SIGTERM )
250- elif function_name == 'kill_workers' :
251- self .assertEqual (worker_process .exitcode , - signal .SIGKILL )
244+ def test_process_pool_executor_kill_workers (self ):
245+ with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
246+ executor ._terminate_or_kill_workers = unittest .mock .Mock ()
247+ executor .kill_workers ()
252248
253- self . assertRaises ( queue . Empty , q . get , timeout = 1 )
249+ executor . _terminate_or_kill_workers . assert_called_once_with ( operation = futures . process . _KILL )
254250
255- def test_process_pool_executor_terminate_kill_workers_dead_workers (self ):
256- for function_name in ('terminate_workers' , 'kill_workers' ):
257- with self .subTest (function_name = function_name ):
258- with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
259- future = executor .submit (os ._exit , 1 )
260- self .assertRaises (BrokenProcessPool , future .result )
251+ def test_process_pool_executor_terminate_or_kill_workers_invalid_op (self ):
252+ with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
253+ self .assertRaises (ValueError ,
254+ executor ._terminate_or_kill_workers ,
255+ operation = 'invalid operation' ),
261256
262- # even though the pool is broken, this shouldn't raise
263- getattr (executor , function_name )()
257+ @parameterize (* TERMINATE_OR_KILL_PARAMS )
258+ def test_process_pool_executor_terminate_kill_workers (self , function_name ):
259+ manager = multiprocessing .Manager ()
260+ q = manager .Queue ()
264261
265- def test_process_pool_executor_terminate_kill_workers_not_started_yet (self ):
266- for function_name in ('terminate_workers' , 'kill_workers' ):
267- with self .subTest (function_name = function_name ):
268- context_with_mocked_process = multiprocessing .get_context ()
269- with unittest .mock .patch .object (context_with_mocked_process , 'Process' ) as mock_process :
270- with futures .ProcessPoolExecutor (max_workers = 1 , mp_context = context_with_mocked_process ) as executor :
271- # The worker has not been started yet, terminate/kill_workers should basically no-op
272- getattr (executor , function_name )()
262+ with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
263+ executor .submit (_put_sleep_put , q )
273264
274- mock_process .return_value .kill .assert_not_called ()
275- mock_process .return_value .terminate .assert_not_called ()
265+ # We should get started, but not finished since we'll terminate the
266+ # workers just after
267+ self .assertEqual (q .get (timeout = 5 ), 'started' )
276268
277- def test_process_pool_executor_terminate_kill_workers_stops_pool (self ):
278- for function_name in ('terminate_workers' , 'kill_workers' ):
279- with self .subTest (function_name = function_name ):
280- with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
281- executor .submit (time .sleep , 0 ).result ()
269+ worker_process = list (executor ._processes .values ())[0 ]
270+ getattr (executor , function_name )()
271+ worker_process .join ()
282272
283- getattr (executor , function_name )()
273+ if function_name == 'terminate_workers' or \
274+ sys .platform == 'win32' :
275+ # On windows, kill and terminate both send SIGTERM
276+ self .assertEqual (worker_process .exitcode , - signal .SIGTERM )
277+ elif function_name == 'kill_workers' :
278+ self .assertEqual (worker_process .exitcode , - signal .SIGKILL )
284279
285- self .assertRaises (RuntimeError , executor . submit , time . sleep , 0 )
280+ self .assertRaises (queue . Empty , q . get , timeout = 1 )
286281
287- def test_process_pool_executor_terminate_workers (self ):
282+ @parameterize (* TERMINATE_OR_KILL_PARAMS )
283+ def test_process_pool_executor_terminate_kill_workers_dead_workers (self , function_name ):
288284 with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
289- executor ._terminate_or_kill_workers = unittest .mock .Mock ()
290- executor .terminate_workers ()
291-
292- executor ._terminate_or_kill_workers .assert_called_once_with (operation = futures .process ._TerminateOrKillOperation .TERMINATE )
293-
294- def test_process_pool_executor_kill_workers (self ):
285+ future = executor .submit (os ._exit , 1 )
286+ self .assertRaises (BrokenProcessPool , future .result )
287+
288+ # even though the pool is broken, this shouldn't raise
289+ getattr (executor , function_name )()
290+
291+ @parameterize (* TERMINATE_OR_KILL_PARAMS )
292+ def test_process_pool_executor_terminate_kill_workers_not_started_yet (self , function_name ):
293+ ctx = self .get_context ()
294+ with unittest .mock .patch .object (ctx , 'Process' ) as mock_process :
295+ with futures .ProcessPoolExecutor (max_workers = 1 , mp_context = ctx ) as executor :
296+ # The worker has not been started yet, terminate/kill_workers
297+ # should basically no-op
298+ getattr (executor , function_name )()
299+
300+ mock_process .return_value .kill .assert_not_called ()
301+ mock_process .return_value .terminate .assert_not_called ()
302+
303+ @parameterize (* TERMINATE_OR_KILL_PARAMS )
304+ def test_process_pool_executor_terminate_kill_workers_stops_pool (self , function_name ):
295305 with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
296- executor ._terminate_or_kill_workers = unittest .mock .Mock ()
297- executor .kill_workers ()
306+ executor .submit (time .sleep , 0 ).result ()
298307
299- executor ._terminate_or_kill_workers .assert_called_once_with (operation = futures .process ._TerminateOrKillOperation .KILL )
300-
301- def test_process_pool_executor_terminate_or_kill_workers_invalid_operation (self ):
302- with futures .ProcessPoolExecutor (max_workers = 1 ) as executor :
303- self .assertRaises (ValueError , executor ._terminate_or_kill_workers , operation = 'invalid operation' ),
308+ getattr (executor , function_name )()
304309
310+ self .assertRaises (RuntimeError , executor .submit , time .sleep , 0 )
305311
306312
307313create_executor_tests (globals (), ProcessPoolExecutorTest ,
0 commit comments