diff options
author | Dominic <yobmod@gmail.com> | 2021-09-09 19:49:13 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-09 19:49:13 +0100 |
commit | cfcae1d839c5dbb94745c504993c9d5baebc44b5 (patch) | |
tree | 2227e4e6931ad38e1a3c5a2c48cecbf5d57e5f01 /git | |
parent | 5da76e8b4466459a3b6a400c4750a622879acce8 (diff) | |
parent | bb9b50ff2671cda598ff19653d3de49e03b6d163 (diff) | |
download | gitpython-cfcae1d839c5dbb94745c504993c9d5baebc44b5.tar.gz |
Merge pull request #1318 from Yobmod/main
General fixes for next release
Diffstat (limited to 'git')
-rw-r--r-- | git/cmd.py | 42 | ||||
-rw-r--r-- | git/config.py | 11 | ||||
-rw-r--r-- | git/objects/commit.py | 2 | ||||
-rw-r--r-- | git/objects/util.py | 13 | ||||
-rw-r--r-- | git/remote.py | 4 |
5 files changed, 43 insertions, 29 deletions
@@ -3,7 +3,7 @@ # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php - +from __future__ import annotations from contextlib import contextmanager import io import logging @@ -68,7 +68,7 @@ __all__ = ('Git',) # Documentation ## @{ -def handle_process_output(process: Union[subprocess.Popen, 'Git.AutoInterrupt'], +def handle_process_output(process: 'Git.AutoInterrupt' | Popen, stdout_handler: Union[None, Callable[[AnyStr], None], Callable[[List[AnyStr]], None], @@ -78,7 +78,8 @@ def handle_process_output(process: Union[subprocess.Popen, 'Git.AutoInterrupt'], Callable[[List[AnyStr]], None]], finalizer: Union[None, Callable[[Union[subprocess.Popen, 'Git.AutoInterrupt']], None]] = None, - decode_streams: bool = True) -> None: + decode_streams: bool = True, + timeout: float = 10.0) -> None: """Registers for notifications to learn that process output is ready to read, and dispatches lines to the respective line handlers. This function returns once the finalizer returns @@ -93,9 +94,10 @@ def handle_process_output(process: Union[subprocess.Popen, 'Git.AutoInterrupt'], their contents to handlers. Set it to False if `universal_newline == True` (then streams are in text-mode) or if decoding must happen later (i.e. for Diffs). + :param timeout: float, timeout to pass to t.join() in case it hangs. Default = 10.0 seconds """ # Use 2 "pump" threads and wait for both to finish. - def pump_stream(cmdline: str, name: str, stream: Union[BinaryIO, TextIO], is_decode: bool, + def pump_stream(cmdline: List[str], name: str, stream: Union[BinaryIO, TextIO], is_decode: bool, handler: Union[None, Callable[[Union[bytes, str]], None]]) -> None: try: for line in stream: @@ -107,22 +109,32 @@ def handle_process_output(process: Union[subprocess.Popen, 'Git.AutoInterrupt'], else: handler(line) except Exception as ex: - log.error("Pumping %r of cmd(%s) failed due to: %r", name, remove_password_if_present(cmdline), ex) - raise CommandError(['<%s-pump>' % name] + remove_password_if_present(cmdline), ex) from ex + log.error(f"Pumping {name!r} of cmd({remove_password_if_present(cmdline)}) failed due to: {ex!r}") + raise CommandError([f'<{name}-pump>'] + remove_password_if_present(cmdline), ex) from ex finally: stream.close() - cmdline = getattr(process, 'args', '') # PY3+ only + if hasattr(process, 'proc'): + process = cast('Git.AutoInterrupt', process) + cmdline: str | Tuple[str, ...] | List[str] = getattr(process.proc, 'args', '') + p_stdout = process.proc.stdout if process.proc else None + p_stderr = process.proc.stderr if process.proc else None + else: + process = cast(Popen, process) + cmdline = getattr(process, 'args', '') + p_stdout = process.stdout + p_stderr = process.stderr + if not isinstance(cmdline, (tuple, list)): cmdline = cmdline.split() - pumps = [] - if process.stdout: - pumps.append(('stdout', process.stdout, stdout_handler)) - if process.stderr: - pumps.append(('stderr', process.stderr, stderr_handler)) + pumps: List[Tuple[str, IO, Callable[..., None] | None]] = [] + if p_stdout: + pumps.append(('stdout', p_stdout, stdout_handler)) + if p_stderr: + pumps.append(('stderr', p_stderr, stderr_handler)) - threads = [] + threads: List[threading.Thread] = [] for name, stream, handler in pumps: t = threading.Thread(target=pump_stream, @@ -134,7 +146,9 @@ def handle_process_output(process: Union[subprocess.Popen, 'Git.AutoInterrupt'], ## FIXME: Why Join?? Will block if `stdin` needs feeding... # for t in threads: - t.join() + t.join(timeout=timeout) + if t.is_alive(): + raise RuntimeError(f"Thread join() timed out in cmd.handle_process_output(). Timeout={timeout} seconds") if finalizer: return finalizer(process) diff --git a/git/config.py b/git/config.py index cf32d4ba..cbd66022 100644 --- a/git/config.py +++ b/git/config.py @@ -31,7 +31,7 @@ import configparser as cp # typing------------------------------------------------------- from typing import (Any, Callable, Generic, IO, List, Dict, Sequence, - TYPE_CHECKING, Tuple, TypeVar, Union, cast, overload) + TYPE_CHECKING, Tuple, TypeVar, Union, cast) from git.types import Lit_config_levels, ConfigLevels_Tup, PathLike, assert_never, _T @@ -709,15 +709,6 @@ class GitConfigParser(cp.RawConfigParser, metaclass=MetaParserBuilder): """:return: True if this instance may change the configuration file""" return self._read_only - @overload - def get_value(self, section: str, option: str, default: None = None) -> Union[int, float, str, bool]: ... - - @overload - def get_value(self, section: str, option: str, default: str) -> str: ... - - @overload - def get_value(self, section: str, option: str, default: float) -> float: ... - def get_value(self, section: str, option: str, default: Union[int, float, str, bool, None] = None ) -> Union[int, float, str, bool]: # can default or return type include bool? diff --git a/git/objects/commit.py b/git/objects/commit.py index b689167f..b36cd46d 100644 --- a/git/objects/commit.py +++ b/git/objects/commit.py @@ -446,6 +446,8 @@ class Commit(base.Object, TraversableIterableObj, Diffable, Serializable): # assume utf8 encoding enc_section, enc_option = cls.conf_encoding.split('.') conf_encoding = cr.get_value(enc_section, enc_option, cls.default_encoding) + if not isinstance(conf_encoding, str): + raise TypeError("conf_encoding could not be coerced to str") # if the tree is no object, make sure we create one - otherwise # the created commit object is invalid diff --git a/git/objects/util.py b/git/objects/util.py index 16d4c0ac..187318fe 100644 --- a/git/objects/util.py +++ b/git/objects/util.py @@ -5,7 +5,7 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php """Module for general utility functions""" -from abc import abstractmethod +from abc import ABC, abstractmethod import warnings from git.util import ( IterableList, @@ -22,10 +22,10 @@ import calendar from datetime import datetime, timedelta, tzinfo # typing ------------------------------------------------------------ -from typing import (Any, Callable, Deque, Iterator, NamedTuple, overload, Sequence, +from typing import (Any, Callable, Deque, Iterator, Generic, NamedTuple, overload, Sequence, # NOQA: F401 TYPE_CHECKING, Tuple, Type, TypeVar, Union, cast) -from git.types import Has_id_attribute, Literal, Protocol, runtime_checkable +from git.types import Has_id_attribute, Literal, _T # NOQA: F401 if TYPE_CHECKING: from io import BytesIO, StringIO @@ -35,6 +35,13 @@ if TYPE_CHECKING: from .tree import Tree, TraversedTreeTup from subprocess import Popen from .submodule.base import Submodule + from git.types import Protocol, runtime_checkable +else: + # Protocol = Generic[_T] # NNeeded for typing bug #572? + Protocol = ABC + + def runtime_checkable(f): + return f class TraverseNT(NamedTuple): diff --git a/git/remote.py b/git/remote.py index da08fb57..55772f4a 100644 --- a/git/remote.py +++ b/git/remote.py @@ -22,12 +22,12 @@ from git.util import ( join_path, ) -from .config import ( +from git.config import ( GitConfigParser, SectionConstraint, cp, ) -from .refs import ( +from git.refs import ( Head, Reference, RemoteReference, |