From 48fc77926ebf1e2aa450c18c71a12b012a85dbc1 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Thu, 1 Jul 2021 13:33:38 +0800 Subject: [PATCH] Re-implement Git version parsing with regex After packaging drops LegacyVersion, version.parse() will no longer guarantee to be able to parse Git versions, so we need to invent our own parser. Since we really only care about the major and minor segments, the logic is pretty simple. --- news/10117.removal.rst | 2 ++ src/pip/_internal/vcs/git.py | 30 ++++++++++++++---------------- tests/unit/test_vcs.py | 3 +-- 3 files changed, 17 insertions(+), 18 deletions(-) create mode 100644 news/10117.removal.rst diff --git a/news/10117.removal.rst b/news/10117.removal.rst new file mode 100644 index 00000000000..c0191a35358 --- /dev/null +++ b/news/10117.removal.rst @@ -0,0 +1,2 @@ +Git version parsing is now done with regular expression to prepare for the +pending upstream removal of non-PEP-440 version parsing logic. diff --git a/src/pip/_internal/vcs/git.py b/src/pip/_internal/vcs/git.py index b860e350a2d..269bf6a65b3 100644 --- a/src/pip/_internal/vcs/git.py +++ b/src/pip/_internal/vcs/git.py @@ -6,9 +6,6 @@ import urllib.request from typing import List, Optional, Tuple -from pip._vendor.packaging.version import _BaseVersion -from pip._vendor.packaging.version import parse as parse_version - from pip._internal.exceptions import BadCommand, InstallationError from pip._internal.utils.misc import HiddenText, display_path, hide_url from pip._internal.utils.subprocess import make_command @@ -29,6 +26,14 @@ logger = logging.getLogger(__name__) +GIT_VERSION_REGEX = re.compile( + r"^git version " # Prefix. + r"(\d+)" # Major. + r"\.(\d+)" # Dot, minor. + r"(?:\.(\d+))?" # Optional dot, patch. + r".*$" # Suffix, including any pre- and post-release segments we don't care about. +) + HASH_REGEX = re.compile('^[a-fA-F0-9]{40}$') # SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git' @@ -83,21 +88,14 @@ def is_immutable_rev_checkout(self, url, dest): ) return not is_tag_or_branch - def get_git_version(self): - # type: () -> _BaseVersion - VERSION_PFX = 'git version ' + def get_git_version(self) -> Tuple[int, ...]: version = self.run_command( ['version'], show_stdout=False, stdout_only=True ) - if version.startswith(VERSION_PFX): - version = version[len(VERSION_PFX):].split()[0] - else: - version = '' - # get first 3 positions of the git version because - # on windows it is x.y.z.windows.t, and this parses as - # LegacyVersion which always smaller than a Version. - version = '.'.join(version.split('.')[:3]) - return parse_version(version) + match = GIT_VERSION_REGEX.match(version) + if not match: + return () + return tuple(int(c) for c in match.groups()) @classmethod def get_current_branch(cls, location): @@ -301,7 +299,7 @@ def switch(self, dest, url, rev_options): def update(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None # First fetch changes from the default remote - if self.get_git_version() >= parse_version('1.9.0'): + if self.get_git_version() >= (1, 9): # fetch tags in addition to everything else self.run_command(['fetch', '-q', '--tags'], cwd=dest) else: diff --git a/tests/unit/test_vcs.py b/tests/unit/test_vcs.py index 305c45fd857..403f3946673 100644 --- a/tests/unit/test_vcs.py +++ b/tests/unit/test_vcs.py @@ -4,7 +4,6 @@ from unittest.mock import patch import pytest -from pip._vendor.packaging.version import parse as parse_version from pip._internal.exceptions import BadCommand, InstallationError from pip._internal.utils.misc import hide_url, hide_value @@ -483,7 +482,7 @@ def test_subversion__get_url_rev_options(): def test_get_git_version(): git_version = Git().get_git_version() - assert git_version >= parse_version('1.0.0') + assert git_version >= (1, 0, 0) @pytest.mark.parametrize('use_interactive,is_atty,expected', [