1212import tempfile
1313import threading
1414import uuid
15- from typing import Any , Dict , List , Optional , Tuple
15+ from typing import Any , Dict , List , Optional , Tuple , Union
1616
1717if sys .platform == "win32" :
1818 from namedpipe import NPopen # pylint: disable=import-error # cspell: disable-line
2828TEST_DATA_PATH = pathlib .Path (__file__ ).parent / ".data"
2929CONTENT_LENGTH : str = "Content-Length:"
3030CONTENT_TYPE : str = "Content-Type:"
31+ PIPE_RESULT_TIMEOUT_SECONDS = 10
32+ TEST_SUBPROCESS_TIMEOUT_SECONDS = 300
3133
3234
3335@contextlib .contextmanager
@@ -242,10 +244,36 @@ def _listen_on_pipe_new(listener, result: List[str], completed: threading.Event)
242244 result .append ("" .join (all_data ))
243245
244246
245- def _run_test_code (proc_args : List [str ], proc_env , proc_cwd : str , completed : threading .Event ):
246- result = subprocess .run (proc_args , env = proc_env , cwd = proc_cwd , check = False )
247- completed .set ()
248- return result
247+ def _run_test_code (
248+ proc_args : List [str ],
249+ proc_env ,
250+ proc_cwd : Union [str , os .PathLike [str ]],
251+ completed : threading .Event ,
252+ ) -> subprocess .CompletedProcess :
253+ try :
254+ return subprocess .run (
255+ proc_args ,
256+ env = proc_env ,
257+ cwd = proc_cwd ,
258+ check = False ,
259+ timeout = TEST_SUBPROCESS_TIMEOUT_SECONDS ,
260+ )
261+ finally :
262+ completed .set ()
263+
264+
265+ def _wait_for_pipe_result (
266+ listener_thread : threading .Thread ,
267+ process_result : subprocess .CompletedProcess ,
268+ result : List [str ],
269+ ) -> None :
270+ listener_thread .join (timeout = PIPE_RESULT_TIMEOUT_SECONDS )
271+ if listener_thread .is_alive ():
272+ if process_result .returncode :
273+ raise subprocess .CalledProcessError (process_result .returncode , process_result .args )
274+ raise TimeoutError ("Timed out waiting for the test subprocess pipe result" )
275+ if process_result .returncode and not result :
276+ raise subprocess .CalledProcessError (process_result .returncode , process_result .args )
249277
250278
251279def runner (args : List [str ]) -> Optional [List [Dict [str , Any ]]]:
@@ -359,18 +387,12 @@ def runner_with_cwd_env(
359387
360388 result = [] # result is a string array to store the data during threading
361389 t1 : threading .Thread = threading .Thread (
362- target = _listen_on_pipe_new , args = (pipe , result , completed )
390+ target = _listen_on_pipe_new , args = (pipe , result , completed ), daemon = True
363391 )
364392 t1 .start ()
365393
366- t2 = threading .Thread (
367- target = _run_test_code ,
368- args = (process_args , env , path , completed ),
369- )
370- t2 .start ()
371-
372- t1 .join ()
373- t2 .join ()
394+ process_result = _run_test_code (process_args , env , path , completed )
395+ _wait_for_pipe_result (t1 , process_result , result )
374396
375397 return process_data_received (result [0 ]) if result else None
376398 else : # Unix design
@@ -397,19 +419,12 @@ def runner_with_cwd_env(
397419
398420 result = [] # result is a string array to store the data during threading
399421 t1 : threading .Thread = threading .Thread (
400- target = _listen_on_fifo , args = (pipe_name , result , completed )
422+ target = _listen_on_fifo , args = (pipe_name , result , completed ), daemon = True
401423 )
402424 t1 .start ()
403425
404- t2 : threading .Thread = threading .Thread (
405- target = _run_test_code ,
406- args = (process_args , env , path , completed ),
407- )
408-
409- t2 .start ()
410-
411- t1 .join ()
412- t2 .join ()
426+ process_result = _run_test_code (process_args , env , path , completed )
427+ _wait_for_pipe_result (t1 , process_result , result )
413428
414429 return process_data_received (result [0 ]) if result else None
415430
0 commit comments