From 06e12aaa418d29ea14a4a1c5b8d05bc1d04ac277 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Wed, 26 Oct 2022 16:24:19 +0800 Subject: [PATCH] Fix multi-digit version in entry point replacement Previously, the special case to generate 'pip' and 'easy_install' entry points with the correct Python version (e.g. 'pip3.9' on Python 3.9) only accounted for single-digit version segments, and did not work correctly on Python 3.10 and up. This was missed when Python 3.10 was released because we (accidentally) generated wheels that did not need any such replacements, but was exposed in CPython 3.11.0 since it bundled pip 22.3 generated against Python 3.10. --- src/pip/_internal/operations/install/wheel.py | 4 +-- tests/unit/test_wheel.py | 36 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py index 1650d59a374..c79941398a2 100644 --- a/src/pip/_internal/operations/install/wheel.py +++ b/src/pip/_internal/operations/install/wheel.py @@ -325,7 +325,7 @@ def get_console_script_specs(console: Dict[str, str]) -> List[str]: scripts_to_generate.append(f"pip{get_major_minor_version()} = {pip_script}") # Delete any other versioned pip entry points - pip_ep = [k for k in console if re.match(r"pip(\d(\.\d)?)?$", k)] + pip_ep = [k for k in console if re.match(r"pip(\d+(\.\d+)?)?$", k)] for k in pip_ep: del console[k] easy_install_script = console.pop("easy_install", None) @@ -340,7 +340,7 @@ def get_console_script_specs(console: Dict[str, str]) -> List[str]: ) # Delete any other versioned easy_install entry points easy_install_ep = [ - k for k in console if re.match(r"easy_install(-\d\.\d)?$", k) + k for k in console if re.match(r"easy_install(-\d+\.\d+)?$", k) ] for k in easy_install_ep: del console[k] diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py index 6aec64702d2..14bbfd3b4b7 100644 --- a/tests/unit/test_wheel.py +++ b/tests/unit/test_wheel.py @@ -3,6 +3,7 @@ import logging import os import pathlib +import sys import textwrap from email import message_from_string from pathlib import Path @@ -22,7 +23,11 @@ from pip._internal.models.scheme import Scheme from pip._internal.operations.build.wheel_legacy import get_legacy_build_wheel_path from pip._internal.operations.install import wheel -from pip._internal.operations.install.wheel import InstalledCSVRow, RecordPath +from pip._internal.operations.install.wheel import ( + InstalledCSVRow, + RecordPath, + get_console_script_specs, +) from pip._internal.utils.compat import WINDOWS from pip._internal.utils.misc import hash_file from pip._internal.utils.unpacking import unpack_file @@ -681,3 +686,32 @@ def test_rehash(self, tmpdir: Path) -> None: h, length = wheel.rehash(os.fspath(self.test_file)) assert length == str(self.test_file_len) assert h == self.test_file_hash_encoded + + +def test_get_console_script_specs_replaces_python_version( + monkeypatch: pytest.MonkeyPatch, +) -> None: + # Fake Python version. + monkeypatch + monkeypatch.setattr(sys, "version_info", (10, 11)) + + entry_points = { + "pip": "real_pip", + "pip2": "whatever", + "pip2.7": "whatever", + "easy_install": "real_easy_install", + "easy_install-2.7": "whatever", + # The followings shouldn't be replaced. + "not_pip_or_easy_install-2": "whatever", + "not_pip_or_easy_install-2.7": "whatever", + } + specs = get_console_script_specs(entry_points) + assert specs == [ + "pip = real_pip", + "pip10 = real_pip", + "pip10.11 = real_pip", + "easy_install = real_easy_install", + "easy_install-10.11 = real_easy_install", + "not_pip_or_easy_install-2 = whatever", + "not_pip_or_easy_install-2.7 = whatever", + ]