Skip to content

Commit de1c912

Browse files
jsiroispradyunsgsbidoul
authored
Merge pip 20.3.4 commits. (#9)
* Merge pull request pypa#9289 from uranusjr/new-resolver-lazy-insert * Merge pull request pypa#9315 from pradyunsg/better-search-errors * Merge pull request pypa#9369 from uranusjr/resolvelib-054 * Merge pull request pypa#9320 from uranusjr/wheel-check-valid Verify built wheel contains valid metadata * Merge pull request pypa#9432 from uranusjr/new-resolver-dedup-on-backtracking Avoid downloading multiple candidates of a same version * Revert "Remove on_returncode parameter from call_subprocess" This reverts commit ab3ee71. * Revert "Remove show_stdout from run_command args" This reverts commit 94882fd. * Revert "Create call_subprocess just for vcs commands" This reverts commit 8adbc21. * Revert "Improve check for svn version string" This reverts commit 1471897. * Revert "Bubble up SubProcessError to basecommand._main" This reverts commit e9f738a. * Additional revert of 7969 Revert additional changes that were made after 7969 and depended on it. * add stdout_only to call_subprocess * vcs: capture subprocess stdout only * Add test for call_subprocess stdout_only * Bump for release * Fix test bitrot. Setuptools released 52.0.0 which killed easy_install support and thus caused tests exercising easy_install to fail. Co-authored-by: Pradyun Gedam <3275593+pradyunsg@users.noreply.github.com> Co-authored-by: Stéphane Bidoul <stephane.bidoul@gmail.com> Co-authored-by: Pradyun Gedam <pradyunsg@users.noreply.github.com>
1 parent 06f4625 commit de1c912

24 files changed

+518
-254
lines changed

Diff for: NEWS.rst

+33
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,39 @@
99
1010
.. towncrier release notes start
1111
12+
20.3.4 (2021-01-23)
13+
===================
14+
15+
Features
16+
--------
17+
18+
- ``pip wheel`` now verifies the built wheel contains valid metadata, and can be
19+
installed by a subsequent ``pip install``. This can be disabled with
20+
``--no-verify``. (`#9206 <https://github.com/pypa/pip/issues/9206>`_)
21+
- Improve presentation of XMLRPC errors in pip search. (`#9315 <https://github.com/pypa/pip/issues/9315>`_)
22+
23+
Bug Fixes
24+
---------
25+
26+
- Fixed hanging VCS subprocess calls when the VCS outputs a large amount of data
27+
on stderr. Restored logging of VCS errors that was inadvertently removed in pip
28+
20.2. (`#8876 <https://github.com/pypa/pip/issues/8876>`_)
29+
- Fix error when an existing incompatibility is unable to be applied to a backtracked state. (`#9180 <https://github.com/pypa/pip/issues/9180>`_)
30+
- New resolver: Discard a faulty distribution, instead of quitting outright.
31+
This implementation is taken from 20.2.2, with a fix that always makes the
32+
resolver iterate through candidates from indexes lazily, to avoid downloading
33+
candidates we do not need. (`#9203 <https://github.com/pypa/pip/issues/9203>`_)
34+
- New resolver: Discard a source distribution if it fails to generate metadata,
35+
instead of quitting outright. This implementation is taken from 20.2.2, with a
36+
fix that always makes the resolver iterate through candidates from indexes
37+
lazily, to avoid downloading candidates we do not need. (`#9246 <https://github.com/pypa/pip/issues/9246>`_)
38+
39+
Vendored Libraries
40+
------------------
41+
42+
- Upgrade resolvelib to 0.5.4.
43+
44+
1245
20.3.3 (2020-12-15)
1346
===================
1447

Diff for: src/pip/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import List, Optional
55

66

7-
__version__ = "21.0.dev0"
7+
__version__ = "20.3.4"
88

99

1010
def main(args=None):

Diff for: src/pip/_internal/cli/base_command.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
InstallationError,
2828
NetworkConnectionError,
2929
PreviousBuildDirError,
30-
SubProcessError,
3130
UninstallationError,
3231
)
3332
from pip._internal.utils.deprecation import deprecated
@@ -230,7 +229,7 @@ def _main(self, args):
230229

231230
return PREVIOUS_BUILD_DIR_ERROR
232231
except (InstallationError, UninstallationError, BadCommand,
233-
SubProcessError, NetworkConnectionError) as exc:
232+
NetworkConnectionError) as exc:
234233
logger.critical(str(exc))
235234
logger.debug('Exception information:', exc_info=True)
236235

Diff for: src/pip/_internal/commands/install.py

+1
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ def run(self, options, args):
347347
_, build_failures = build(
348348
reqs_to_build,
349349
wheel_cache=wheel_cache,
350+
verify=True,
350351
build_options=[],
351352
global_options=[],
352353
)

Diff for: src/pip/_internal/commands/search.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,14 @@ def search(self, query, options):
7979

8080
transport = PipXmlrpcTransport(index_url, session)
8181
pypi = xmlrpc_client.ServerProxy(index_url, transport)
82-
hits = pypi.search({'name': query, 'summary': query}, 'or')
82+
try:
83+
hits = pypi.search({'name': query, 'summary': query}, 'or')
84+
except xmlrpc_client.Fault as fault:
85+
message = "XMLRPC request failed [code: {code}]\n{string}".format(
86+
code=fault.faultCode,
87+
string=fault.faultString,
88+
)
89+
raise CommandError(message)
8390
return hits
8491

8592

Diff for: src/pip/_internal/commands/wheel.py

+9
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ def add_options(self):
8181
self.cmd_opts.add_option(cmdoptions.build_dir())
8282
self.cmd_opts.add_option(cmdoptions.progress_bar())
8383

84+
self.cmd_opts.add_option(
85+
'--no-verify',
86+
dest='no_verify',
87+
action='store_true',
88+
default=False,
89+
help="Don't verify if built wheel is valid.",
90+
)
91+
8492
self.cmd_opts.add_option(
8593
'--global-option',
8694
dest='global_options',
@@ -166,6 +174,7 @@ def run(self, options, args):
166174
build_successes, build_failures = build(
167175
reqs_to_build,
168176
wheel_cache=wheel_cache,
177+
verify=(not options.no_verify),
169178
build_options=options.build_options or [],
170179
global_options=options.global_options or [],
171180
)

Diff for: src/pip/_internal/exceptions.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,6 @@ class CommandError(PipError):
9191
"""Raised when there is an error in command-line arguments"""
9292

9393

94-
class SubProcessError(PipError):
95-
"""Raised when there is an error raised while executing a
96-
command in subprocess"""
97-
98-
9994
class PreviousBuildDirError(PipError):
10095
"""Raised when there's a previous conflicting build directory"""
10196

@@ -151,6 +146,21 @@ def __str__(self):
151146
)
152147

153148

149+
class InstallationSubprocessError(InstallationError):
150+
"""A subprocess call failed during installation."""
151+
def __init__(self, returncode, description):
152+
# type: (int, str) -> None
153+
self.returncode = returncode
154+
self.description = description
155+
156+
def __str__(self):
157+
# type: () -> str
158+
return (
159+
"Command errored out with exit status {}: {} "
160+
"Check the logs for full command output."
161+
).format(self.returncode, self.description)
162+
163+
154164
class HashErrors(InstallationError):
155165
"""Multiple HashError instances rolled into one for reporting"""
156166

Diff for: src/pip/_internal/resolution/resolvelib/candidates.py

+6-17
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def __init__(
141141
self._ireq = ireq
142142
self._name = name
143143
self._version = version
144-
self._dist = None # type: Optional[Distribution]
144+
self.dist = self._prepare()
145145

146146
def __str__(self):
147147
# type: () -> str
@@ -209,8 +209,6 @@ def _prepare_distribution(self):
209209
def _check_metadata_consistency(self, dist):
210210
# type: (Distribution) -> None
211211
"""Check for consistency of project name and version of dist."""
212-
# TODO: (Longer term) Rather than abort, reject this candidate
213-
# and backtrack. This would need resolvelib support.
214212
name = canonicalize_name(dist.project_name)
215213
if self._name is not None and self._name != name:
216214
raise MetadataInconsistent(self._ireq, "name", dist.project_name)
@@ -219,25 +217,17 @@ def _check_metadata_consistency(self, dist):
219217
raise MetadataInconsistent(self._ireq, "version", dist.version)
220218

221219
def _prepare(self):
222-
# type: () -> None
223-
if self._dist is not None:
224-
return
220+
# type: () -> Distribution
225221
try:
226222
dist = self._prepare_distribution()
227223
except HashError as e:
224+
# Provide HashError the underlying ireq that caused it. This
225+
# provides context for the resulting error message to show the
226+
# offending line to the user.
228227
e.req = self._ireq
229228
raise
230-
231-
assert dist is not None, "Distribution already installed"
232229
self._check_metadata_consistency(dist)
233-
self._dist = dist
234-
235-
@property
236-
def dist(self):
237-
# type: () -> Distribution
238-
if self._dist is None:
239-
self._prepare()
240-
return self._dist
230+
return dist
241231

242232
def _get_requires_python_dependency(self):
243233
# type: () -> Optional[Requirement]
@@ -261,7 +251,6 @@ def iter_dependencies(self, with_requires):
261251

262252
def get_install_requirement(self):
263253
# type: () -> Optional[InstallRequirement]
264-
self._prepare()
265254
return self._ireq
266255

267256

Diff for: src/pip/_internal/resolution/resolvelib/factory.py

+48-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from pip._internal.exceptions import (
66
DistributionNotFound,
77
InstallationError,
8+
InstallationSubprocessError,
9+
MetadataInconsistent,
810
UnsupportedPythonVersion,
911
UnsupportedWheel,
1012
)
@@ -33,6 +35,7 @@
3335
ExplicitRequirement,
3436
RequiresPythonRequirement,
3537
SpecifierRequirement,
38+
UnsatisfiableRequirement,
3639
)
3740

3841
if MYPY_CHECK_RUNNING:
@@ -94,6 +97,7 @@ def __init__(
9497
self._force_reinstall = force_reinstall
9598
self._ignore_requires_python = ignore_requires_python
9699

100+
self._build_failures = {} # type: Cache[InstallationError]
97101
self._link_candidate_cache = {} # type: Cache[LinkCandidate]
98102
self._editable_candidate_cache = {} # type: Cache[EditableCandidate]
99103
self._installed_candidate_cache = {
@@ -136,21 +140,40 @@ def _make_candidate_from_link(
136140
name, # type: Optional[str]
137141
version, # type: Optional[_BaseVersion]
138142
):
139-
# type: (...) -> Candidate
143+
# type: (...) -> Optional[Candidate]
140144
# TODO: Check already installed candidate, and use it if the link and
141145
# 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+
142152
if template.editable:
143153
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
147163
base = self._editable_candidate_cache[link] # type: BaseCandidate
148164
else:
149165
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
153175
base = self._link_candidate_cache[link]
176+
154177
if extras:
155178
return ExtrasCandidate(base, extras)
156179
return base
@@ -207,16 +230,23 @@ def iter_index_candidates():
207230
all_yanked = all(ican.link.is_yanked for ican in icans)
208231

209232
# PackageFinder returns earlier versions first, so we reverse.
233+
versions_found = set() # type: Set[_BaseVersion]
210234
for ican in reversed(icans):
211235
if not all_yanked and ican.link.is_yanked:
212236
continue
213-
yield self._make_candidate_from_link(
237+
if ican.version in versions_found:
238+
continue
239+
candidate = self._make_candidate_from_link(
214240
link=ican.link,
215241
extras=extras,
216242
template=template,
217243
name=name,
218244
version=ican.version,
219245
)
246+
if candidate is None:
247+
continue
248+
yield candidate
249+
versions_found.add(ican.version)
220250

221251
return FoundCandidates(
222252
iter_index_candidates,
@@ -280,6 +310,16 @@ def make_requirement_from_install_req(self, ireq, requested_extras):
280310
name=canonicalize_name(ireq.name) if ireq.name else None,
281311
version=None,
282312
)
313+
if cand is None:
314+
# There's no way we can satisfy a URL requirement if the underlying
315+
# candidate fails to build. An unnamed URL must be user-supplied, so
316+
# we fail eagerly. If the URL is named, an unsatisfiable requirement
317+
# can make the resolver do the right thing, either backtrack (and
318+
# maybe find some other requirement that's buildable) or raise a
319+
# ResolutionImpossible eventually.
320+
if not ireq.name:
321+
raise self._build_failures[ireq.link]
322+
return UnsatisfiableRequirement(canonicalize_name(ireq.name))
283323
return self.make_requirement_from_candidate(cand)
284324

285325
def make_requirement_from_candidate(self, candidate):

0 commit comments

Comments
 (0)