From f699a387e19764e591dbca392035a5f9e457b06d Mon Sep 17 00:00:00 2001 From: Eliah Kagan Date: Wed, 21 Feb 2024 23:20:44 -0500 Subject: [PATCH] Fix Git.version_info pickling For #1836. This uses a property with the same logic as before for version_info caching, except that the _version_info backing attribute holds the value None, rather than being unset, for the uncomputed state. This rectifies the inconistency between the property's behavior and the way the associated states are represented by its __setstate__ and __getstate__ methods for pickling. Because the Git class only used LazyMixin as an implementation detail of the version_info attribute, it is removed as a subclass. --- git/cmd.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/git/cmd.py b/git/cmd.py index f58e6df5b..e5edb4959 100644 --- a/git/cmd.py +++ b/git/cmd.py @@ -25,7 +25,6 @@ UnsafeProtocolError, ) from git.util import ( - LazyMixin, cygpath, expand_path, is_cygwin_git, @@ -287,7 +286,7 @@ def dict_to_slots_and__excluded_are_none(self: object, d: Mapping[str, Any], exc ## -- End Utilities -- @} -class Git(LazyMixin): +class Git: """The Git class manages communication with the Git binary. It provides a convenient interface to calling the Git binary, such as in:: @@ -784,6 +783,7 @@ def __init__(self, working_dir: Union[None, PathLike] = None): self._environment: Dict[str, str] = {} # Cached command slots + self._version_info: Union[Tuple[int, int, int, int], None] = None self.cat_file_header: Union[None, TBD] = None self.cat_file_all: Union[None, TBD] = None @@ -795,8 +795,8 @@ def __getattr__(self, name: str) -> Any: Callable object that will execute call :meth:`_call_process` with your arguments. """ - if name[0] == "_": - return LazyMixin.__getattr__(self, name) + if name.startswith("_"): + return super().__getattribute__(name) return lambda *args, **kwargs: self._call_process(name, *args, **kwargs) def set_persistent_git_options(self, **kwargs: Any) -> None: @@ -811,20 +811,6 @@ def set_persistent_git_options(self, **kwargs: Any) -> None: self._persistent_git_options = self.transform_kwargs(split_single_char_options=True, **kwargs) - def _set_cache_(self, attr: str) -> None: - if attr == "_version_info": - # We only use the first 4 numbers, as everything else could be strings in fact (on Windows). - process_version = self._call_process("version") # Should be as default *args and **kwargs used. - version_numbers = process_version.split(" ")[2] - - self._version_info = cast( - Tuple[int, int, int, int], - tuple(int(n) for n in version_numbers.split(".")[:4] if n.isdigit()), - ) - else: - super()._set_cache_(attr) - # END handle version info - @property def working_dir(self) -> Union[None, PathLike]: """:return: Git directory we are working on""" @@ -838,6 +824,16 @@ def version_info(self) -> Tuple[int, int, int, int]: This value is generated on demand and is cached. """ + if self._version_info is None: + # We only use the first 4 numbers, as everything else could be strings in fact (on Windows). + process_version = self._call_process("version") # Should be as default *args and **kwargs used. + version_numbers = process_version.split(" ")[2] + + self._version_info = cast( + Tuple[int, int, int, int], + tuple(int(n) for n in version_numbers.split(".")[:4] if n.isdigit()), + ) + return self._version_info @overload