From cf3ffdc8fba9e7c34d3afcdee8670d9f0bc58510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Fri, 3 Jul 2020 22:33:21 +0700 Subject: [PATCH] Use lazy wheel to obtain dep info for new resolver --- src/pip/_internal/cli/cmdoptions.py | 2 +- src/pip/_internal/cli/req_command.py | 1 + .../resolution/resolvelib/candidates.py | 57 +++++++++++++------ .../resolution/resolvelib/factory.py | 2 + .../resolution/resolvelib/resolver.py | 9 +++ 5 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/pip/_internal/cli/cmdoptions.py b/src/pip/_internal/cli/cmdoptions.py index 2a4c230f6b5..911914169ef 100644 --- a/src/pip/_internal/cli/cmdoptions.py +++ b/src/pip/_internal/cli/cmdoptions.py @@ -916,7 +916,7 @@ def check_list_path_option(options): metavar='feature', action='append', default=[], - choices=['2020-resolver'], + choices=['2020-resolver', 'fast-deps'], help='Enable new functionality, that may be backward incompatible.', ) # type: Callable[..., Option] diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py index 50a60c8ed5a..78b5ce6a141 100644 --- a/src/pip/_internal/cli/req_command.py +++ b/src/pip/_internal/cli/req_command.py @@ -271,6 +271,7 @@ def make_resolver( force_reinstall=force_reinstall, upgrade_strategy=upgrade_strategy, py_version_info=py_version_info, + lazy_wheel='fast-deps' in options.features_enabled, ) import pip._internal.resolution.legacy.resolver return pip._internal.resolution.legacy.resolver.Resolver( diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index 1ee46430292..1ae4c7171b6 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -1,16 +1,19 @@ import logging import sys +from pip._vendor.contextlib2 import suppress from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.packaging.version import Version from pip._internal.exceptions import HashError, MetadataInconsistent +from pip._internal.network.lazy_wheel import dist_from_wheel_url from pip._internal.req.constructors import ( install_req_from_editable, install_req_from_line, ) from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import dist_is_editable, normalize_version_info from pip._internal.utils.packaging import get_requires_python from pip._internal.utils.typing import MYPY_CHECK_RUNNING @@ -197,11 +200,21 @@ def _prepare_abstract_distribution(self): # type: () -> AbstractDistribution raise NotImplementedError("Override in subclass") - def _prepare(self): + def _check_metadata_consistency(self): # type: () -> None - if self._dist is not None: - return + """Check for consistency of project name and version of dist.""" + # TODO: (Longer term) Rather than abort, reject this candidate + # and backtrack. This would need resolvelib support. + dist = self._dist # type: Distribution + name = canonicalize_name(dist.project_name) + if self._name is not None and self._name != name: + raise MetadataInconsistent(self._ireq, "name", dist.project_name) + version = dist.parsed_version + if self._version is not None and self._version != version: + raise MetadataInconsistent(self._ireq, "version", dist.version) + def _prepare(self): + # type: () -> None try: abstract_dist = self._prepare_abstract_distribution() except HashError as e: @@ -210,24 +223,36 @@ def _prepare(self): self._dist = abstract_dist.get_pkg_resources_distribution() assert self._dist is not None, "Distribution already installed" + self._check_metadata_consistency() - # TODO: (Longer term) Rather than abort, reject this candidate - # and backtrack. This would need resolvelib support. - name = canonicalize_name(self._dist.project_name) - if self._name is not None and self._name != name: - raise MetadataInconsistent( - self._ireq, "name", self._dist.project_name, - ) - version = self._dist.parsed_version - if self._version is not None and self._version != version: - raise MetadataInconsistent( - self._ireq, "version", self._dist.version, - ) + def _fetch_metadata(self): + # type: () -> None + """Fetch metadata, using lazy wheel if possible.""" + preparer = self._factory.preparer + use_lazy_wheel = self._factory.use_lazy_wheel + remote_wheel = self._link.is_wheel and not self._link.is_file + if use_lazy_wheel and remote_wheel and not preparer.require_hashes: + assert self._name is not None + logger.info('Collecting %s', self._ireq.req or self._ireq) + # TODO: Rename to HTTPRangeRequestUnsupported (GH-8584) + # If RuntimeError is raised, fallback to self._prepare + with indent_log(), suppress(RuntimeError): + logger.info( + 'Obtaining dependency information from %s %s', + self._name, self._version, + ) + url = self._link.url.split('#', 1)[0] + session = preparer.downloader._session + self._dist = dist_from_wheel_url(self._name, url, session) + self._check_metadata_consistency() + if self._dist is None: + self._prepare() @property def dist(self): # type: () -> Distribution - self._prepare() + if self._dist is None: + self._fetch_metadata() return self._dist def _get_requires_python_specifier(self): diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index 81f887c6b1e..1a2e57f2649 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -82,6 +82,7 @@ def __init__( ignore_installed, # type: bool ignore_requires_python, # type: bool py_version_info=None, # type: Optional[Tuple[int, ...]] + lazy_wheel=False, # type: bool ): # type: (...) -> None self._finder = finder @@ -92,6 +93,7 @@ def __init__( self._use_user_site = use_user_site self._force_reinstall = force_reinstall self._ignore_requires_python = ignore_requires_python + self.use_lazy_wheel = lazy_wheel self._link_candidate_cache = {} # type: Cache[LinkCandidate] self._editable_candidate_cache = {} # type: Cache[EditableCandidate] diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py index d2ac9d0418a..43ea248632d 100644 --- a/src/pip/_internal/resolution/resolvelib/resolver.py +++ b/src/pip/_internal/resolution/resolvelib/resolver.py @@ -49,8 +49,16 @@ def __init__( force_reinstall, # type: bool upgrade_strategy, # type: str py_version_info=None, # type: Optional[Tuple[int, ...]] + lazy_wheel=False, # type: bool ): super(Resolver, self).__init__() + if lazy_wheel: + logger.warning( + 'pip is using lazily downloaded wheels using HTTP ' + 'range requests to obtain dependency information. ' + 'This experimental feature is enabled through ' + '--use-feature=fast-deps and it is not ready for production.' + ) assert upgrade_strategy in self._allowed_strategies @@ -64,6 +72,7 @@ def __init__( ignore_installed=ignore_installed, ignore_requires_python=ignore_requires_python, py_version_info=py_version_info, + lazy_wheel=lazy_wheel, ) self.ignore_dependencies = ignore_dependencies self.upgrade_strategy = upgrade_strategy