@@ -3400,3 +3400,62 @@ def __exit__(self, *exc_info):
34003400 del self .exc_value
34013401 del self .exc_traceback
34023402 del self .thread
3403+
3404+
3405+ def wait_process (pid , * , exitcode , timeout = None ):
3406+ """
3407+ Wait until process pid completes and check that the process exit code is
3408+ exitcode.
3409+
3410+ Raise an AssertionError if the process exit code is not equal to exitcode.
3411+
3412+ If the process runs longer than timeout seconds (SHORT_TIMEOUT by default),
3413+ kill the process (if signal.SIGKILL is available) and raise an
3414+ AssertionError. The timeout feature is not available on Windows.
3415+ """
3416+ if os .name != "nt" :
3417+ if timeout is None :
3418+ timeout = SHORT_TIMEOUT
3419+ t0 = time .monotonic ()
3420+ deadline = t0 + timeout
3421+ sleep = 0.001
3422+ max_sleep = 0.1
3423+ while True :
3424+ pid2 , status = os .waitpid (pid , os .WNOHANG )
3425+ if pid2 != 0 :
3426+ break
3427+ # process is still running
3428+
3429+ dt = time .monotonic () - t0
3430+ if dt > SHORT_TIMEOUT :
3431+ try :
3432+ os .kill (pid , signal .SIGKILL )
3433+ os .waitpid (pid , 0 )
3434+ except OSError :
3435+ # Ignore errors like ChildProcessError or PermissionError
3436+ pass
3437+
3438+ raise AssertionError (f"process { pid } is still running "
3439+ f"after { dt :.1f} seconds" )
3440+
3441+ sleep = min (sleep * 2 , max_sleep )
3442+ time .sleep (sleep )
3443+
3444+ if os .WIFEXITED (status ):
3445+ exitcode2 = os .WEXITSTATUS (status )
3446+ elif os .WIFSIGNALED (status ):
3447+ exitcode2 = - os .WTERMSIG (status )
3448+ else :
3449+ raise ValueError (f"invalid wait status: { status !r} " )
3450+ else :
3451+ # Windows implementation
3452+ pid2 , status = os .waitpid (pid , 0 )
3453+ exitcode2 = (status >> 8 )
3454+
3455+ if exitcode2 != exitcode :
3456+ raise AssertionError (f"process { pid } exited with code { exitcode2 } , "
3457+ f"but exit code { exitcode } is expected" )
3458+
3459+ # sanity check: it should not fail in practice
3460+ if pid2 != pid :
3461+ raise AssertionError (f"pid { pid2 } != pid { pid } " )
0 commit comments