Give markers chance to remove incompat wheel links

`pip install -r reqs.txt` was failing when the requirements file includes
an unsupported wheel, regardless of whether it is conditionally removed
by a marker. This patch fixes that issue.

Additionally, this patch makes pip check local file wheels for
compatibility. Previously, a requirements file could include a path
to a valid wheel for any platform and pip would happily install it.
This commit is contained in:
burrows 2016-03-09 21:55:20 -05:00
parent e69cc372b8
commit 3c11bb4de9
5 changed files with 91 additions and 13 deletions

View File

@ -2,6 +2,9 @@
* Fix a regression on systems with uninitialized locale (:issue:`3575`).
* Use environment markers to filter packages before determining if a
required wheel is supported. Solves (:issue:`3254`).
**8.1.1 (2016-03-17)**

View File

@ -27,7 +27,7 @@ import pip.wheel
from pip.compat import native_str, get_stdlib, WINDOWS
from pip.download import is_url, url_to_path, path_to_url, is_archive_file
from pip.exceptions import (
InstallationError, UninstallationError, UnsupportedWheel,
InstallationError, UninstallationError,
)
from pip.locations import (
bin_py, running_under_virtualenv, PIP_DELETE_MARKER_FILENAME, bin_user,
@ -210,11 +210,6 @@ class InstallRequirement(object):
# wheel file
if link.is_wheel:
wheel = Wheel(link.filename) # can raise InvalidWheelFilename
if not wheel.supported():
raise UnsupportedWheel(
"%s is not a supported wheel on this platform." %
wheel.filename
)
req = "%s==%s" % (wheel.name, wheel.version)
else:
# set the req to the egg fragment. when it's not there, this

View File

@ -21,7 +21,7 @@ from pip.utils import (
from pip.utils.hashes import MissingHashes
from pip.utils.logging import indent_log
from pip.vcs import vcs
from pip.wheel import Wheel
logger = logging.getLogger(__name__)
@ -227,6 +227,16 @@ class RequirementSet(object):
install_req.markers)
return []
# This check has to come after we filter requirements with the
# environment markers.
if install_req.link and install_req.link.is_wheel:
wheel = Wheel(install_req.link.filename)
if not wheel.supported():
raise InstallationError(
"%s is not a supported wheel on this platform." %
wheel.filename
)
install_req.as_egg = self.as_egg
install_req.use_user_site = self.use_user_site
install_req.target_dir = self.target_dir

View File

@ -457,3 +457,41 @@ def test_install_distribution_union_conflicting_extras(script, data):
expect_error=True)
assert 'installed' not in result.stdout
assert "Conflict" in result.stderr
def test_install_unsupported_wheel_link_with_marker(script, data):
script.scratch_path.join("with-marker.txt").write(
textwrap.dedent("""\
%s; %s
""") %
(
'https://github.com/a/b/c/asdf-1.5.2-cp27-none-xyz.whl',
'sys_platform == "xyz"'
)
)
result = script.pip(
'install', '-r', script.scratch_path / 'with-marker.txt',
expect_error=False,
expect_stderr=True,
)
s = "Ignoring asdf: markers %r don't match your environment" %\
u'sys_platform == "xyz"'
assert s in result.stderr
assert len(result.files_created) == 0
def test_install_unsupported_wheel_file(script, data):
# Trying to install a local wheel with an incompatible version/type
# should fail.
script.scratch_path.join("wheel-file.txt").write(textwrap.dedent("""\
%s
""" % data.packages.join("simple.dist-0.1-py1-none-invalid.whl")))
result = script.pip(
'install', '-r', script.scratch_path / 'wheel-file.txt',
expect_error=True,
expect_stderr=True,
)
assert ("simple.dist-0.1-py1-none-invalid.whl is not a supported " +
"wheel on this platform" in result.stderr)
assert len(result.files_created) == 0

View File

@ -8,7 +8,7 @@ import pytest
from mock import Mock, patch, mock_open
from pip.commands.install import InstallCommand
from pip.exceptions import (PreviousBuildDirError, InvalidWheelFilename,
InstallationError, UnsupportedWheel, HashErrors)
InstallationError, HashErrors)
from pip.download import path_to_url, PipSession
from pip.index import PackageFinder
from pip.req import (InstallRequirement, RequirementSet, Requirements)
@ -301,6 +301,20 @@ def test_egg_info_data(file_contents, expected):
class TestInstallRequirement(object):
def setup(self):
self.tempdir = tempfile.mkdtemp()
def teardown(self):
shutil.rmtree(self.tempdir, ignore_errors=True)
def basic_reqset(self, **kwargs):
return RequirementSet(
build_dir=os.path.join(self.tempdir, 'build'),
src_dir=os.path.join(self.tempdir, 'src'),
download_dir=None,
session=PipSession(),
**kwargs
)
def test_url_with_query(self):
"""InstallRequirement should strip the fragment, but not the query."""
@ -309,11 +323,29 @@ class TestInstallRequirement(object):
req = InstallRequirement.from_line(url + fragment)
assert req.link.url == url + fragment, req.link
def test_unsupported_wheel_requirement_raises(self):
with pytest.raises(UnsupportedWheel):
InstallRequirement.from_line(
'peppercorn-0.4-py2.py3-bogus-any.whl',
)
def test_unsupported_wheel_link_requirement_raises(self):
reqset = self.basic_reqset()
req = InstallRequirement.from_line(
'https://whatever.com/peppercorn-0.4-py2.py3-bogus-any.whl',
)
assert req.link is not None
assert req.link.is_wheel
assert req.link.scheme == "https"
with pytest.raises(InstallationError):
reqset.add_requirement(req)
def test_unsupported_wheel_local_file_requirement_raises(self, data):
reqset = self.basic_reqset()
req = InstallRequirement.from_line(
data.packages.join('simple.dist-0.1-py1-none-invalid.whl'),
)
assert req.link is not None
assert req.link.is_wheel
assert req.link.scheme == "file"
with pytest.raises(InstallationError):
reqset.add_requirement(req)
def test_installed_version_not_installed(self):
req = InstallRequirement.from_line('simple-0.1-py2.py3-none-any.whl')