@@ -370,10 +370,15 @@ def _terminate(self) -> None:
370370 # Try to kill it.
371371 try :
372372 proc .terminate ()
373- status = proc .wait (timeout = self .timeout ) # Ensure the process goes away .
373+ status = proc .wait (timeout = self .timeout ) # Gracefully wait for the process to terminate .
374374
375375 self .status = self ._status_code_if_terminate or status
376- except (OSError , AttributeError , subprocess .TimeoutExpired ) as ex :
376+ except subprocess .TimeoutExpired as ex :
377+ _logger .warning ("Process did not complete successfully in %s seconds; will forcefully kill" , exc_info = True )
378+ proc .kill ()
379+ status = proc .wait () # Ensure the process goes away by blocking with `timeout=None`.
380+ self .status = self ._status_code_if_terminate or status
381+ except (OSError , AttributeError ) as ex :
377382 # On interpreter shutdown (notably on Windows), parts of the stdlib used by
378383 # subprocess can already be torn down (e.g. `subprocess._winapi` becomes None),
379384 # which can cause AttributeError during terminate(). In that case, we prefer
@@ -389,7 +394,7 @@ def __getattr__(self, attr: str) -> Any:
389394 return getattr (self .proc , attr )
390395
391396 # TODO: Bad choice to mimic `proc.wait()` but with different args.
392- def wait (self , stderr : Union [ None , str , bytes ] = b"" ) -> int :
397+ def wait (self , stderr : Optional [ AnyStr ] = b"" , timeout : Optional [ int , float ] = None ) -> int :
393398 """Wait for the process and return its status code.
394399
395400 :param stderr:
@@ -406,7 +411,8 @@ def wait(self, stderr: Union[None, str, bytes] = b"") -> int:
406411 stderr_b = force_bytes (data = stderr , encoding = "utf-8" )
407412 status : Union [int , None ]
408413 if self .proc is not None :
409- status = self .proc .wait (timeout = self .timeout )
414+ timeout = self .timeout if timeout is None else timeout
415+ status = self .proc .wait (timeout = timeout )
410416 p_stderr = self .proc .stderr
411417 else : # Assume the underlying proc was killed earlier or never existed.
412418 status = self .status
@@ -1325,8 +1331,9 @@ def execute(
13251331 "error: process killed because it timed out. kill_after_timeout=%s seconds" ,
13261332 kill_after_timeout ,
13271333 )
1328- proc .terminate ()
1329- stdout_value , stderr_value = proc .communicate ()
1334+ with contextlib .suppress (subprocess .TimeoutExpired ):
1335+ proc .kill ()
1336+ stdout_value , stderr_value = proc .communicate (timeout = 0.1 )
13301337 if with_exceptions :
13311338 raise GitCommandError (redacted_command , proc .returncode , stderr_value , stdout_value ) from err
13321339 finally :
0 commit comments