| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 6fc4742 commit 1435486
1 file changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,7 +13,6 @@ | |||
| 13 | 13 | import logging | |
| 14 | 14 | import os | |
| 15 | 15 | import re | |
| 16 | - import signal | ||
| 17 | 16 | import subprocess | |
| 18 | 17 | from subprocess import DEVNULL, PIPE, Popen | |
| 19 | 18 | import sys | |
@@ -110,7 +109,7 @@ def handle_process_output( | |||
| 110 | 109 | stderr_handler: Union[None, Callable[[AnyStr], None], Callable[[List[AnyStr]], None]], | |
| 111 | 110 | finalizer: Union[None, Callable[[Union[Popen, "Git.AutoInterrupt"]], None]] = None, | |
| 112 | 111 | decode_streams: bool = True, | |
| 113 | - kill_after_timeout: Union[None, float] = None, | ||
| 112 | + kill_after_timeout: float | int | None = None, | ||
| 114 | 113 | ) -> None: | |
| 115 | 114 | R"""Register for notifications to learn that process output is ready to read, and | |
| 116 | 115 | dispatch lines to the respective line handlers. | |
@@ -139,7 +138,7 @@ def handle_process_output( | |||
| 139 | 138 | - decoding must happen later, such as for :class:`~git.diff.Diff`\s. | |
| 140 | 139 | ||
| 141 | 140 | :param kill_after_timeout: | |
| 142 | - :class:`float` or ``None``, Default = ``None`` | ||
| 141 | + :class:`int`, `float`, or ``None`` (block indefinitely), Default = ``None``. | ||
| 143 | 142 | ||
| 144 | 143 | To specify a timeout in seconds for the git command, after which the process | |
| 145 | 144 | should be killed. | |
@@ -326,16 +325,22 @@ class _AutoInterrupt: | |||
| 326 | 325 | raise. | |
| 327 | 326 | """ | |
| 328 | 327 | ||
| 329 | - __slots__ = ("proc", "args", "status") | ||
| 328 | + __slots__ = ("proc", "args", "status", "timeout") | ||
| 330 | 329 | ||
| 331 | 330 | # If this is non-zero it will override any status code during _terminate, used | |
| 332 | 331 | # to prevent race conditions in testing. | |
| 333 | 332 | _status_code_if_terminate: int = 0 | |
| 334 | 333 | ||
| 335 | - def __init__(self, proc: Union[None, subprocess.Popen], args: Any) -> None: | ||
| 334 | + def __init__( | ||
| 335 | + self, | ||
| 336 | + proc: subprocess.Popen | None, | ||
| 337 | + args: Any, | ||
| 338 | + timeout: float | int | None = None, | ||
| 339 | + ) -> None: | ||
| 336 | 340 | self.proc = proc | |
| 337 | 341 | self.args = args | |
| 338 | 342 | self.status: Union[int, None] = None | |
| 343 | + self.timeout = timeout | ||
| 339 | 344 | ||
| 340 | 345 | def _terminate(self) -> None: | |
| 341 | 346 | """Terminate the underlying process.""" | |
@@ -365,7 +370,7 @@ def _terminate(self) -> None: | |||
| 365 | 370 | # Try to kill it. | |
| 366 | 371 | try: | |
| 367 | 372 | proc.terminate() | |
| 368 | - status = proc.wait() # Ensure the process goes away. | ||
| 373 | + status = proc.wait(timeout=self.timeout) # Ensure the process goes away. | ||
| 369 | 374 | ||
| 370 | 375 | self.status = self._status_code_if_terminate or status | |
| 371 | 376 | except (OSError, AttributeError) as ex: | |
@@ -400,7 +405,7 @@ def wait(self, stderr: Union[None, str, bytes] = b"") -> int: | |||
| 400 | 405 | stderr_b = force_bytes(data=stderr, encoding="utf-8") | |
| 401 | 406 | status: Union[int, None] | |
| 402 | 407 | if self.proc is not None: | |
| 403 | - status = self.proc.wait() | ||
| 408 | + status = self.proc.wait(timeout=self.timeout) | ||
| 404 | 409 | p_stderr = self.proc.stderr | |
| 405 | 410 | else: # Assume the underlying proc was killed earlier or never existed. | |
| 406 | 411 | status = self.status | |
@@ -1303,66 +1308,14 @@ def execute( | |||
| 1303 | 1308 | if as_process: | |
| 1304 | 1309 | return self.AutoInterrupt(proc, command) | |
| 1305 | 1310 | ||
| 1306 | - if sys.platform != "win32" and kill_after_timeout is not None: | ||
| 1307 | - # Help mypy figure out this is not None even when used inside communicate(). | ||
| 1308 | - timeout = kill_after_timeout | ||
| 1309 | - | ||
| 1310 | - def kill_process(pid: int) -> None: | ||
| 1311 | - """Callback to kill a process. | ||
| 1312 | - | ||
| 1313 | - This callback implementation would be ineffective and unsafe on Windows. | ||
| 1314 | - """ | ||
| 1315 | - p = Popen(["ps", "--ppid", str(pid)], stdout=PIPE) | ||
| 1316 | - child_pids = [] | ||
| 1317 | - if p.stdout is not None: | ||
| 1318 | - for line in p.stdout: | ||
| 1319 | - if len(line.split()) > 0: | ||
| 1320 | - local_pid = (line.split())[0] | ||
| 1321 | - if local_pid.isdigit(): | ||
| 1322 | - child_pids.append(int(local_pid)) | ||
| 1323 | - try: | ||
| 1324 | - os.kill(pid, signal.SIGKILL) | ||
| 1325 | - for child_pid in child_pids: | ||
| 1326 | - try: | ||
| 1327 | - os.kill(child_pid, signal.SIGKILL) | ||
| 1328 | - except OSError: | ||
| 1329 | - pass | ||
| 1330 | - # Tell the main routine that the process was killed. | ||
| 1331 | - kill_check.set() | ||
| 1332 | - except OSError: | ||
| 1333 | - # It is possible that the process gets completed in the duration | ||
| 1334 | - # after timeout happens and before we try to kill the process. | ||
| 1335 | - pass | ||
| 1336 | - return | ||
| 1337 | - | ||
| 1338 | - def communicate() -> Tuple[AnyStr, AnyStr]: | ||
| 1339 | - watchdog.start() | ||
| 1340 | - out, err = proc.communicate() | ||
| 1341 | - watchdog.cancel() | ||
| 1342 | - if kill_check.is_set(): | ||
| 1343 | - err = 'Timeout: the command "%s" did not complete in %d secs.' % ( | ||
| 1344 | - " ".join(redacted_command), | ||
| 1345 | - timeout, | ||
| 1346 | - ) | ||
| 1347 | - if not universal_newlines: | ||
| 1348 | - err = err.encode(defenc) | ||
| 1349 | - return out, err | ||
| 1350 | - | ||
| 1351 | - # END helpers | ||
| 1352 | - | ||
| 1353 | - kill_check = threading.Event() | ||
| 1354 | - watchdog = threading.Timer(timeout, kill_process, args=(proc.pid,)) | ||
| 1355 | - else: | ||
| 1356 | - communicate = proc.communicate | ||
| 1357 | - | ||
| 1358 | 1311 | # Wait for the process to return. | |
| 1359 | 1312 | status = 0 | |
| 1360 | 1313 | stdout_value: Union[str, bytes] = b"" | |
| 1361 | 1314 | stderr_value: Union[str, bytes] = b"" | |
| 1362 | 1315 | newline = "\n" if universal_newlines else b"\n" | |
| 1363 | 1316 | try: | |
| 1364 | 1317 | if output_stream is None: | |
| 1365 | - stdout_value, stderr_value = communicate() | ||
| 1318 | + stdout_value, stderr_value = proc.communicate(timeout=kill_after_timeout) | ||
| 1366 | 1319 | # Strip trailing "\n". | |
| 1367 | 1320 | if stdout_value is not None and stdout_value.endswith(newline) and strip_newline_in_stdout: # type: ignore[arg-type] | |
| 1368 | 1321 | stdout_value = stdout_value[:-1] | |
@@ -1380,7 +1333,7 @@ def communicate() -> Tuple[AnyStr, AnyStr]: | |||
| 1380 | 1333 | # Strip trailing "\n". | |
| 1381 | 1334 | if stderr_value is not None and stderr_value.endswith(newline): # type: ignore[arg-type] | |
| 1382 | 1335 | stderr_value = stderr_value[:-1] | |
| 1383 | - status = proc.wait() | ||
| 1336 | + status = proc.wait(timeout=kill_after_timeout) | ||
| 1384 | 1337 | # END stdout handling | |
| 1385 | 1338 | finally: | |
| 1386 | 1339 | if proc.stdout is not None: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments