diff --git a/src/pip/_internal/cache.py b/src/pip/_internal/cache.py index 7aa72b9a7..7a431f9a2 100644 --- a/src/pip/_internal/cache.py +++ b/src/pip/_internal/cache.py @@ -11,12 +11,13 @@ import os from pip._vendor.packaging.utils import canonicalize_name +from pip._internal.exceptions import InvalidWheelFilename from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel from pip._internal.utils.compat import expanduser from pip._internal.utils.temp_dir import TempDirectory from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import path_to_url -from pip._internal.wheel import InvalidWheelFilename, Wheel if MYPY_CHECK_RUNNING: from typing import Optional, Set, List, Any diff --git a/src/pip/_internal/index/package_finder.py b/src/pip/_internal/index/package_finder.py index 3e034ba91..3c98de2c5 100644 --- a/src/pip/_internal/index/package_finder.py +++ b/src/pip/_internal/index/package_finder.py @@ -25,6 +25,7 @@ from pip._internal.models.format_control import FormatControl from pip._internal.models.link import Link from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.models.target_python import TargetPython +from pip._internal.models.wheel import Wheel from pip._internal.utils.filetypes import WHEEL_EXTENSION from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import build_netloc @@ -32,7 +33,6 @@ from pip._internal.utils.packaging import check_requires_python from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS from pip._internal.utils.urls import url_to_path -from pip._internal.wheel import Wheel if MYPY_CHECK_RUNNING: from typing import ( diff --git a/src/pip/_internal/models/wheel.py b/src/pip/_internal/models/wheel.py new file mode 100644 index 000000000..4c7a8af78 --- /dev/null +++ b/src/pip/_internal/models/wheel.py @@ -0,0 +1,79 @@ +"""Represents a wheel file and provides access to the various parts of the +name that have meaning. +""" +import re + +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.pep425tags import format_tag +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + from pip._internal.pep425tags import Pep425Tag + + +class Wheel(object): + """A wheel file""" + + wheel_file_re = re.compile( + r"""^(?P(?P.+?)-(?P.*?)) + ((-(?P\d[^-]*?))?-(?P.+?)-(?P.+?)-(?P.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) + + def __init__(self, filename): + # type: (str) -> None + """ + :raises InvalidWheelFilename: when the filename is invalid for a wheel + """ + wheel_info = self.wheel_file_re.match(filename) + if not wheel_info: + raise InvalidWheelFilename( + "%s is not a valid wheel filename." % filename + ) + self.filename = filename + self.name = wheel_info.group('name').replace('_', '-') + # we'll assume "_" means "-" due to wheel naming scheme + # (https://github.com/pypa/pip/issues/1150) + self.version = wheel_info.group('ver').replace('_', '-') + self.build_tag = wheel_info.group('build') + self.pyversions = wheel_info.group('pyver').split('.') + self.abis = wheel_info.group('abi').split('.') + self.plats = wheel_info.group('plat').split('.') + + # All the tag combinations from this file + self.file_tags = { + (x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + } + + def get_formatted_file_tags(self): + # type: () -> List[str] + """Return the wheel's tags as a sorted list of strings.""" + return sorted(format_tag(tag) for tag in self.file_tags) + + def support_index_min(self, tags): + # type: (List[Pep425Tag]) -> int + """Return the lowest index that one of the wheel's file_tag combinations + achieves in the given list of supported tags. + + For example, if there are 8 supported tags and one of the file tags + is first in the list, then return 0. + + :param tags: the PEP 425 tags to check the wheel against, in order + with most preferred first. + + :raises ValueError: If none of the wheel's file tags match one of + the supported tags. + """ + return min(tags.index(tag) for tag in self.file_tags if tag in tags) + + def supported(self, tags): + # type: (List[Pep425Tag]) -> bool + """Return whether the wheel is compatible with one of the given tags. + + :param tags: the PEP 425 tags to check the wheel against. + """ + return not self.file_tags.isdisjoint(tags) diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 70fb5e086..1f3cd8a10 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -23,6 +23,7 @@ from pip._vendor.pkg_resources import RequirementParseError, parse_requirements from pip._internal.exceptions import InstallationError from pip._internal.models.index import PyPI, TestPyPI from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel from pip._internal.pyproject import make_pyproject_path from pip._internal.req.req_install import InstallRequirement from pip._internal.utils.filetypes import ARCHIVE_EXTENSIONS @@ -30,7 +31,6 @@ from pip._internal.utils.misc import is_installable_dir, splitext from pip._internal.utils.typing import MYPY_CHECK_RUNNING from pip._internal.utils.urls import path_to_url from pip._internal.vcs import is_url, vcs -from pip._internal.wheel import Wheel if MYPY_CHECK_RUNNING: from typing import ( diff --git a/src/pip/_internal/req/req_set.py b/src/pip/_internal/req/req_set.py index f1ad97fc7..087ac5925 100644 --- a/src/pip/_internal/req/req_set.py +++ b/src/pip/_internal/req/req_set.py @@ -10,9 +10,9 @@ from pip._vendor.packaging.utils import canonicalize_name from pip._internal import pep425tags from pip._internal.exceptions import InstallationError +from pip._internal.models.wheel import Wheel from pip._internal.utils.logging import indent_log from pip._internal.utils.typing import MYPY_CHECK_RUNNING -from pip._internal.wheel import Wheel if MYPY_CHECK_RUNNING: from typing import Dict, Iterable, List, Optional, Tuple diff --git a/src/pip/_internal/wheel.py b/src/pip/_internal/wheel.py index e7b8c6bb6..98432427c 100644 --- a/src/pip/_internal/wheel.py +++ b/src/pip/_internal/wheel.py @@ -25,13 +25,8 @@ from pip._vendor.distlib.util import get_export_entry from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.six import StringIO -from pip._internal.exceptions import ( - InstallationError, - InvalidWheelFilename, - UnsupportedWheel, -) +from pip._internal.exceptions import InstallationError, UnsupportedWheel from pip._internal.locations import get_major_minor_version -from pip._internal.pep425tags import format_tag from pip._internal.utils.misc import captured_stdout, ensure_dir, hash_file from pip._internal.utils.typing import MYPY_CHECK_RUNNING @@ -42,7 +37,6 @@ if MYPY_CHECK_RUNNING: ) from pip._internal.models.scheme import Scheme - from pip._internal.pep425tags import Pep425Tag InstalledCSVRow = Tuple[str, ...] @@ -664,71 +658,3 @@ def check_compatibility(version, name): 'Installing from a newer Wheel-Version (%s)', '.'.join(map(str, version)), ) - - -class Wheel(object): - """A wheel file""" - - # TODO: Maybe move the class into the models sub-package - - wheel_file_re = re.compile( - r"""^(?P(?P.+?)-(?P.*?)) - ((-(?P\d[^-]*?))?-(?P.+?)-(?P.+?)-(?P.+?) - \.whl|\.dist-info)$""", - re.VERBOSE - ) - - def __init__(self, filename): - # type: (str) -> None - """ - :raises InvalidWheelFilename: when the filename is invalid for a wheel - """ - wheel_info = self.wheel_file_re.match(filename) - if not wheel_info: - raise InvalidWheelFilename( - "%s is not a valid wheel filename." % filename - ) - self.filename = filename - self.name = wheel_info.group('name').replace('_', '-') - # we'll assume "_" means "-" due to wheel naming scheme - # (https://github.com/pypa/pip/issues/1150) - self.version = wheel_info.group('ver').replace('_', '-') - self.build_tag = wheel_info.group('build') - self.pyversions = wheel_info.group('pyver').split('.') - self.abis = wheel_info.group('abi').split('.') - self.plats = wheel_info.group('plat').split('.') - - # All the tag combinations from this file - self.file_tags = { - (x, y, z) for x in self.pyversions - for y in self.abis for z in self.plats - } - - def get_formatted_file_tags(self): - # type: () -> List[str] - """Return the wheel's tags as a sorted list of strings.""" - return sorted(format_tag(tag) for tag in self.file_tags) - - def support_index_min(self, tags): - # type: (List[Pep425Tag]) -> int - """Return the lowest index that one of the wheel's file_tag combinations - achieves in the given list of supported tags. - - For example, if there are 8 supported tags and one of the file tags - is first in the list, then return 0. - - :param tags: the PEP 425 tags to check the wheel against, in order - with most preferred first. - - :raises ValueError: If none of the wheel's file tags match one of - the supported tags. - """ - return min(tags.index(tag) for tag in self.file_tags if tag in tags) - - def supported(self, tags): - # type: (List[Pep425Tag]) -> bool - """Return whether the wheel is compatible with one of the given tags. - - :param tags: the PEP 425 tags to check the wheel against. - """ - return not self.file_tags.isdisjoint(tags) diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py index 9fb5f8de9..ce4265668 100644 --- a/tests/unit/test_wheel.py +++ b/tests/unit/test_wheel.py @@ -13,12 +13,12 @@ from pip._internal.commands.wheel import WheelCommand from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel from pip._internal.locations import get_scheme from pip._internal.models.scheme import Scheme +from pip._internal.models.wheel import Wheel from pip._internal.utils.compat import WINDOWS from pip._internal.utils.misc import hash_file from pip._internal.utils.unpacking import unpack_file from pip._internal.wheel import ( MissingCallableSuffix, - Wheel, _raise_for_invalid_entrypoint, ) from pip._internal.wheel_builder import get_legacy_build_wheel_path