2 from typing
import Iterable
, Set
, Tuple
4 from pip
._internal
.build_env
import BuildEnvironment
5 from pip
._internal
.distributions
.base
import AbstractDistribution
6 from pip
._internal
.exceptions
import InstallationError
7 from pip
._internal
.index
.package_finder
import PackageFinder
8 from pip
._internal
.metadata
import BaseDistribution
9 from pip
._internal
.utils
.subprocess
import runner_with_spinner_message
11 logger
= logging
.getLogger(__name__
)
14 class SourceDistribution(AbstractDistribution
):
15 """Represents a source distribution.
17 The preparation step for these needs metadata for the packages to be
18 generated, either using PEP 517 or using the legacy `setup.py egg_info`.
21 def get_metadata_distribution(self
) -> BaseDistribution
:
22 return self
.req
.get_dist()
24 def prepare_distribution_metadata(
26 finder
: PackageFinder
,
27 build_isolation
: bool,
28 check_build_deps
: bool,
30 # Load pyproject.toml, to determine whether PEP 517 is to be used
31 self
.req
.load_pyproject_toml()
33 # Set up the build isolation, if this requirement should be isolated
34 should_isolate
= self
.req
.use_pep517
and build_isolation
36 # Setup an isolated environment and install the build backend static
38 self
._prepare
_build
_backend
(finder
)
39 # Check that if the requirement is editable, it either supports PEP 660 or
40 # has a setup.py or a setup.cfg. This cannot be done earlier because we need
41 # to setup the build backend to verify it supports build_editable, nor can
42 # it be done later, because we want to avoid installing build requirements
43 # needlessly. Doing it here also works around setuptools generating
44 # UNKNOWN.egg-info when running get_requires_for_build_wheel on a directory
45 # without setup.py nor setup.cfg.
46 self
.req
.isolated_editable_sanity_check()
47 # Install the dynamic build requirements.
48 self
._install
_build
_reqs
(finder
)
49 # Check if the current environment provides build dependencies
50 should_check_deps
= self
.req
.use_pep517
and check_build_deps
52 pyproject_requires
= self
.req
.pyproject_requires
53 assert pyproject_requires
is not None
54 conflicting
, missing
= self
.req
.build_env
.check_requirements(
58 self
._raise
_conflicts
("the backend dependencies", conflicting
)
60 self
._raise
_missing
_reqs
(missing
)
61 self
.req
.prepare_metadata()
63 def _prepare_build_backend(self
, finder
: PackageFinder
) -> None:
64 # Isolate in a BuildEnvironment and install the build-time
66 pyproject_requires
= self
.req
.pyproject_requires
67 assert pyproject_requires
is not None
69 self
.req
.build_env
= BuildEnvironment()
70 self
.req
.build_env
.install_requirements(
71 finder
, pyproject_requires
, "overlay", kind
="build dependencies"
73 conflicting
, missing
= self
.req
.build_env
.check_requirements(
74 self
.req
.requirements_to_check
77 self
._raise
_conflicts
("PEP 517/518 supported requirements", conflicting
)
80 "Missing build requirements in pyproject.toml for %s.",
84 "The project does not specify a build backend, and "
85 "pip cannot fall back to setuptools without %s.",
86 " and ".join(map(repr, sorted(missing
))),
89 def _get_build_requires_wheel(self
) -> Iterable
[str]:
90 with self
.req
.build_env
:
91 runner
= runner_with_spinner_message("Getting requirements to build wheel")
92 backend
= self
.req
.pep517_backend
93 assert backend
is not None
94 with backend
.subprocess_runner(runner
):
95 return backend
.get_requires_for_build_wheel()
97 def _get_build_requires_editable(self
) -> Iterable
[str]:
98 with self
.req
.build_env
:
99 runner
= runner_with_spinner_message(
100 "Getting requirements to build editable"
102 backend
= self
.req
.pep517_backend
103 assert backend
is not None
104 with backend
.subprocess_runner(runner
):
105 return backend
.get_requires_for_build_editable()
107 def _install_build_reqs(self
, finder
: PackageFinder
) -> None:
108 # Install any extra build dependencies that the backend requests.
109 # This must be done in a second pass, as the pyproject.toml
110 # dependencies must be installed before we can call the backend.
113 and self
.req
.permit_editable_wheels
114 and self
.req
.supports_pyproject_editable()
116 build_reqs
= self
._get
_build
_requires
_editable
()
118 build_reqs
= self
._get
_build
_requires
_wheel
()
119 conflicting
, missing
= self
.req
.build_env
.check_requirements(build_reqs
)
121 self
._raise
_conflicts
("the backend dependencies", conflicting
)
122 self
.req
.build_env
.install_requirements(
123 finder
, missing
, "normal", kind
="backend dependencies"
126 def _raise_conflicts(
127 self
, conflicting_with
: str, conflicting_reqs
: Set
[Tuple
[str, str]]
130 "Some build dependencies for {requirement} "
131 "conflict with {conflicting_with}: {description}."
133 error_message
= format_string
.format(
134 requirement
=self
.req
,
135 conflicting_with
=conflicting_with
,
136 description
=", ".join(
137 f
"{installed} is incompatible with {wanted}"
138 for installed
, wanted
in sorted(conflicting_reqs
)
141 raise InstallationError(error_message
)
143 def _raise_missing_reqs(self
, missing
: Set
[str]) -> None:
145 "Some build dependencies for {requirement} are missing: {missing}."
147 error_message
= format_string
.format(
148 requirement
=self
.req
, missing
=", ".join(map(repr, sorted(missing
)))
150 raise InstallationError(error_message
)