@@ -1297,36 +1297,70 @@ def print_time():
12971297 return print_time
12981298
12991299
1300- def wait_for_process_to_die (pid ) -> None :
1300+ def wait_for_process_to_die (pid : int ) -> None :
13011301 """
1302- Wait for specified process to die, or alternatively kill it
1303- NOTE: This function operates best with psutil pypi package
1302+ Wait for the specified process to die.
1303+
13041304 TODO: Add timeout which raises exception
13051305 """
13061306 # wait for the process to fully killed
13071307 try :
13081308 import psutil # pylint: disable=import-outside-toplevel
13091309 while True :
1310+ # TODO: this should use psutil.process_exists() or psutil.Process.wait()
1311+ # The psutil docs explicitly recommend against using process_iter()/pids()
1312+ # for checking the existence of a process.
13101313 if pid not in [proc .pid for proc in psutil .process_iter ()]:
13111314 break
13121315 time .sleep (0.1 )
13131316 except ImportError :
13141317 # if psutil is not installed we can do this the hard way
1315- while True :
1316- if sys .platform == 'win32' :
1317- import ctypes # pylint: disable=import-outside-toplevel
1318- PROCESS_QUERY_INFORMATION = 0x1000
1319- processHandle = ctypes .windll .kernel32 .OpenProcess (PROCESS_QUERY_INFORMATION , 0 , pid )
1320- if processHandle == 0 :
1321- break
1322- ctypes .windll .kernel32 .CloseHandle (processHandle )
1323- time .sleep (0.1 )
1324- else :
1325- try :
1326- os .kill (pid , 0 )
1327- except OSError :
1328- break
1329- time .sleep (0.1 )
1318+ _wait_for_process_to_die_non_psutil (pid , timeout = - 1.0 )
1319+
1320+
1321+ def _wait_for_process_to_die_non_psutil (pid : int , timeout : float = 60.0 ) -> None :
1322+ start_time = time .time ()
1323+ while True :
1324+ if not _is_process_alive (pid ):
1325+ break
1326+ if timeout >= 0.0 and time .time () - start_time > timeout :
1327+ raise TimeoutError (f"timed out waiting for process { pid } " )
1328+ time .sleep (0.1 )
1329+
1330+
1331+ if sys .platform == 'win32' :
1332+ def _is_process_alive (pid : int ) -> bool :
1333+ import ctypes # pylint: disable=import-outside-toplevel
1334+ PROCESS_QUERY_INFORMATION = 0x1000
1335+ STILL_ACTIVE = 259
1336+
1337+ processHandle = ctypes .windll .kernel32 .OpenProcess (PROCESS_QUERY_INFORMATION , 0 , pid )
1338+ if processHandle == 0 :
1339+ return False
1340+
1341+ # OpenProcess() may successfully return a handle even for terminated
1342+ # processes when something else in the system is still holding a
1343+ # reference to their handle. Call GetExitCodeProcess() to check if the
1344+ # process has already exited.
1345+ try :
1346+ exit_code = ctypes .c_ulong ()
1347+ success = ctypes .windll .kernel32 .GetExitCodeProcess (
1348+ processHandle , ctypes .byref (exit_code ))
1349+ if success :
1350+ return exit_code .value == STILL_ACTIVE
1351+ finally :
1352+ ctypes .windll .kernel32 .CloseHandle (processHandle )
1353+
1354+ return True
1355+
1356+ else :
1357+ def _is_process_alive (pid : int ) -> bool :
1358+ try :
1359+ os .kill (pid , 0 )
1360+ return True
1361+ except OSError :
1362+ return False
1363+
13301364
13311365# From: https://stackoverflow.com/questions/1741972/how-to-use-different-formatters-with-the-same-logging-handler-in-python
13321366class DispatchingFormatter (Formatter ):
0 commit comments