Skip to content

Commit

Permalink
Introduce BuildPurpose concept
Browse files Browse the repository at this point in the history
This allows deciding if build are allow to create editables or not.
It follows the PEP 660 principle stating that editables are only
meant as a transient artifact during installation.
It also makes the implementation of should_build slightly
easier to read.
  • Loading branch information
sbidoul committed Sep 21, 2021
1 parent ae0bda5 commit 53419b6
Show file tree
Hide file tree
Showing 16 changed files with 56 additions and 36 deletions.
4 changes: 3 additions & 1 deletion src/pip/_internal/cli/req_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -234,6 +234,7 @@ def make_requirement_preparer(
session: PipSession,
finder: PackageFinder,
use_user_site: bool,
purpose: BuildPurpose,
download_dir: Optional[str] = None,
) -> RequirementPreparer:
"""
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/pip/_internal/commands/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 3 additions & 1 deletion src/pip/_internal/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
9 changes: 3 additions & 6 deletions src/pip/_internal/commands/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/pip/_internal/distributions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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()
3 changes: 2 additions & 1 deletion src/pip/_internal/distributions/installed.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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
14 changes: 9 additions & 5 deletions src/pip/_internal/distributions/sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand All @@ -24,19 +25,22 @@ 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()

# 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:
Expand Down Expand Up @@ -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()
Expand Down
3 changes: 2 additions & 1 deletion src/pip/_internal/distributions/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
FilesystemWheel,
get_wheel_distribution,
)
from pip._internal.req import BuildPurpose


class WheelDistribution(AbstractDistribution):
Expand All @@ -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
9 changes: 7 additions & 2 deletions src/pip/_internal/operations/prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()


Expand Down Expand Up @@ -279,6 +280,7 @@ def __init__(
use_user_site: bool,
lazy_wheel: bool,
in_tree_build: bool,
purpose: BuildPurpose,
) -> None:
super().__init__()

Expand All @@ -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.
Expand Down Expand Up @@ -558,6 +561,7 @@ def _prepare_linked_requirement(
self.req_tracker,
self.finder,
self.build_isolation,
self.purpose,
)
return dist

Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/pip/_internal/req/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 0 additions & 2 deletions src/pip/_internal/req/constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down
12 changes: 8 additions & 4 deletions src/pip/_internal/req/req_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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()
Expand Down
1 change: 0 additions & 1 deletion src/pip/_internal/resolution/resolvelib/candidates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 53419b6

Please sign in to comment.