mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Ensure wheel-Version is compatible with pip
This commit is contained in:
parent
3333f5def6
commit
b26d91c880
8 changed files with 130 additions and 2 deletions
|
@ -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
|
||||
|
|
53
pip/wheel.py
53
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"""
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
BIN
tests/data/packages/brokenwheel-1.0-py2.py3-none-any.whl
Normal file
BIN
tests/data/packages/brokenwheel-1.0-py2.py3-none-any.whl
Normal file
Binary file not shown.
BIN
tests/data/packages/futurewheel-1.9-py2.py3-none-any.whl
Normal file
BIN
tests/data/packages/futurewheel-1.9-py2.py3-none-any.whl
Normal file
Binary file not shown.
BIN
tests/data/packages/futurewheel-3.0-py2.py3-none-any.whl
Normal file
BIN
tests/data/packages/futurewheel-3.0-py2.py3-none-any.whl
Normal file
Binary file not shown.
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue