| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b7cd520 commit ef0ca65
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -79,7 +79,7 @@ def handle_process_output(process: 'Git.AutoInterrupt' | Popen, | |||
| 79 | 79 | finalizer: Union[None, | |
| 80 | 80 | Callable[[Union[subprocess.Popen, 'Git.AutoInterrupt']], None]] = None, | |
| 81 | 81 | decode_streams: bool = True, | |
| 82 | - timeout: Union[None, float] = None) -> None: | ||
| 82 | + kill_after_timeout: Union[None, float] = None) -> None: | ||
| 83 | 83 | """Registers for notifications to learn that process output is ready to read, and dispatches lines to | |
| 84 | 84 | the respective line handlers. | |
| 85 | 85 | This function returns once the finalizer returns | |
@@ -94,7 +94,10 @@ def handle_process_output(process: 'Git.AutoInterrupt' | Popen, | |||
| 94 | 94 | their contents to handlers. | |
| 95 | 95 | Set it to False if `universal_newline == True` (then streams are in text-mode) | |
| 96 | 96 | or if decoding must happen later (i.e. for Diffs). | |
| 97 | - :param timeout: float, or None timeout to pass to t.join() in case it hangs. Default = None. | ||
| 97 | + :param kill_after_timeout: | ||
| 98 | + float or None, Default = None | ||
| 99 | + To specify a timeout in seconds for the git command, after which the process | ||
| 100 | + should be killed. | ||
| 98 | 101 | """ | |
| 99 | 102 | # Use 2 "pump" threads and wait for both to finish. | |
| 100 | 103 | def pump_stream(cmdline: List[str], name: str, stream: Union[BinaryIO, TextIO], is_decode: bool, | |
@@ -108,9 +111,12 @@ def pump_stream(cmdline: List[str], name: str, stream: Union[BinaryIO, TextIO], | |||
| 108 | 111 | handler(line_str) | |
| 109 | 112 | else: | |
| 110 | 113 | handler(line) | |
| 114 | + | ||
| 111 | 115 | except Exception as ex: | |
| 112 | 116 | log.error(f"Pumping {name!r} of cmd({remove_password_if_present(cmdline)}) failed due to: {ex!r}") | |
| 113 | - raise CommandError([f'<{name}-pump>'] + remove_password_if_present(cmdline), ex) from ex | ||
| 117 | + if "I/O operation on closed file" not in str(ex): | ||
| 118 | + # Only reraise if the error was not due to the stream closing | ||
| 119 | + raise CommandError([f'<{name}-pump>'] + remove_password_if_present(cmdline), ex) from ex | ||
| 114 | 120 | finally: | |
| 115 | 121 | stream.close() | |
| 116 | 122 | ||
@@ -146,9 +152,16 @@ def pump_stream(cmdline: List[str], name: str, stream: Union[BinaryIO, TextIO], | |||
| 146 | 152 | ## FIXME: Why Join?? Will block if `stdin` needs feeding... | |
| 147 | 153 | # | |
| 148 | 154 | for t in threads: | |
| 149 | - t.join(timeout=timeout) | ||
| 155 | + t.join(timeout=kill_after_timeout) | ||
| 150 | 156 | if t.is_alive(): | |
| 151 | - raise RuntimeError(f"Thread join() timed out in cmd.handle_process_output(). Timeout={timeout} seconds") | ||
| 157 | + if hasattr(process, 'proc'): # Assume it is a Git.AutoInterrupt: | ||
| 158 | + process._terminate() | ||
| 159 | + else: # Don't want to deal with the other case | ||
| 160 | + raise RuntimeError(f"Thread join() timed out in cmd.handle_process_output()." | ||
| 161 | + " kill_after_timeout={kill_after_timeout} seconds") | ||
| 162 | + if stderr_handler: | ||
| 163 | + stderr_handler("error: process killed because it timed out." | ||
| 164 | + f" kill_after_timeout={kill_after_timeout} seconds") | ||
| 152 | 165 | ||
| 153 | 166 | if finalizer: | |
| 154 | 167 | return finalizer(process) | |
@@ -386,13 +399,15 @@ class AutoInterrupt(object): | |||
| 386 | 399 | The wait method was overridden to perform automatic status code checking | |
| 387 | 400 | and possibly raise.""" | |
| 388 | 401 | ||
| 389 | - __slots__ = ("proc", "args") | ||
| 402 | + __slots__ = ("proc", "args", "status") | ||
| 390 | 403 | ||
| 391 | 404 | def __init__(self, proc: Union[None, subprocess.Popen], args: Any) -> None: | |
| 392 | 405 | self.proc = proc | |
| 393 | 406 | self.args = args | |
| 407 | + self.status = None | ||
| 394 | 408 | ||
| 395 | - def __del__(self) -> None: | ||
| 409 | + def _terminate(self) -> None: | ||
| 410 | + """Terminate the underlying process""" | ||
| 396 | 411 | if self.proc is None: | |
| 397 | 412 | return | |
| 398 | 413 | ||
@@ -408,6 +423,7 @@ def __del__(self) -> None: | |||
| 408 | 423 | # did the process finish already so we have a return code ? | |
| 409 | 424 | try: | |
| 410 | 425 | if proc.poll() is not None: | |
| 426 | + self.status = proc.poll() | ||
| 411 | 427 | return None | |
| 412 | 428 | except OSError as ex: | |
| 413 | 429 | log.info("Ignored error after process had died: %r", ex) | |
@@ -419,7 +435,7 @@ def __del__(self) -> None: | |||
| 419 | 435 | # try to kill it | |
| 420 | 436 | try: | |
| 421 | 437 | proc.terminate() | |
| 422 | - proc.wait() # ensure process goes away | ||
| 438 | + self.status = proc.wait() # ensure process goes away | ||
| 423 | 439 | except OSError as ex: | |
| 424 | 440 | log.info("Ignored error after process had died: %r", ex) | |
| 425 | 441 | except AttributeError: | |
@@ -431,6 +447,11 @@ def __del__(self) -> None: | |||
| 431 | 447 | call(("TASKKILL /F /T /PID %s 2>nul 1>nul" % str(proc.pid)), shell=True) | |
| 432 | 448 | # END exception handling | |
| 433 | 449 | ||
| 450 | + | ||
| 451 | + | ||
| 452 | + def __del__(self) -> None: | ||
| 453 | + self._terminate() | ||
| 454 | + | ||
| 434 | 455 | def __getattr__(self, attr: str) -> Any: | |
| 435 | 456 | return getattr(self.proc, attr) | |
| 436 | 457 | ||
@@ -447,21 +468,26 @@ def wait(self, stderr: Union[None, str, bytes] = b'') -> int: | |||
| 447 | 468 | ||
| 448 | 469 | if self.proc is not None: | |
| 449 | 470 | status = self.proc.wait() | |
| 471 | + p_stderr = self.proc.stderr | ||
| 472 | + else: #Assume the underlying proc was killed earlier or never existed | ||
| 473 | + status = self.status | ||
| 474 | + p_stderr = None | ||
| 450 | 475 | ||
| 451 | - def read_all_from_possibly_closed_stream(stream: Union[IO[bytes], None]) -> bytes: | ||
| 452 | - if stream: | ||
| 453 | - try: | ||
| 454 | - return stderr_b + force_bytes(stream.read()) | ||
| 455 | - except ValueError: | ||
| 456 | - return stderr_b or b'' | ||
| 457 | - else: | ||
| 476 | + def read_all_from_possibly_closed_stream(stream: Union[IO[bytes], None]) -> bytes: | ||
| 477 | + if stream: | ||
| 478 | + try: | ||
| 479 | + return stderr_b + force_bytes(stream.read()) | ||
| 480 | + except ValueError: | ||
| 458 | 481 | return stderr_b or b'' | |
| 482 | + else: | ||
| 483 | + return stderr_b or b'' | ||
| 459 | 484 | ||
| 460 | - if status != 0: | ||
| 461 | - errstr = read_all_from_possibly_closed_stream(self.proc.stderr) | ||
| 462 | - log.debug('AutoInterrupt wait stderr: %r' % (errstr,)) | ||
| 463 | - raise GitCommandError(remove_password_if_present(self.args), status, errstr) | ||
| 464 | 485 | # END status handling | |
| 486 | + | ||
| 487 | + if status != 0: | ||
| 488 | + errstr = read_all_from_possibly_closed_stream(p_stderr) | ||
| 489 | + log.debug('AutoInterrupt wait stderr: %r' % (errstr,)) | ||
| 490 | + raise GitCommandError(remove_password_if_present(self.args), status, errstr) | ||
| 465 | 491 | return status | |
| 466 | 492 | ||
| 467 | 493 | # END auto interrupt | |
@@ -694,7 +720,7 @@ def execute(self, | |||
| 694 | 720 | as_process: bool = False, | |
| 695 | 721 | output_stream: Union[None, BinaryIO] = None, | |
| 696 | 722 | stdout_as_string: bool = True, | |
| 697 | - kill_after_timeout: Union[None, int] = None, | ||
| 723 | + kill_after_timeout: Union[None, float] = None, | ||
| 698 | 724 | with_stdout: bool = True, | |
| 699 | 725 | universal_newlines: bool = False, | |
| 700 | 726 | shell: Union[None, bool] = None, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -708,7 +708,7 @@ def update(self, **kwargs: Any) -> 'Remote': | |||
| 708 | 708 | ||
| 709 | 709 | def _get_fetch_info_from_stderr(self, proc: 'Git.AutoInterrupt', | |
| 710 | 710 | progress: Union[Callable[..., Any], RemoteProgress, None], | |
| 711 | - timeout: Union[None, float] = None, | ||
| 711 | + kill_after_timeout: Union[None, float] = None, | ||
| 712 | 712 | ) -> IterableList['FetchInfo']: | |
| 713 | 713 | ||
| 714 | 714 | progress = to_progress_instance(progress) | |
@@ -726,7 +726,7 @@ def _get_fetch_info_from_stderr(self, proc: 'Git.AutoInterrupt', | |||
| 726 | 726 | ||
| 727 | 727 | progress_handler = progress.new_message_handler() | |
| 728 | 728 | handle_process_output(proc, None, progress_handler, finalizer=None, decode_streams=False, | |
| 729 | - timeout=timeout) | ||
| 729 | + kill_after_timeout=kill_after_timeout) | ||
| 730 | 730 | ||
| 731 | 731 | stderr_text = progress.error_lines and '\n'.join(progress.error_lines) or '' | |
| 732 | 732 | proc.wait(stderr=stderr_text) | |
@@ -772,7 +772,7 @@ def _get_fetch_info_from_stderr(self, proc: 'Git.AutoInterrupt', | |||
| 772 | 772 | ||
| 773 | 773 | def _get_push_info(self, proc: 'Git.AutoInterrupt', | |
| 774 | 774 | progress: Union[Callable[..., Any], RemoteProgress, None], | |
| 775 | - timeout: Union[None, float] = None) -> IterableList[PushInfo]: | ||
| 775 | + kill_after_timeout: Union[None, float] = None) -> IterableList[PushInfo]: | ||
| 776 | 776 | progress = to_progress_instance(progress) | |
| 777 | 777 | ||
| 778 | 778 | # read progress information from stderr | |
@@ -790,7 +790,7 @@ def stdout_handler(line: str) -> None: | |||
| 790 | 790 | pass | |
| 791 | 791 | ||
| 792 | 792 | handle_process_output(proc, stdout_handler, progress_handler, finalizer=None, decode_streams=False, | |
| 793 | - timeout=timeout) | ||
| 793 | + kill_after_timeout=kill_after_timeout) | ||
| 794 | 794 | stderr_text = progress.error_lines and '\n'.join(progress.error_lines) or '' | |
| 795 | 795 | try: | |
| 796 | 796 | proc.wait(stderr=stderr_text) | |
@@ -817,7 +817,8 @@ def _assert_refspec(self) -> None: | |||
| 817 | 817 | ||
| 818 | 818 | def fetch(self, refspec: Union[str, List[str], None] = None, | |
| 819 | 819 | progress: Union[RemoteProgress, None, 'UpdateProgress'] = None, | |
| 820 | - verbose: bool = True, timeout: Union[None, float] = None, | ||
| 820 | + verbose: bool = True, | ||
| 821 | + kill_after_timeout: Union[None, float] = None, | ||
| 821 | 822 | **kwargs: Any) -> IterableList[FetchInfo]: | |
| 822 | 823 | """Fetch the latest changes for this remote | |
| 823 | 824 | ||
@@ -838,6 +839,9 @@ def fetch(self, refspec: Union[str, List[str], None] = None, | |||
| 838 | 839 | for 'refspec' will make use of this facility. | |
| 839 | 840 | :param progress: See 'push' method | |
| 840 | 841 | :param verbose: Boolean for verbose output | |
| 842 | + :param kill_after_timeout: | ||
| 843 | + To specify a timeout in seconds for the git command, after which the process | ||
| 844 | + should be killed. It is set to None by default. | ||
| 841 | 845 | :param kwargs: Additional arguments to be passed to git-fetch | |
| 842 | 846 | :return: | |
| 843 | 847 | IterableList(FetchInfo, ...) list of FetchInfo instances providing detailed | |
@@ -858,20 +862,22 @@ def fetch(self, refspec: Union[str, List[str], None] = None, | |||
| 858 | 862 | ||
| 859 | 863 | proc = self.repo.git.fetch(self, *args, as_process=True, with_stdout=False, | |
| 860 | 864 | universal_newlines=True, v=verbose, **kwargs) | |
| 861 | - res = self._get_fetch_info_from_stderr(proc, progress, timeout=timeout) | ||
| 865 | + res = self._get_fetch_info_from_stderr(proc, progress, | ||
| 866 | + kill_after_timeout=kill_after_timeout) | ||
| 862 | 867 | if hasattr(self.repo.odb, 'update_cache'): | |
| 863 | 868 | self.repo.odb.update_cache() | |
| 864 | 869 | return res | |
| 865 | 870 | ||
| 866 | 871 | def pull(self, refspec: Union[str, List[str], None] = None, | |
| 867 | 872 | progress: Union[RemoteProgress, 'UpdateProgress', None] = None, | |
| 868 | - timeout: Union[None, float] = None, | ||
| 873 | + kill_after_timeout: Union[None, float] = None, | ||
| 869 | 874 | **kwargs: Any) -> IterableList[FetchInfo]: | |
| 870 | 875 | """Pull changes from the given branch, being the same as a fetch followed | |
| 871 | 876 | by a merge of branch with your local branch. | |
| 872 | 877 | ||
| 873 | 878 | :param refspec: see 'fetch' method | |
| 874 | 879 | :param progress: see 'push' method | |
| 880 | + :param kill_after_timeout: see 'fetch' method | ||
| 875 | 881 | :param kwargs: Additional arguments to be passed to git-pull | |
| 876 | 882 | :return: Please see 'fetch' method """ | |
| 877 | 883 | if refspec is None: | |
@@ -880,14 +886,16 @@ def pull(self, refspec: Union[str, List[str], None] = None, | |||
| 880 | 886 | kwargs = add_progress(kwargs, self.repo.git, progress) | |
| 881 | 887 | proc = self.repo.git.pull(self, refspec, with_stdout=False, as_process=True, | |
| 882 | 888 | universal_newlines=True, v=True, **kwargs) | |
| 883 | - res = self._get_fetch_info_from_stderr(proc, progress, timeout=timeout) | ||
| 889 | + res = self._get_fetch_info_from_stderr(proc, progress, | ||
| 890 | + kill_after_timeout=kill_after_timeout) | ||
| 884 | 891 | if hasattr(self.repo.odb, 'update_cache'): | |
| 885 | 892 | self.repo.odb.update_cache() | |
| 886 | 893 | return res | |
| 887 | 894 | ||
| 888 | 895 | def push(self, refspec: Union[str, List[str], None] = None, | |
| 889 | 896 | progress: Union[RemoteProgress, 'UpdateProgress', Callable[..., RemoteProgress], None] = None, | |
| 890 | - timeout: Union[None, float] = None, **kwargs: Any) -> IterableList[PushInfo]: | ||
| 897 | + kill_after_timeout: Union[None, float] = None, | ||
| 898 | + **kwargs: Any) -> IterableList[PushInfo]: | ||
| 891 | 899 | """Push changes from source branch in refspec to target branch in refspec. | |
| 892 | 900 | ||
| 893 | 901 | :param refspec: see 'fetch' method | |
@@ -903,6 +911,9 @@ def push(self, refspec: Union[str, List[str], None] = None, | |||
| 903 | 911 | overrides the ``update()`` function. | |
| 904 | 912 | ||
| 905 | 913 | :note: No further progress information is returned after push returns. | |
| 914 | + :param kill_after_timeout: | ||
| 915 | + To specify a timeout in seconds for the git command, after which the process | ||
| 916 | + should be killed. It is set to None by default. | ||
| 906 | 917 | :param kwargs: Additional arguments to be passed to git-push | |
| 907 | 918 | :return: | |
| 908 | 919 | list(PushInfo, ...) list of PushInfo instances, each | |
@@ -914,8 +925,11 @@ def push(self, refspec: Union[str, List[str], None] = None, | |||
| 914 | 925 | be 0.""" | |
| 915 | 926 | kwargs = add_progress(kwargs, self.repo.git, progress) | |
| 916 | 927 | proc = self.repo.git.push(self, refspec, porcelain=True, as_process=True, | |
| 917 | - universal_newlines=True, **kwargs) | ||
| 918 | - return self._get_push_info(proc, progress, timeout=timeout) | ||
| 928 | + universal_newlines=True, | ||
| 929 | + kill_after_timeout=kill_after_timeout, | ||
| 930 | + **kwargs) | ||
| 931 | + return self._get_push_info(proc, progress, | ||
| 932 | + kill_after_timeout=kill_after_timeout) | ||
| 919 | 933 | ||
| 920 | 934 | @ property | |
| 921 | 935 | def config_reader(self) -> SectionConstraint[GitConfigParser]: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments