summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDonald Stufft <donald@stufft.io>2014-02-19 20:55:09 -0500
committerDonald Stufft <donald@stufft.io>2014-02-19 20:55:09 -0500
commit6ae7e8fec60a5fe6f55a14be0beb94b7bb8629e2 (patch)
tree2ab2b3189f1903a5eae55371de60e989106c904b
parent3333f5def623134a3ddfca52b3f181d580822d6c (diff)
parentb26d91c880db8ee7222a82e7cfb5964a06815560 (diff)
downloadpip-6ae7e8fec60a5fe6f55a14be0beb94b7bb8629e2.tar.gz
Merge pull request #1581 from dstufft/test-wheel-version-1.5.x
Test wheel version 1.5.x
-rw-r--r--pip/req.py3
-rw-r--r--pip/wheel.py53
-rw-r--r--tests/data/packages/README.txt5
-rw-r--r--tests/data/packages/brokenwheel-1.0-py2.py3-none-any.whlbin0 -> 1345 bytes
-rw-r--r--tests/data/packages/futurewheel-1.9-py2.py3-none-any.whlbin0 -> 1778 bytes
-rw-r--r--tests/data/packages/futurewheel-3.0-py2.py3-none-any.whlbin0 -> 1770 bytes
-rw-r--r--tests/functional/test_install_wheel.py28
-rw-r--r--tests/unit/test_wheel.py43
8 files changed, 130 insertions, 2 deletions
diff --git a/pip/req.py b/pip/req.py
index 54933db5d..b0ea10aa3 100644
--- a/pip/req.py
+++ b/pip/req.py
@@ -661,6 +661,9 @@ exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n',
self.install_editable(install_options, global_options)
return
if self.is_wheel:
+ version = pip.wheel.wheel_version(self.source_dir)
+ pip.wheel.check_compatibility(version, self.name)
+
self.move_wheel_files(self.source_dir, root=root)
self.install_succeeded = True
return
diff --git a/pip/wheel.py b/pip/wheel.py
index f8128158a..839259df4 100644
--- a/pip/wheel.py
+++ b/pip/wheel.py
@@ -13,18 +13,23 @@ import shutil
import sys
from base64 import urlsafe_b64encode
+from email.parser import Parser
from pip.backwardcompat import ConfigParser, StringIO
-from pip.exceptions import InvalidWheelFilename
+from pip.exceptions import InvalidWheelFilename, UnsupportedWheel
from pip.locations import distutils_scheme
from pip.log import logger
from pip import pep425tags
from pip.util import call_subprocess, normalize_path, make_path_relative
from pip._vendor import pkg_resources
from pip._vendor.distlib.scripts import ScriptMaker
+from pip._vendor import pkg_resources
+
wheel_ext = '.whl'
+VERSION_COMPATIBLE = (1, 0)
+
def rehash(path, algo='sha256', blocksize=1<<20):
"""Return (hash, length) for path using hashlib.new(algo)"""
@@ -388,6 +393,52 @@ def uninstallation_paths(dist):
yield path
+def wheel_version(source_dir):
+ """
+ Return the Wheel-Version of an extracted wheel, if possible.
+
+ Otherwise, return False if we couldn't parse / extract it.
+ """
+ try:
+ dist = [d for d in pkg_resources.find_on_path(None, source_dir)][0]
+
+ wheel_data = dist.get_metadata('WHEEL')
+ wheel_data = Parser().parsestr(wheel_data)
+
+ version = wheel_data['Wheel-Version'].strip()
+ version = tuple(map(int, version.split('.')))
+ return version
+ except:
+ return False
+
+
+def check_compatibility(version, name):
+ """
+ Raises errors or warns if called with an incompatible Wheel-Version.
+
+ Pip should refuse to install a Wheel-Version that's a major series
+ ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when
+ installing a version only minor version ahead (e.g 1.2 > 1.1).
+
+ version: a 2-tuple representing a Wheel-Version (Major, Minor)
+ name: name of wheel or package to raise exception about
+
+ :raises UnsupportedWheel: when an incompatible Wheel-Version is given
+ """
+ if not version:
+ raise UnsupportedWheel(
+ "%s is in an unsupported or invalid wheel" % name
+ )
+ if version[0] > VERSION_COMPATIBLE[0]:
+ raise UnsupportedWheel(
+ "%s's Wheel-Version (%s) is not compatible with this version "
+ "of pip" % (name, '.'.join(map(str, version)))
+ )
+ elif version > VERSION_COMPATIBLE:
+ logger.warn('Installing from a newer Wheel-Version (%s)'
+ % '.'.join(map(str, version)))
+
+
class Wheel(object):
"""A wheel file"""
diff --git a/tests/data/packages/README.txt b/tests/data/packages/README.txt
index 9393466ff..3fd0c69bd 100644
--- a/tests/data/packages/README.txt
+++ b/tests/data/packages/README.txt
@@ -29,6 +29,11 @@ FSPkg
-----
for installing from the file system
+futurewheel
+-----------
+Wheels of a Wheel-Version that is newer in minor and major series.
+Their version coincides with the apparent Wheel-Version they indicate.
+
gmpy-1.15.tar.gz
----------------
hash testing (although this pkg isn't needed explicitly)
diff --git a/tests/data/packages/brokenwheel-1.0-py2.py3-none-any.whl b/tests/data/packages/brokenwheel-1.0-py2.py3-none-any.whl
new file mode 100644
index 000000000..3ae77cf03
--- /dev/null
+++ b/tests/data/packages/brokenwheel-1.0-py2.py3-none-any.whl
Binary files differ
diff --git a/tests/data/packages/futurewheel-1.9-py2.py3-none-any.whl b/tests/data/packages/futurewheel-1.9-py2.py3-none-any.whl
new file mode 100644
index 000000000..703243cbb
--- /dev/null
+++ b/tests/data/packages/futurewheel-1.9-py2.py3-none-any.whl
Binary files differ
diff --git a/tests/data/packages/futurewheel-3.0-py2.py3-none-any.whl b/tests/data/packages/futurewheel-3.0-py2.py3-none-any.whl
new file mode 100644
index 000000000..372b1be79
--- /dev/null
+++ b/tests/data/packages/futurewheel-3.0-py2.py3-none-any.whl
Binary files differ
diff --git a/tests/functional/test_install_wheel.py b/tests/functional/test_install_wheel.py
index c3c00f60f..1a602c644 100644
--- a/tests/functional/test_install_wheel.py
+++ b/tests/functional/test_install_wheel.py
@@ -3,6 +3,34 @@ import pytest
import glob
from tests.lib.path import Path
+from tests.lib import TestFailure
+
+
+def test_install_from_future_wheel_version(script, data):
+ """
+ Test installing a future wheel
+ """
+ package = data.packages.join("futurewheel-3.0-py2.py3-none-any.whl")
+ result = script.pip('install', package, '--no-index', expect_error=True)
+ with pytest.raises(TestFailure):
+ result.assert_installed('futurewheel', without_egg_link=True,
+ editable=False)
+
+ package = data.packages.join("futurewheel-1.9-py2.py3-none-any.whl")
+ result = script.pip('install', package, '--no-index', expect_error=False)
+ result.assert_installed('futurewheel', without_egg_link=True,
+ editable=False)
+
+
+def test_install_from_broken_wheel(script, data):
+ """
+ Test that installing a broken wheel fails properly
+ """
+ package = data.packages.join("brokenwheel-1.0-py2.py3-none-any.whl")
+ result = script.pip('install', package, '--no-index', expect_error=True)
+ with pytest.raises(TestFailure):
+ result.assert_installed('futurewheel', without_egg_link=True,
+ editable=False)
def test_install_from_wheel(script, data):
diff --git a/tests/unit/test_wheel.py b/tests/unit/test_wheel.py
index 5eb21dc7b..9b1061b9f 100644
--- a/tests/unit/test_wheel.py
+++ b/tests/unit/test_wheel.py
@@ -6,8 +6,11 @@ import pytest
import pkg_resources
from mock import patch, Mock
from pip import wheel
-from pip.exceptions import InstallationError, InvalidWheelFilename
+from pip.exceptions import (
+ InstallationError, InvalidWheelFilename, UnsupportedWheel,
+)
from pip.index import PackageFinder
+from pip.util import unpack_file
from tests.lib import assert_raises_regexp
@@ -51,6 +54,44 @@ def test_uninstallation_paths():
assert paths2 == paths
+def test_wheel_version(tmpdir, data):
+ future_wheel = 'futurewheel-1.9-py2.py3-none-any.whl'
+ broken_wheel = 'brokenwheel-1.0-py2.py3-none-any.whl'
+ future_version = (1, 9)
+
+ unpack_file(data.packages.join(future_wheel),
+ tmpdir + 'future', None, None)
+ unpack_file(data.packages.join(broken_wheel),
+ tmpdir + 'broken', None, None)
+
+ assert wheel.wheel_version(tmpdir + 'future') == future_version
+ assert not wheel.wheel_version(tmpdir + 'broken')
+
+
+def test_check_compatibility():
+ name = 'test'
+ vc = wheel.VERSION_COMPATIBLE
+
+ # Major version is higher - should be incompatible
+ higher_v = (vc[0] + 1, vc[1])
+
+ # test raises with correct error
+ with pytest.raises(UnsupportedWheel) as e:
+ wheel.check_compatibility(higher_v, name)
+ assert 'is not compatible' in str(e)
+
+ # Should only log.warn - minor version is greator
+ higher_v = (vc[0], vc[1] + 1)
+ wheel.check_compatibility(higher_v, name)
+
+ # These should work fine
+ wheel.check_compatibility(wheel.VERSION_COMPATIBLE, name)
+
+ # E.g if wheel to install is 1.0 and we support up to 1.2
+ lower_v = (vc[0], max(0, vc[1] - 1))
+ wheel.check_compatibility(lower_v, name)
+
+
class TestWheelFile(object):
def test_std_wheel_pattern(self):