Skip to content

Commit 333fe0e

Browse files
committed
fixup! Use subprocess's timeout feature instead of reinventing the wheel
1 parent e33c84f commit 333fe0e

3 files changed

Lines changed: 21 additions & 22 deletions

File tree

git/cmd.py

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def handle_process_output(
138138
- decoding must happen later, such as for :class:`~git.diff.Diff`\s.
139139
140140
:param kill_after_timeout:
141-
:class:`int`, `float`, or ``None`` (block indefinitely), Default = ``None``.
141+
:class:``int``, ``float``, or ``None`` (block indefinitely), Default = ``None``.
142142
143143
To specify a timeout in seconds for the git command, after which the process
144144
should be killed.
@@ -373,7 +373,7 @@ def _terminate(self) -> None:
373373
status = proc.wait(timeout=self.timeout) # Ensure the process goes away.
374374

375375
self.status = self._status_code_if_terminate or status
376-
except (OSError, AttributeError) as ex:
376+
except (OSError, AttributeError, subprocess.TimeoutExpired) as ex:
377377
# On interpreter shutdown (notably on Windows), parts of the stdlib used by
378378
# subprocess can already be torn down (e.g. `subprocess._winapi` becomes None),
379379
# which can cause AttributeError during terminate(). In that case, we prefer
@@ -1111,7 +1111,7 @@ def execute(
11111111
as_process: bool = False,
11121112
output_stream: Union[None, BinaryIO] = None,
11131113
stdout_as_string: bool = True,
1114-
kill_after_timeout: Union[None, float] = None,
1114+
kill_after_timeout: float | int | None = None,
11151115
with_stdout: bool = True,
11161116
universal_newlines: bool = False,
11171117
shell: Union[None, bool] = None,
@@ -1163,13 +1163,12 @@ def execute(
11631163
until the timeout is explicitly specified. Uses of this feature should be
11641164
carefully considered, due to the following limitations:
11651165
1166-
1. This feature is not supported at all on Windows.
1167-
2. Effectiveness may vary by operating system. ``ps --ppid`` is used to
1166+
1. Effectiveness may vary by operating system. ``ps --ppid`` is used to
11681167
enumerate child processes, which is available on most GNU/Linux systems
11691168
but not most others.
1170-
3. Deeper descendants do not receive signals, though they may sometimes
1169+
2. Deeper descendants do not receive signals, though they may sometimes
11711170
terminate as a consequence of their parent processes being killed.
1172-
4. `kill_after_timeout` uses ``SIGKILL``, which can have negative side
1171+
3. `kill_after_timeout` uses ``SIGKILL``, which can have negative side
11731172
effects on a repository. For example, stale locks in case of
11741173
:manpage:`git-gc(1)` could render the repository incapable of accepting
11751174
changes until the lock is manually removed.
@@ -1257,15 +1256,7 @@ def execute(
12571256
if inline_env is not None:
12581257
env.update(inline_env)
12591258

1260-
if sys.platform == "win32":
1261-
if kill_after_timeout is not None:
1262-
raise GitCommandError(
1263-
redacted_command,
1264-
'"kill_after_timeout" feature is not supported on Windows.',
1265-
)
1266-
cmd_not_found_exception = OSError
1267-
else:
1268-
cmd_not_found_exception = FileNotFoundError
1259+
cmd_not_found_exception = OSError if sys.platform == "win32" else FileNotFoundError
12691260
# END handle
12701261

12711262
stdout_sink = PIPE if with_stdout else getattr(subprocess, "DEVNULL", None) or open(os.devnull, "wb")
@@ -1335,6 +1326,12 @@ def execute(
13351326
stderr_value = stderr_value[:-1]
13361327
status = proc.wait(timeout=kill_after_timeout)
13371328
# END stdout handling
1329+
except subprocess.TimeoutExpired as err:
1330+
_logger.info(
1331+
"error: process killed because it timed out. kill_after_timeout=%s seconds", kill_after_timeout,
1332+
)
1333+
if with_exceptions:
1334+
raise GitCommandError(redacted_command, status or 255, err.stderr, err.stdout) from err
13381335
finally:
13391336
if proc.stdout is not None:
13401337
proc.stdout.close()

git/remote.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
"""Module implementing a remote object allowing easy access to git remotes."""
77

8+
from __future__ import annotations
9+
810
__all__ = ["RemoteProgress", "PushInfo", "FetchInfo", "Remote"]
911

1012
import contextlib
@@ -873,7 +875,7 @@ def _get_fetch_info_from_stderr(
873875
self,
874876
proc: "Git.AutoInterrupt",
875877
progress: Union[Callable[..., Any], RemoteProgress, None],
876-
kill_after_timeout: Union[None, float] = None,
878+
kill_after_timeout: float | int | None = None,
877879
) -> IterableList["FetchInfo"]:
878880
progress = to_progress_instance(progress)
879881

@@ -944,7 +946,7 @@ def _get_push_info(
944946
self,
945947
proc: "Git.AutoInterrupt",
946948
progress: Union[Callable[..., Any], RemoteProgress, None],
947-
kill_after_timeout: Union[None, float] = None,
949+
kill_after_timeout: float | int | None = None,
948950
) -> PushInfoList:
949951
progress = to_progress_instance(progress)
950952

@@ -1002,7 +1004,7 @@ def fetch(
10021004
refspec: Union[str, List[str], None] = None,
10031005
progress: Union[RemoteProgress, None, "UpdateProgress"] = None,
10041006
verbose: bool = True,
1005-
kill_after_timeout: Union[None, float] = None,
1007+
kill_after_timeout: float | int | None = None,
10061008
allow_unsafe_protocols: bool = False,
10071009
allow_unsafe_options: bool = False,
10081010
**kwargs: Any,
@@ -1082,7 +1084,7 @@ def pull(
10821084
self,
10831085
refspec: Union[str, List[str], None] = None,
10841086
progress: Union[RemoteProgress, "UpdateProgress", None] = None,
1085-
kill_after_timeout: Union[None, float] = None,
1087+
kill_after_timeout: float | int | None = None,
10861088
allow_unsafe_protocols: bool = False,
10871089
allow_unsafe_options: bool = False,
10881090
**kwargs: Any,
@@ -1136,7 +1138,7 @@ def push(
11361138
self,
11371139
refspec: Union[str, List[str], None] = None,
11381140
progress: Union[RemoteProgress, "UpdateProgress", Callable[..., RemoteProgress], None] = None,
1139-
kill_after_timeout: Union[None, float] = None,
1141+
kill_after_timeout: float | int | None = None,
11401142
allow_unsafe_protocols: bool = False,
11411143
allow_unsafe_options: bool = False,
11421144
**kwargs: Any,

test/test_remote.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ def _assert_push_and_pull(self, remote, rw_repo, remote_repo):
434434
TagReference.delete(rw_repo, new_tag, other_tag)
435435
remote.push(":%s" % other_tag.path, kill_after_timeout=10.0)
436436

437-
@skipIf(HIDE_WINDOWS_FREEZE_ERRORS, "FIXME: Freezes!")
437+
#@skipIf(HIDE_WINDOWS_FREEZE_ERRORS, "FIXME: Freezes!")
438438
@with_rw_and_rw_remote_repo("0.1.6")
439439
def test_base(self, rw_repo, remote_repo):
440440
num_remotes = 0

0 commit comments

Comments
 (0)