diff options
author | Donald Stufft <donald@stufft.io> | 2014-02-19 20:55:09 -0500 |
---|---|---|
committer | Donald Stufft <donald@stufft.io> | 2014-02-19 20:55:09 -0500 |
commit | 6ae7e8fec60a5fe6f55a14be0beb94b7bb8629e2 (patch) | |
tree | 2ab2b3189f1903a5eae55371de60e989106c904b | |
parent | 3333f5def623134a3ddfca52b3f181d580822d6c (diff) | |
parent | b26d91c880db8ee7222a82e7cfb5964a06815560 (diff) | |
download | pip-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.py | 3 | ||||
-rw-r--r-- | pip/wheel.py | 53 | ||||
-rw-r--r-- | tests/data/packages/README.txt | 5 | ||||
-rw-r--r-- | tests/data/packages/brokenwheel-1.0-py2.py3-none-any.whl | bin | 0 -> 1345 bytes | |||
-rw-r--r-- | tests/data/packages/futurewheel-1.9-py2.py3-none-any.whl | bin | 0 -> 1778 bytes | |||
-rw-r--r-- | tests/data/packages/futurewheel-3.0-py2.py3-none-any.whl | bin | 0 -> 1770 bytes | |||
-rw-r--r-- | tests/functional/test_install_wheel.py | 28 | ||||
-rw-r--r-- | tests/unit/test_wheel.py | 43 |
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 Binary files differnew file mode 100644 index 000000000..3ae77cf03 --- /dev/null +++ b/tests/data/packages/brokenwheel-1.0-py2.py3-none-any.whl 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 Binary files differnew file mode 100644 index 000000000..703243cbb --- /dev/null +++ b/tests/data/packages/futurewheel-1.9-py2.py3-none-any.whl 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 Binary files differnew file mode 100644 index 000000000..372b1be79 --- /dev/null +++ b/tests/data/packages/futurewheel-3.0-py2.py3-none-any.whl 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): |