|
5 | 5 | from pip._internal.exceptions import (
|
6 | 6 | DistributionNotFound,
|
7 | 7 | InstallationError,
|
| 8 | + InstallationSubprocessError, |
| 9 | + MetadataInconsistent, |
8 | 10 | UnsupportedPythonVersion,
|
9 | 11 | UnsupportedWheel,
|
10 | 12 | )
|
|
33 | 35 | ExplicitRequirement,
|
34 | 36 | RequiresPythonRequirement,
|
35 | 37 | SpecifierRequirement,
|
| 38 | + UnsatisfiableRequirement, |
36 | 39 | )
|
37 | 40 |
|
38 | 41 | if MYPY_CHECK_RUNNING:
|
@@ -94,6 +97,7 @@ def __init__(
|
94 | 97 | self._force_reinstall = force_reinstall
|
95 | 98 | self._ignore_requires_python = ignore_requires_python
|
96 | 99 |
|
| 100 | + self._build_failures = {} # type: Cache[InstallationError] |
97 | 101 | self._link_candidate_cache = {} # type: Cache[LinkCandidate]
|
98 | 102 | self._editable_candidate_cache = {} # type: Cache[EditableCandidate]
|
99 | 103 | self._installed_candidate_cache = {
|
@@ -136,21 +140,40 @@ def _make_candidate_from_link(
|
136 | 140 | name, # type: Optional[str]
|
137 | 141 | version, # type: Optional[_BaseVersion]
|
138 | 142 | ):
|
139 |
| - # type: (...) -> Candidate |
| 143 | + # type: (...) -> Optional[Candidate] |
140 | 144 | # TODO: Check already installed candidate, and use it if the link and
|
141 | 145 | # editable flag match.
|
| 146 | + |
| 147 | + if link in self._build_failures: |
| 148 | + # We already tried this candidate before, and it does not build. |
| 149 | + # Don't bother trying again. |
| 150 | + return None |
| 151 | + |
142 | 152 | if template.editable:
|
143 | 153 | if link not in self._editable_candidate_cache:
|
144 |
| - self._editable_candidate_cache[link] = EditableCandidate( |
145 |
| - link, template, factory=self, name=name, version=version, |
146 |
| - ) |
| 154 | + try: |
| 155 | + self._editable_candidate_cache[link] = EditableCandidate( |
| 156 | + link, template, factory=self, |
| 157 | + name=name, version=version, |
| 158 | + ) |
| 159 | + except (InstallationSubprocessError, MetadataInconsistent) as e: |
| 160 | + logger.warning("Discarding %s. %s", link, e) |
| 161 | + self._build_failures[link] = e |
| 162 | + return None |
147 | 163 | base = self._editable_candidate_cache[link] # type: BaseCandidate
|
148 | 164 | else:
|
149 | 165 | if link not in self._link_candidate_cache:
|
150 |
| - self._link_candidate_cache[link] = LinkCandidate( |
151 |
| - link, template, factory=self, name=name, version=version, |
152 |
| - ) |
| 166 | + try: |
| 167 | + self._link_candidate_cache[link] = LinkCandidate( |
| 168 | + link, template, factory=self, |
| 169 | + name=name, version=version, |
| 170 | + ) |
| 171 | + except (InstallationSubprocessError, MetadataInconsistent) as e: |
| 172 | + logger.warning("Discarding %s. %s", link, e) |
| 173 | + self._build_failures[link] = e |
| 174 | + return None |
153 | 175 | base = self._link_candidate_cache[link]
|
| 176 | + |
154 | 177 | if extras:
|
155 | 178 | return ExtrasCandidate(base, extras)
|
156 | 179 | return base
|
@@ -210,13 +233,16 @@ def iter_index_candidates():
|
210 | 233 | for ican in reversed(icans):
|
211 | 234 | if not all_yanked and ican.link.is_yanked:
|
212 | 235 | continue
|
213 |
| - yield self._make_candidate_from_link( |
| 236 | + candidate = self._make_candidate_from_link( |
214 | 237 | link=ican.link,
|
215 | 238 | extras=extras,
|
216 | 239 | template=template,
|
217 | 240 | name=name,
|
218 | 241 | version=ican.version,
|
219 | 242 | )
|
| 243 | + if candidate is None: |
| 244 | + continue |
| 245 | + yield candidate |
220 | 246 |
|
221 | 247 | return FoundCandidates(
|
222 | 248 | iter_index_candidates,
|
@@ -280,6 +306,16 @@ def make_requirement_from_install_req(self, ireq, requested_extras):
|
280 | 306 | name=canonicalize_name(ireq.name) if ireq.name else None,
|
281 | 307 | version=None,
|
282 | 308 | )
|
| 309 | + if cand is None: |
| 310 | + # There's no way we can satisfy a URL requirement if the underlying |
| 311 | + # candidate fails to build. An unnamed URL must be user-supplied, so |
| 312 | + # we fail eagerly. If the URL is named, an unsatisfiable requirement |
| 313 | + # can make the resolver do the right thing, either backtrack (and |
| 314 | + # maybe find some other requirement that's buildable) or raise a |
| 315 | + # ResolutionImpossible eventually. |
| 316 | + if not ireq.name: |
| 317 | + raise self._build_failures[ireq.link] |
| 318 | + return UnsatisfiableRequirement(canonicalize_name(ireq.name)) |
283 | 319 | return self.make_requirement_from_candidate(cand)
|
284 | 320 |
|
285 | 321 | def make_requirement_from_candidate(self, candidate):
|
|
0 commit comments