Skip to content

Commit 52bf831

Browse files
committed
fixup! Use subprocess's timeout feature instead of reinventing the wheel
1 parent e2d689f commit 52bf831

1 file changed

Lines changed: 13 additions & 6 deletions

File tree

git/cmd.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)