Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix version_info cache invalidation, typing, parsing, and serialization#1838

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Byron merged 19 commits intogitpython-developers:mainfromEliahKagan:refresh-version
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
19 commits
Select commitHold shift + click to select a range
e40fc2c
Use TemporaryFile as a context manager
EliahKaganFeb 21, 2024
1de2fdc
Test that version_info caches
EliahKaganFeb 21, 2024
3f107d5
Test that version_info caching is per instance
EliahKaganFeb 21, 2024
2eac36c
Have _fake_git fixture take version_info
EliahKaganFeb 21, 2024
2d6311b
Test version_info on unpickled Git instance
EliahKaganFeb 22, 2024
f699a38
Fix Git.version_info pickling
EliahKaganFeb 22, 2024
634b618
Use documented version_info in test_index_file_diffing
EliahKaganFeb 22, 2024
626a550
Test that refreshing invalidates cached version_info
EliahKaganFeb 21, 2024
9151c38
Test "installing" another git and refreshing
EliahKaganFeb 22, 2024
c2d72ff
Simplify patched PATH env var construction
EliahKaganFeb 22, 2024
ee385bd
Make "install" and refresh version_info test portable
EliahKaganFeb 22, 2024
2940662
Test that version_info is not pickled
EliahKaganFeb 22, 2024
e3e5687
Fix tests for Python <3.9 which lack Path.with_stem
EliahKaganFeb 22, 2024
24b065e
Invalidate all cached version_info on refresh
EliahKaganFeb 22, 2024
82b0a1e
Clarify comments; add assertion (helps mypy)
EliahKaganFeb 22, 2024
eb438ee
Refactor and further clarify comments
EliahKaganFeb 22, 2024
dc6b90f
Fix version_info type hint + more refactoring
EliahKaganFeb 22, 2024
ac20325
Test version_info parsing
EliahKaganFeb 22, 2024
629fd87
Fix how version_info omits non-numeric fields
EliahKaganFeb 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 40 additions & 22 deletionsgit/cmd.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@
import re
import contextlib
import io
import itertools
import logging
import os
import signal
Expand All@@ -25,7 +26,6 @@
UnsafeProtocolError,
)
from git.util import (
LazyMixin,
cygpath,
expand_path,
is_cygwin_git,
Expand DownExpand Up@@ -287,7 +287,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::
Expand All@@ -307,12 +307,18 @@ class Git(LazyMixin):
"cat_file_all",
"cat_file_header",
"_version_info",
"_version_info_token",
"_git_options",
"_persistent_git_options",
"_environment",
)

_excluded_ = ("cat_file_all", "cat_file_header", "_version_info")
_excluded_ = (
"cat_file_all",
"cat_file_header",
"_version_info",
"_version_info_token",
)

re_unsafe_protocol = re.compile(r"(.+)::.+")

Expand DownExpand Up@@ -359,6 +365,8 @@ def __setstate__(self, d: Dict[str, Any]) -> None:
the top level ``__init__``.
"""

_refresh_token = object() # Since None would match an initial _version_info_token.

@classmethod
def refresh(cls, path: Union[None, PathLike] = None) -> bool:
"""This gets called by the refresh function (see the top level __init__)."""
Expand All@@ -371,7 +379,9 @@ def refresh(cls, path: Union[None, PathLike] = None) -> bool:

# Keep track of the old and new git executable path.
old_git = cls.GIT_PYTHON_GIT_EXECUTABLE
old_refresh_token = cls._refresh_token
cls.GIT_PYTHON_GIT_EXECUTABLE = new_git
cls._refresh_token = object()

# Test if the new git executable path is valid. A GitCommandNotFound error is
# spawned by us. A PermissionError is spawned if the git executable cannot be
Expand DownExpand Up@@ -400,6 +410,7 @@ def refresh(cls, path: Union[None, PathLike] = None) -> bool:

# Revert to whatever the old_git was.
cls.GIT_PYTHON_GIT_EXECUTABLE = old_git
cls._refresh_token = old_refresh_token

if old_git is None:
# On the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is None) we only
Expand DownExpand Up@@ -783,6 +794,10 @@ def __init__(self, working_dir: Union[None, PathLike] = None):
# Extra environment variables to pass to git commands
self._environment: Dict[str, str] = {}

# Cached version slots
self._version_info: Union[Tuple[int, ...], None] = None
self._version_info_token: object = None

# Cached command slots
self.cat_file_header: Union[None, TBD] = None
self.cat_file_all: Union[None, TBD] = None
Expand All@@ -795,8 +810,8 @@ def __getattr__(self, name: str) -> Any:
Callable object that will execute call :meth:`_call_process` with
your arguments.
"""
if name[0] =="_":
returnLazyMixin.__getattr__(self,name)
if name.startswith("_"):
returnsuper().__getattribute__(name)
return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)

def set_persistent_git_options(self, **kwargs: Any) -> None:
Expand All@@ -811,33 +826,36 @@ 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"""
return self._working_dir

@property
def version_info(self) -> Tuple[int,int, int, int]:
def version_info(self) -> Tuple[int,...]:
"""
:return:tuple(int, int, int, int)tuple with integers representing the major, minor
and additionalversion numbers as parsed from git version.
:return:tuple with integers representing the major, minor and additional
version numbers as parsed from git version. Up to four fields are used.

This value is generated on demand and is cached.
"""
# Refreshing is global, but version_info caching is per-instance.
refresh_token = self._refresh_token # Copy token in case of concurrent refresh.

# Use the cached version if obtained after the most recent refresh.
if self._version_info_token is refresh_token:
assert self._version_info is not None, "Bug: corrupted token-check state"
return self._version_info

# Run "git version" and parse it.
process_version = self._call_process("version")
version_string = process_version.split(" ")[2]
version_fields = version_string.split(".")[:4]
leading_numeric_fields = itertools.takewhile(str.isdigit, version_fields)
self._version_info = tuple(map(int, leading_numeric_fields))

# This value will be considered valid until the next refresh.
self._version_info_token = refresh_token
return self._version_info

@overload
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp