diff --git a/src/pip/_internal/cli/req_command.py b/src/pip/_internal/cli/req_command.py index 4129bf7e14c..be223e547ac 100644 --- a/src/pip/_internal/cli/req_command.py +++ b/src/pip/_internal/cli/req_command.py @@ -30,7 +30,7 @@ install_req_from_req_string, ) from pip._internal.req.req_file import parse_requirements -from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_install import BuildPurpose, InstallRequirement from pip._internal.req.req_tracker import RequirementTracker from pip._internal.resolution.base import BaseResolver from pip._internal.self_outdated_check import pip_self_version_check @@ -234,6 +234,7 @@ def make_requirement_preparer( session: PipSession, finder: PackageFinder, use_user_site: bool, + purpose: BuildPurpose, download_dir: Optional[str] = None, ) -> RequirementPreparer: """ @@ -273,6 +274,7 @@ def make_requirement_preparer( use_user_site=use_user_site, lazy_wheel=lazy_wheel, in_tree_build="in-tree-build" in options.features_enabled, + purpose=purpose, ) @classmethod diff --git a/src/pip/_internal/commands/download.py b/src/pip/_internal/commands/download.py index 2f6aac29e83..9808c878f3b 100644 --- a/src/pip/_internal/commands/download.py +++ b/src/pip/_internal/commands/download.py @@ -7,6 +7,7 @@ from pip._internal.cli.cmdoptions import make_target_python from pip._internal.cli.req_command import RequirementCommand, with_cleanup from pip._internal.cli.status_codes import SUCCESS +from pip._internal.req.req_install import BuildPurpose from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.utils.misc import ensure_dir, normalize_path, write_output from pip._internal.utils.temp_dir import TempDirectory @@ -114,6 +115,7 @@ def run(self, options: Values, args: List[str]) -> int: finder=finder, download_dir=options.download_dir, use_user_site=False, + purpose=BuildPurpose.WHEEL, ) resolver = self.make_resolver( diff --git a/src/pip/_internal/commands/install.py b/src/pip/_internal/commands/install.py index a427c6c5929..567940d5bdf 100644 --- a/src/pip/_internal/commands/install.py +++ b/src/pip/_internal/commands/install.py @@ -23,7 +23,7 @@ from pip._internal.models.format_control import FormatControl from pip._internal.operations.check import ConflictDetails, check_install_conflicts from pip._internal.req import install_given_reqs -from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_install import BuildPurpose, InstallRequirement from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.utils.compat import WINDOWS from pip._internal.utils.distutils_args import parse_distutils_args @@ -315,6 +315,7 @@ def run(self, options: Values, args: List[str]) -> int: session=session, finder=finder, use_user_site=options.use_user_site, + purpose=BuildPurpose.INSTALL, ) resolver = self.make_resolver( preparer=preparer, @@ -359,6 +360,7 @@ def run(self, options: Values, args: List[str]) -> int: verify=True, build_options=[], global_options=[], + purpose=BuildPurpose.INSTALL, ) # If we're using PEP 517, we cannot do a direct install diff --git a/src/pip/_internal/commands/wheel.py b/src/pip/_internal/commands/wheel.py index 87a20f58c51..1e31d1c752f 100644 --- a/src/pip/_internal/commands/wheel.py +++ b/src/pip/_internal/commands/wheel.py @@ -9,7 +9,7 @@ from pip._internal.cli.req_command import RequirementCommand, with_cleanup from pip._internal.cli.status_codes import SUCCESS from pip._internal.exceptions import CommandError -from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_install import BuildPurpose, InstallRequirement from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.utils.misc import ensure_dir, normalize_path from pip._internal.utils.temp_dir import TempDirectory @@ -121,11 +121,6 @@ def run(self, options: Values, args: List[str]) -> int: reqs = self.get_requirements(args, options, finder, session) - # Since we want to build regular wheels, disable the building of - # editable wheels. - for req in reqs: - req.no_build_editable = True - preparer = self.make_requirement_preparer( temp_build_dir=directory, options=options, @@ -134,6 +129,7 @@ def run(self, options: Values, args: List[str]) -> int: finder=finder, download_dir=options.wheel_dir, use_user_site=False, + purpose=BuildPurpose.WHEEL, ) resolver = self.make_resolver( @@ -163,6 +159,7 @@ def run(self, options: Values, args: List[str]) -> int: verify=(not options.no_verify), build_options=options.build_options or [], global_options=options.global_options or [], + purpose=BuildPurpose.WHEEL, ) for req in build_successes: assert req.link and req.link.is_wheel diff --git a/src/pip/_internal/distributions/base.py b/src/pip/_internal/distributions/base.py index 149fff55dab..dfb824eb48c 100644 --- a/src/pip/_internal/distributions/base.py +++ b/src/pip/_internal/distributions/base.py @@ -2,7 +2,7 @@ from pip._internal.index.package_finder import PackageFinder from pip._internal.metadata.base import BaseDistribution -from pip._internal.req import InstallRequirement +from pip._internal.req import BuildPurpose, InstallRequirement class AbstractDistribution(metaclass=abc.ABCMeta): @@ -31,6 +31,6 @@ def get_metadata_distribution(self) -> BaseDistribution: @abc.abstractmethod def prepare_distribution_metadata( - self, finder: PackageFinder, build_isolation: bool + self, finder: PackageFinder, build_isolation: bool, purpose: BuildPurpose ) -> None: raise NotImplementedError() diff --git a/src/pip/_internal/distributions/installed.py b/src/pip/_internal/distributions/installed.py index be5962f9800..95d776a2c82 100644 --- a/src/pip/_internal/distributions/installed.py +++ b/src/pip/_internal/distributions/installed.py @@ -1,6 +1,7 @@ from pip._internal.distributions.base import AbstractDistribution from pip._internal.index.package_finder import PackageFinder from pip._internal.metadata import BaseDistribution +from pip._internal.req import BuildPurpose class InstalledDistribution(AbstractDistribution): @@ -15,6 +16,6 @@ def get_metadata_distribution(self) -> BaseDistribution: return self.req.satisfied_by def prepare_distribution_metadata( - self, finder: PackageFinder, build_isolation: bool + self, finder: PackageFinder, build_isolation: bool, purpose: BuildPurpose ) -> None: pass diff --git a/src/pip/_internal/distributions/sdist.py b/src/pip/_internal/distributions/sdist.py index 1deb525d7c2..33aa20a95ad 100644 --- a/src/pip/_internal/distributions/sdist.py +++ b/src/pip/_internal/distributions/sdist.py @@ -6,6 +6,7 @@ from pip._internal.exceptions import InstallationError from pip._internal.index.package_finder import PackageFinder from pip._internal.metadata import BaseDistribution +from pip._internal.req import BuildPurpose from pip._internal.utils.subprocess import runner_with_spinner_message logger = logging.getLogger(__name__) @@ -24,7 +25,10 @@ def get_metadata_distribution(self) -> BaseDistribution: return _Dist(self.req.get_dist()) def prepare_distribution_metadata( - self, finder: PackageFinder, build_isolation: bool + self, + finder: PackageFinder, + build_isolation: bool, + purpose: BuildPurpose, ) -> None: # Load pyproject.toml, to determine whether PEP 517 is to be used self.req.load_pyproject_toml() @@ -32,11 +36,11 @@ def prepare_distribution_metadata( # Set up the build isolation, if this requirement should be isolated should_isolate = self.req.use_pep517 and build_isolation if should_isolate: - self._setup_isolation(finder) + self._setup_isolation(finder, purpose) - self.req.prepare_metadata() + self.req.prepare_metadata(purpose) - def _setup_isolation(self, finder: PackageFinder) -> None: + def _setup_isolation(self, finder: PackageFinder, purpose: BuildPurpose) -> None: def _raise_conflicts( conflicting_with: str, conflicting_reqs: Set[Tuple[str, str]] ) -> None: @@ -90,7 +94,7 @@ def _raise_conflicts( backend = self.req.pep517_backend assert backend is not None with backend.subprocess_runner(runner): - if self.req.editable and not self.req.no_build_editable: + if self.req.editable and purpose == BuildPurpose.INSTALL: reqs = backend.get_requires_for_build_editable() else: reqs = backend.get_requires_for_build_wheel() diff --git a/src/pip/_internal/distributions/wheel.py b/src/pip/_internal/distributions/wheel.py index 340b0f3c5c7..c3de1ed87fa 100644 --- a/src/pip/_internal/distributions/wheel.py +++ b/src/pip/_internal/distributions/wheel.py @@ -7,6 +7,7 @@ FilesystemWheel, get_wheel_distribution, ) +from pip._internal.req import BuildPurpose class WheelDistribution(AbstractDistribution): @@ -26,6 +27,6 @@ def get_metadata_distribution(self) -> BaseDistribution: return get_wheel_distribution(wheel, canonicalize_name(self.req.name)) def prepare_distribution_metadata( - self, finder: PackageFinder, build_isolation: bool + self, finder: PackageFinder, build_isolation: bool, purpose: BuildPurpose ) -> None: pass diff --git a/src/pip/_internal/operations/prepare.py b/src/pip/_internal/operations/prepare.py index 67ef9dd8313..3c23360ddc1 100644 --- a/src/pip/_internal/operations/prepare.py +++ b/src/pip/_internal/operations/prepare.py @@ -33,7 +33,7 @@ dist_from_wheel_url, ) from pip._internal.network.session import PipSession -from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_install import BuildPurpose, InstallRequirement from pip._internal.req.req_tracker import RequirementTracker from pip._internal.utils.deprecation import deprecated from pip._internal.utils.filesystem import copy2_fixed @@ -52,11 +52,12 @@ def _get_prepared_distribution( req_tracker: RequirementTracker, finder: PackageFinder, build_isolation: bool, + purpose: BuildPurpose, ) -> BaseDistribution: """Prepare a distribution for installation.""" abstract_dist = make_distribution_for_install_requirement(req) with req_tracker.track(req): - abstract_dist.prepare_distribution_metadata(finder, build_isolation) + abstract_dist.prepare_distribution_metadata(finder, build_isolation, purpose) return abstract_dist.get_metadata_distribution() @@ -279,6 +280,7 @@ def __init__( use_user_site: bool, lazy_wheel: bool, in_tree_build: bool, + purpose: BuildPurpose, ) -> None: super().__init__() @@ -289,6 +291,7 @@ def __init__( self._download = Downloader(session, progress_bar) self._batch_download = BatchDownloader(session, progress_bar) self.finder = finder + self.purpose = purpose # Where still-packed archives should be written to. If None, they are # not saved, and are deleted immediately after unpacking. @@ -558,6 +561,7 @@ def _prepare_linked_requirement( self.req_tracker, self.finder, self.build_isolation, + self.purpose, ) return dist @@ -611,6 +615,7 @@ def prepare_editable_requirement( self.req_tracker, self.finder, self.build_isolation, + self.purpose, ) req.check_if_exists(self.use_user_site) diff --git a/src/pip/_internal/req/__init__.py b/src/pip/_internal/req/__init__.py index 70dea27a6a8..71cba9f7c13 100644 --- a/src/pip/_internal/req/__init__.py +++ b/src/pip/_internal/req/__init__.py @@ -5,11 +5,12 @@ from pip._internal.utils.logging import indent_log from .req_file import parse_requirements -from .req_install import InstallRequirement +from .req_install import BuildPurpose, InstallRequirement from .req_set import RequirementSet __all__ = [ "RequirementSet", + "BuildPurpose", "InstallRequirement", "parse_requirements", "install_given_reqs", diff --git a/src/pip/_internal/req/constructors.py b/src/pip/_internal/req/constructors.py index 5a4563b4fa7..006143677c1 100644 --- a/src/pip/_internal/req/constructors.py +++ b/src/pip/_internal/req/constructors.py @@ -181,7 +181,6 @@ def install_req_from_editable( options: Optional[Dict[str, Any]] = None, constraint: bool = False, user_supplied: bool = False, - no_build_editable: bool = False, ) -> InstallRequirement: parts = parse_req_from_editable(editable_req) @@ -191,7 +190,6 @@ def install_req_from_editable( comes_from=comes_from, user_supplied=user_supplied, editable=True, - no_build_editable=no_build_editable, link=parts.link, constraint=constraint, use_pep517=use_pep517, diff --git a/src/pip/_internal/req/req_install.py b/src/pip/_internal/req/req_install.py index 0e360027bec..188ec55feba 100644 --- a/src/pip/_internal/req/req_install.py +++ b/src/pip/_internal/req/req_install.py @@ -7,6 +7,7 @@ import sys import uuid import zipfile +from enum import Enum from typing import Any, Dict, Iterable, List, Optional, Sequence, Union from pip._vendor import pkg_resources @@ -60,6 +61,11 @@ logger = logging.getLogger(__name__) +class BuildPurpose(Enum): + WHEEL = 1 + INSTALL = 2 + + def _get_dist(metadata_directory: str) -> Distribution: """Return a pkg_resources.Distribution for the provided metadata directory. @@ -108,14 +114,12 @@ def __init__( constraint: bool = False, extras: Iterable[str] = (), user_supplied: bool = False, - no_build_editable: bool = False, ) -> None: assert req is None or isinstance(req, Requirement), req self.req = req self.comes_from = comes_from self.constraint = constraint self.editable = editable - self.no_build_editable = no_build_editable self.legacy_install_reason: Optional[int] = None # source_dir is the local directory where the linked requirement is @@ -574,7 +578,7 @@ def _generate_metadata(self) -> str: details=self.name or f"from {self.link}", ) - def prepare_metadata(self) -> None: + def prepare_metadata(self, purpose: BuildPurpose) -> None: """Ensure that project metadata is available. Under PEP 517, call the backend hook to prepare the metadata. @@ -583,7 +587,7 @@ def prepare_metadata(self) -> None: assert self.source_dir with indent_log(): - if self.editable and not self.no_build_editable: + if self.editable and purpose == BuildPurpose.INSTALL: self.metadata_directory = self._generate_metadata_for_editable() else: self.metadata_directory = self._generate_metadata() diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index d80c81ef802..5c6f9a04a97 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -82,7 +82,6 @@ def make_install_req_from_editable( use_pep517=template.use_pep517, isolated=template.isolated, constraint=template.constraint, - no_build_editable=template.no_build_editable, options=dict( install_options=template.install_options, global_options=template.global_options, diff --git a/src/pip/_internal/wheel_builder.py b/src/pip/_internal/wheel_builder.py index e7e94c41b96..21e18dca0ec 100644 --- a/src/pip/_internal/wheel_builder.py +++ b/src/pip/_internal/wheel_builder.py @@ -17,7 +17,7 @@ from pip._internal.models.wheel import Wheel from pip._internal.operations.build.wheel import build_wheel_pep517 from pip._internal.operations.build.wheel_legacy import build_wheel_legacy -from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_install import BuildPurpose, InstallRequirement from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed from pip._internal.utils.setuptools_build import make_setuptools_clean_args @@ -44,7 +44,7 @@ def _contains_egg_info(s: str) -> bool: def _should_build( req: InstallRequirement, - need_wheel: bool, + purpose: BuildPurpose, check_binary_allowed: BinaryAllowedPredicate, ) -> bool: """Return whether an InstallRequirement should be built into a wheel.""" @@ -52,19 +52,19 @@ def _should_build( # never build requirements that are merely constraints return False if req.is_wheel: - if need_wheel: + if purpose == BuildPurpose.WHEEL: logger.info( "Skipping %s, due to already being wheel.", req.name, ) return False - if need_wheel: + if purpose == BuildPurpose.WHEEL: # i.e. pip wheel, not pip install return True # From this point, this concerns the pip install command only - # (need_wheel=False). + # (purpose=INSTALL). if not req.source_dir: return False @@ -100,7 +100,7 @@ def _should_build( def should_build_for_wheel_command( req: InstallRequirement, ) -> bool: - return _should_build(req, need_wheel=True, check_binary_allowed=_always_true) + return _should_build(req, BuildPurpose.WHEEL, check_binary_allowed=_always_true) def should_build_for_install_command( @@ -108,7 +108,7 @@ def should_build_for_install_command( check_binary_allowed: BinaryAllowedPredicate, ) -> bool: return _should_build( - req, need_wheel=False, check_binary_allowed=check_binary_allowed + req, BuildPurpose.INSTALL, check_binary_allowed=check_binary_allowed ) @@ -317,6 +317,7 @@ def build( verify: bool, build_options: List[str], global_options: List[str], + purpose: BuildPurpose, ) -> BuildResult: """Build wheels. @@ -343,7 +344,7 @@ def build( verify, build_options, global_options, - req.editable and not req.no_build_editable, + editable=req.editable and purpose == BuildPurpose.INSTALL, ) if wheel_file: # Update the link for this. diff --git a/tests/unit/resolution_resolvelib/conftest.py b/tests/unit/resolution_resolvelib/conftest.py index 99c847a662b..cb7b042a22f 100644 --- a/tests/unit/resolution_resolvelib/conftest.py +++ b/tests/unit/resolution_resolvelib/conftest.py @@ -10,6 +10,7 @@ from pip._internal.models.selection_prefs import SelectionPreferences from pip._internal.network.session import PipSession from pip._internal.req.constructors import install_req_from_line +from pip._internal.req.req_install import BuildPurpose from pip._internal.req.req_tracker import get_requirement_tracker from pip._internal.resolution.resolvelib.factory import Factory from pip._internal.resolution.resolvelib.provider import PipProvider @@ -42,6 +43,7 @@ def preparer(finder): session=session, finder=finder, use_user_site=False, + purpose=BuildPurpose.WHEEL, ) yield preparer diff --git a/tests/unit/test_req.py b/tests/unit/test_req.py index f6f552a8fe7..5f118983406 100644 --- a/tests/unit/test_req.py +++ b/tests/unit/test_req.py @@ -20,7 +20,7 @@ ) from pip._internal.network.session import PipSession from pip._internal.operations.prepare import RequirementPreparer -from pip._internal.req import InstallRequirement, RequirementSet +from pip._internal.req import BuildPurpose, InstallRequirement, RequirementSet from pip._internal.req.constructors import ( _get_url_from_path, _looks_like_path, @@ -90,6 +90,7 @@ def _basic_resolver(self, finder, require_hashes=False): use_user_site=False, lazy_wheel=False, in_tree_build=False, + purpose=BuildPurpose.WHEEL, ) yield Resolver( preparer=preparer,