Codebase list python-git / d2fe1eca-10be-4e48-bf57-286f4abd4fb3/main
New upstream release. Debian Janitor 2 years ago
10 changed file(s) with 162 addition(s) and 45 deletion(s). Raw diff Collapse all Expand all
00 Metadata-Version: 2.1
11 Name: GitPython
2 Version: 3.1.23
2 Version: 3.1.24
33 Summary: GitPython is a python library used to interact with Git repositories
44 Home-page: https://github.com/gitpython-developers/GitPython
55 Author: Sebastian Thiel, Michael Trier
44 MANIFEST.in
55 README.md
66 VERSION
7 pyproject.toml
78 requirements.txt
89 setup.py
910 test-requirements.txt
00 Metadata-Version: 2.1
11 Name: GitPython
2 Version: 3.1.23
2 Version: 3.1.24
33 Summary: GitPython is a python library used to interact with Git repositories
44 Home-page: https://github.com/gitpython-developers/GitPython
55 Author: Sebastian Thiel, Michael Trier
0 3.1.23
0 3.1.24
0 python-git (3.1.24-1) UNRELEASED; urgency=low
1
2 * New upstream release.
3
4 -- Debian Janitor <janitor@jelmer.uk> Sun, 26 Sep 2021 03:30:59 -0000
5
06 python-git (3.1.23-1) unstable; urgency=medium
17
28 [ Steffen Möller ]
11 Changelog
22 =========
33
4 31.23
4
5 3.1.24
56 ======
67
8 * Newly added timeout flag is not be enabled by default, and was renamed to kill_after_timeout
9
10 See the following for details:
11 https://github.com/gitpython-developers/gitpython/milestone/54?closed=1
12 https://github.com/gitpython-developers/gitpython/milestone/53?closed=1
13
14 3.1.23 (YANKED)
15 ===============
16
717 * This is the second typed release with a lot of improvements under the hood.
818
919 * General:
4454 - Add timeout to handle_process_output(), in case thread.join() hangs.
4555
4656 See the following for details:
47 https://github.com/gitpython-developers/gitpython/milestone/52?closed=1
57 https://github.com/gitpython-developers/gitpython/milestone/53?closed=1
4858
4959
5060 3.1.20 (YANKED)
1313 from typing import Optional
1414 from git.types import PathLike
1515
16 __version__ = '3.1.23'
16 __version__ = '3.1.24'
1717
1818
1919 #{ Initialization
2020 def _init_externals() -> None:
2121 """Initialize external projects by putting them into the path"""
22 if __version__ == '3.1.23' and 'PYOXIDIZER' not in os.environ:
22 if __version__ == '3.1.24' and 'PYOXIDIZER' not in os.environ:
2323 sys.path.insert(1, osp.join(osp.dirname(__file__), 'ext', 'gitdb'))
2424
2525 try:
7878 finalizer: Union[None,
7979 Callable[[Union[subprocess.Popen, 'Git.AutoInterrupt']], None]] = None,
8080 decode_streams: bool = True,
81 timeout: float = 10.0) -> None:
81 kill_after_timeout: Union[None, float] = None) -> None:
8282 """Registers for notifications to learn that process output is ready to read, and dispatches lines to
8383 the respective line handlers.
8484 This function returns once the finalizer returns
9393 their contents to handlers.
9494 Set it to False if `universal_newline == True` (then streams are in text-mode)
9595 or if decoding must happen later (i.e. for Diffs).
96 :param timeout: float, timeout to pass to t.join() in case it hangs. Default = 10.0 seconds
96 :param kill_after_timeout:
97 float or None, Default = None
98 To specify a timeout in seconds for the git command, after which the process
99 should be killed.
97100 """
98101 # Use 2 "pump" threads and wait for both to finish.
99102 def pump_stream(cmdline: List[str], name: str, stream: Union[BinaryIO, TextIO], is_decode: bool,
107110 handler(line_str)
108111 else:
109112 handler(line)
113
110114 except Exception as ex:
111115 log.error(f"Pumping {name!r} of cmd({remove_password_if_present(cmdline)}) failed due to: {ex!r}")
112 raise CommandError([f'<{name}-pump>'] + remove_password_if_present(cmdline), ex) from ex
116 if "I/O operation on closed file" not in str(ex):
117 # Only reraise if the error was not due to the stream closing
118 raise CommandError([f'<{name}-pump>'] + remove_password_if_present(cmdline), ex) from ex
113119 finally:
114120 stream.close()
115121
145151 ## FIXME: Why Join?? Will block if `stdin` needs feeding...
146152 #
147153 for t in threads:
148 t.join(timeout=timeout)
154 t.join(timeout=kill_after_timeout)
149155 if t.is_alive():
150 raise RuntimeError(f"Thread join() timed out in cmd.handle_process_output(). Timeout={timeout} seconds")
156 if isinstance(process, Git.AutoInterrupt):
157 process._terminate()
158 else: # Don't want to deal with the other case
159 raise RuntimeError("Thread join() timed out in cmd.handle_process_output()."
160 f" kill_after_timeout={kill_after_timeout} seconds")
161 if stderr_handler:
162 error_str: Union[str, bytes] = (
163 "error: process killed because it timed out."
164 f" kill_after_timeout={kill_after_timeout} seconds")
165 if not decode_streams and isinstance(p_stderr, BinaryIO):
166 # Assume stderr_handler needs binary input
167 error_str = cast(str, error_str)
168 error_str = error_str.encode()
169 # We ignore typing on the next line because mypy does not like
170 # the way we inferred that stderr takes str or bytes
171 stderr_handler(error_str) # type: ignore
151172
152173 if finalizer:
153174 return finalizer(process)
385406 The wait method was overridden to perform automatic status code checking
386407 and possibly raise."""
387408
388 __slots__ = ("proc", "args")
409 __slots__ = ("proc", "args", "status")
410
411 # If this is non-zero it will override any status code during
412 # _terminate, used to prevent race conditions in testing
413 _status_code_if_terminate: int = 0
389414
390415 def __init__(self, proc: Union[None, subprocess.Popen], args: Any) -> None:
391416 self.proc = proc
392417 self.args = args
393
394 def __del__(self) -> None:
418 self.status: Union[int, None] = None
419
420 def _terminate(self) -> None:
421 """Terminate the underlying process"""
395422 if self.proc is None:
396423 return
397424
403430 proc.stdout.close()
404431 if proc.stderr:
405432 proc.stderr.close()
406
407433 # did the process finish already so we have a return code ?
408434 try:
409435 if proc.poll() is not None:
436 self.status = self._status_code_if_terminate or proc.poll()
410437 return None
411438 except OSError as ex:
412439 log.info("Ignored error after process had died: %r", ex)
418445 # try to kill it
419446 try:
420447 proc.terminate()
421 proc.wait() # ensure process goes away
448 status = proc.wait() # ensure process goes away
449
450 self.status = self._status_code_if_terminate or status
422451 except OSError as ex:
423452 log.info("Ignored error after process had died: %r", ex)
424453 except AttributeError:
430459 call(("TASKKILL /F /T /PID %s 2>nul 1>nul" % str(proc.pid)), shell=True)
431460 # END exception handling
432461
462 def __del__(self) -> None:
463 self._terminate()
464
433465 def __getattr__(self, attr: str) -> Any:
434466 return getattr(self.proc, attr)
435467
443475 if stderr is None:
444476 stderr_b = b''
445477 stderr_b = force_bytes(data=stderr, encoding='utf-8')
446
478 status: Union[int, None]
447479 if self.proc is not None:
448480 status = self.proc.wait()
449
450 def read_all_from_possibly_closed_stream(stream: Union[IO[bytes], None]) -> bytes:
451 if stream:
452 try:
453 return stderr_b + force_bytes(stream.read())
454 except ValueError:
455 return stderr_b or b''
456 else:
481 p_stderr = self.proc.stderr
482 else: # Assume the underlying proc was killed earlier or never existed
483 status = self.status
484 p_stderr = None
485
486 def read_all_from_possibly_closed_stream(stream: Union[IO[bytes], None]) -> bytes:
487 if stream:
488 try:
489 return stderr_b + force_bytes(stream.read())
490 except ValueError:
457491 return stderr_b or b''
458
459 if status != 0:
460 errstr = read_all_from_possibly_closed_stream(self.proc.stderr)
461 log.debug('AutoInterrupt wait stderr: %r' % (errstr,))
462 raise GitCommandError(remove_password_if_present(self.args), status, errstr)
492 else:
493 return stderr_b or b''
494
463495 # END status handling
496
497 if status != 0:
498 errstr = read_all_from_possibly_closed_stream(p_stderr)
499 log.debug('AutoInterrupt wait stderr: %r' % (errstr,))
500 raise GitCommandError(remove_password_if_present(self.args), status, errstr)
464501 return status
465502
466503 # END auto interrupt
693730 as_process: bool = False,
694731 output_stream: Union[None, BinaryIO] = None,
695732 stdout_as_string: bool = True,
696 kill_after_timeout: Union[None, int] = None,
733 kill_after_timeout: Union[None, float] = None,
697734 with_stdout: bool = True,
698735 universal_newlines: bool = False,
699736 shell: Union[None, bool] = None,
816853
817854 if is_win:
818855 cmd_not_found_exception = OSError
819 if kill_after_timeout:
856 if kill_after_timeout is not None:
820857 raise GitCommandError(redacted_command, '"kill_after_timeout" feature is not supported on Windows.')
821858 else:
822859 cmd_not_found_exception = FileNotFoundError # NOQA # exists, flake8 unknown @UndefinedVariable
883920 return
884921 # end
885922
886 if kill_after_timeout:
923 if kill_after_timeout is not None:
887924 kill_check = threading.Event()
888925 watchdog = threading.Timer(kill_after_timeout, _kill_process, args=(proc.pid,))
889926
894931 newline = "\n" if universal_newlines else b"\n"
895932 try:
896933 if output_stream is None:
897 if kill_after_timeout:
934 if kill_after_timeout is not None:
898935 watchdog.start()
899936 stdout_value, stderr_value = proc.communicate()
900 if kill_after_timeout:
937 if kill_after_timeout is not None:
901938 watchdog.cancel()
902939 if kill_check.is_set():
903940 stderr_value = ('Timeout: the command "%s" did not complete in %d '
706706 return self
707707
708708 def _get_fetch_info_from_stderr(self, proc: 'Git.AutoInterrupt',
709 progress: Union[Callable[..., Any], RemoteProgress, None]
709 progress: Union[Callable[..., Any], RemoteProgress, None],
710 kill_after_timeout: Union[None, float] = None,
710711 ) -> IterableList['FetchInfo']:
711712
712713 progress = to_progress_instance(progress)
723724 cmds = set(FetchInfo._flag_map.keys())
724725
725726 progress_handler = progress.new_message_handler()
726 handle_process_output(proc, None, progress_handler, finalizer=None, decode_streams=False)
727 handle_process_output(proc, None, progress_handler, finalizer=None, decode_streams=False,
728 kill_after_timeout=kill_after_timeout)
727729
728730 stderr_text = progress.error_lines and '\n'.join(progress.error_lines) or ''
729731 proc.wait(stderr=stderr_text)
768770 return output
769771
770772 def _get_push_info(self, proc: 'Git.AutoInterrupt',
771 progress: Union[Callable[..., Any], RemoteProgress, None]) -> IterableList[PushInfo]:
773 progress: Union[Callable[..., Any], RemoteProgress, None],
774 kill_after_timeout: Union[None, float] = None) -> IterableList[PushInfo]:
772775 progress = to_progress_instance(progress)
773776
774777 # read progress information from stderr
785788 # If an error happens, additional info is given which we parse below.
786789 pass
787790
788 handle_process_output(proc, stdout_handler, progress_handler, finalizer=None, decode_streams=False)
791 handle_process_output(proc, stdout_handler, progress_handler, finalizer=None, decode_streams=False,
792 kill_after_timeout=kill_after_timeout)
789793 stderr_text = progress.error_lines and '\n'.join(progress.error_lines) or ''
790794 try:
791795 proc.wait(stderr=stderr_text)
792796 except Exception:
797 # This is different than fetch (which fails if there is any std_err
798 # even if there is an output)
793799 if not output:
794800 raise
795801 elif stderr_text:
812818
813819 def fetch(self, refspec: Union[str, List[str], None] = None,
814820 progress: Union[RemoteProgress, None, 'UpdateProgress'] = None,
815 verbose: bool = True, **kwargs: Any) -> IterableList[FetchInfo]:
821 verbose: bool = True,
822 kill_after_timeout: Union[None, float] = None,
823 **kwargs: Any) -> IterableList[FetchInfo]:
816824 """Fetch the latest changes for this remote
817825
818826 :param refspec:
832840 for 'refspec' will make use of this facility.
833841 :param progress: See 'push' method
834842 :param verbose: Boolean for verbose output
843 :param kill_after_timeout:
844 To specify a timeout in seconds for the git command, after which the process
845 should be killed. It is set to None by default.
835846 :param kwargs: Additional arguments to be passed to git-fetch
836847 :return:
837848 IterableList(FetchInfo, ...) list of FetchInfo instances providing detailed
852863
853864 proc = self.repo.git.fetch(self, *args, as_process=True, with_stdout=False,
854865 universal_newlines=True, v=verbose, **kwargs)
855 res = self._get_fetch_info_from_stderr(proc, progress)
866 res = self._get_fetch_info_from_stderr(proc, progress,
867 kill_after_timeout=kill_after_timeout)
856868 if hasattr(self.repo.odb, 'update_cache'):
857869 self.repo.odb.update_cache()
858870 return res
859871
860872 def pull(self, refspec: Union[str, List[str], None] = None,
861873 progress: Union[RemoteProgress, 'UpdateProgress', None] = None,
874 kill_after_timeout: Union[None, float] = None,
862875 **kwargs: Any) -> IterableList[FetchInfo]:
863876 """Pull changes from the given branch, being the same as a fetch followed
864877 by a merge of branch with your local branch.
865878
866879 :param refspec: see 'fetch' method
867880 :param progress: see 'push' method
881 :param kill_after_timeout: see 'fetch' method
868882 :param kwargs: Additional arguments to be passed to git-pull
869883 :return: Please see 'fetch' method """
870884 if refspec is None:
873887 kwargs = add_progress(kwargs, self.repo.git, progress)
874888 proc = self.repo.git.pull(self, refspec, with_stdout=False, as_process=True,
875889 universal_newlines=True, v=True, **kwargs)
876 res = self._get_fetch_info_from_stderr(proc, progress)
890 res = self._get_fetch_info_from_stderr(proc, progress,
891 kill_after_timeout=kill_after_timeout)
877892 if hasattr(self.repo.odb, 'update_cache'):
878893 self.repo.odb.update_cache()
879894 return res
880895
881896 def push(self, refspec: Union[str, List[str], None] = None,
882897 progress: Union[RemoteProgress, 'UpdateProgress', Callable[..., RemoteProgress], None] = None,
898 kill_after_timeout: Union[None, float] = None,
883899 **kwargs: Any) -> IterableList[PushInfo]:
884900 """Push changes from source branch in refspec to target branch in refspec.
885901
896912 overrides the ``update()`` function.
897913
898914 :note: No further progress information is returned after push returns.
915 :param kill_after_timeout:
916 To specify a timeout in seconds for the git command, after which the process
917 should be killed. It is set to None by default.
899918 :param kwargs: Additional arguments to be passed to git-push
900919 :return:
901920 list(PushInfo, ...) list of PushInfo instances, each
907926 be 0."""
908927 kwargs = add_progress(kwargs, self.repo.git, progress)
909928 proc = self.repo.git.push(self, refspec, porcelain=True, as_process=True,
910 universal_newlines=True, **kwargs)
911 return self._get_push_info(proc, progress)
929 universal_newlines=True,
930 kill_after_timeout=kill_after_timeout,
931 **kwargs)
932 return self._get_push_info(proc, progress,
933 kill_after_timeout=kill_after_timeout)
912934
913935 @ property
914936 def config_reader(self) -> SectionConstraint[GitConfigParser]:
0 [build-system]
1 requires = ["setuptools", "wheel"]
2 build-backend = "setuptools.build_meta"
3
4 [tool.pytest.ini_options]
5 python_files = 'test_*.py'
6 testpaths = 'test' # space seperated list of paths from root e.g test tests doc/testing
7 addopts = '--cov=git --cov-report=term --maxfail=10 --force-sugar --disable-warnings'
8 filterwarnings = 'ignore::DeprecationWarning'
9 # --cov coverage
10 # --cov-report term # send report to terminal term-missing -> terminal with line numbers html xml
11 # --cov-report term-missing # to terminal with line numbers
12 # --cov-report html:path # html file at path
13 # --maxfail # number of errors before giving up
14 # -disable-warnings # Disable pytest warnings (not codebase warnings)
15 # -rf # increased reporting of failures
16 # -rE # increased reporting of errors
17 # --ignore-glob=**/gitdb/* # ignore glob paths
18 # filterwarnings ignore::WarningType # ignores those warnings
19
20 [tool.mypy]
21 disallow_untyped_defs = true
22 no_implicit_optional = true
23 warn_redundant_casts = true
24 # warn_unused_ignores = true
25 warn_unreachable = true
26 show_error_codes = true
27 implicit_reexport = true
28 # strict = true
29
30 # TODO: remove when 'gitdb' is fully annotated
31 [[tool.mypy.overrides]]
32 module = "gitdb.*"
33 ignore_missing_imports = true
34
35 [tool.coverage.run]
36 source = ["git"]
37
38 [tool.coverage.report]
39 include = ["*/git/*"]
40 omit = ["*/git/ext/*"]