Skip to content

Commit

Permalink
Merge pull request #5410 from wtolson/manylinux2010
Browse files Browse the repository at this point in the history
Manylinux2010
  • Loading branch information
pfmoore authored Nov 22, 2018
2 parents 212b964 + 9303895 commit 087d825
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 62 deletions.
3 changes: 3 additions & 0 deletions news/5008.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Implement manylinux2010 platform tag support. manylinux2010 is the successor
to manylinux1. It allows carefully compiled binary wheels to be installed
on compatible Linux platforms.
33 changes: 31 additions & 2 deletions src/pip/_internal/pep425tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,23 @@ def is_manylinux1_compatible():
return pip._internal.utils.glibc.have_compatible_glibc(2, 5)


def is_manylinux2010_compatible():
# Only Linux, and only x86-64 / i686
if get_platform() not in {"linux_x86_64", "linux_i686"}:
return False

# Check for presence of _manylinux module
try:
import _manylinux
return bool(_manylinux.manylinux2010_compatible)
except (ImportError, AttributeError):
# Fall through to heuristic check below
pass

# Check glibc version. CentOS 6 uses glibc 2.12.
return pip._internal.utils.glibc.have_compatible_glibc(2, 12)


def get_darwin_arches(major, minor, machine):
"""Return a list of supported arches (including group arches) for
the given major, minor and machine architecture of an macOS machine.
Expand Down Expand Up @@ -263,6 +280,7 @@ def get_supported(versions=None, noarch=False, platform=None,

if not noarch:
arch = platform or get_platform()
arch_prefix, arch_sep, arch_suffix = arch.partition('_')
if arch.startswith('macosx'):
# support macosx-10.6-intel on macosx-10.9-x86_64
match = _osx_arch_pat.match(arch)
Expand All @@ -276,8 +294,19 @@ def get_supported(versions=None, noarch=False, platform=None,
else:
# arch pattern didn't match (?!)
arches = [arch]
elif platform is None and is_manylinux1_compatible():
arches = [arch.replace('linux', 'manylinux1'), arch]
elif arch_prefix == 'manylinux2010':
# manylinux1 wheels run on most manylinux2010 systems with the
# exception of wheels depending on ncurses. PEP 571 states
# manylinux1 wheels should be considered manylinux2010 wheels:
# https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels
arches = [arch, 'manylinux1' + arch_sep + arch_suffix]
elif platform is None:
arches = []
if is_manylinux2010_compatible():
arches.append('manylinux2010' + arch_sep + arch_suffix)
if is_manylinux1_compatible():
arches.append('manylinux1' + arch_sep + arch_suffix)
arches.append(arch)
else:
arches = [arch]

Expand Down
113 changes: 65 additions & 48 deletions tests/functional/test_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,54 +321,71 @@ def test_download_specify_platform(script, data):
)


def test_download_platform_manylinux(script, data):
"""
Test using "pip download --platform" to download a .whl archive
supported for a specific platform.
"""
fake_wheel(data, 'fake-1.0-py2.py3-none-any.whl')
# Confirm that universal wheels are returned even for specific
# platforms.
result = script.pip(
'download', '--no-index', '--find-links', data.find_links,
'--only-binary=:all:',
'--dest', '.',
'--platform', 'linux_x86_64',
'fake',
)
assert (
Path('scratch') / 'fake-1.0-py2.py3-none-any.whl'
in result.files_created
)

data.reset()
fake_wheel(data, 'fake-1.0-py2.py3-none-manylinux1_x86_64.whl')
result = script.pip(
'download', '--no-index', '--find-links', data.find_links,
'--only-binary=:all:',
'--dest', '.',
'--platform', 'manylinux1_x86_64',
'fake',
)
assert (
Path('scratch') /
'fake-1.0-py2.py3-none-manylinux1_x86_64.whl'
in result.files_created
)

# When specifying the platform, manylinux1 needs to be the
# explicit platform--it won't ever be added to the compatible
# tags.
data.reset()
fake_wheel(data, 'fake-1.0-py2.py3-none-linux_x86_64.whl')
result = script.pip(
'download', '--no-index', '--find-links', data.find_links,
'--only-binary=:all:',
'--dest', '.',
'--platform', 'linux_x86_64',
'fake',
expect_error=True,
)
class TestDownloadPlatformManylinuxes(object):
"""
"pip download --platform" downloads a .whl archive supported for
manylinux platforms.
"""

@pytest.mark.parametrize("platform", [
"linux_x86_64",
"manylinux1_x86_64",
"manylinux2010_x86_64",
])
def test_download_universal(self, platform, script, data):
"""
Universal wheels are returned even for specific platforms.
"""
fake_wheel(data, 'fake-1.0-py2.py3-none-any.whl')
result = script.pip(
'download', '--no-index', '--find-links', data.find_links,
'--only-binary=:all:',
'--dest', '.',
'--platform', platform,
'fake',
)
assert (
Path('scratch') / 'fake-1.0-py2.py3-none-any.whl'
in result.files_created
)

@pytest.mark.parametrize("wheel_abi,platform", [
("manylinux1_x86_64", "manylinux1_x86_64"),
("manylinux1_x86_64", "manylinux2010_x86_64"),
("manylinux2010_x86_64", "manylinux2010_x86_64"),
])
def test_download_compatible_manylinuxes(
self, wheel_abi, platform, script, data,
):
"""
Earlier manylinuxes are compatible with later manylinuxes.
"""
wheel = 'fake-1.0-py2.py3-none-{}.whl'.format(wheel_abi)
fake_wheel(data, wheel)
result = script.pip(
'download', '--no-index', '--find-links', data.find_links,
'--only-binary=:all:',
'--dest', '.',
'--platform', platform,
'fake',
)
assert Path('scratch') / wheel in result.files_created

def test_explicit_platform_only(self, data, script):
"""
When specifying the platform, manylinux1 needs to be the
explicit platform--it won't ever be added to the compatible
tags.
"""
fake_wheel(data, 'fake-1.0-py2.py3-none-linux_x86_64.whl')
script.pip(
'download', '--no-index', '--find-links', data.find_links,
'--only-binary=:all:',
'--dest', '.',
'--platform', 'linux_x86_64',
'fake',
expect_error=True,
)


def test_download_specify_python_version(script, data):
Expand Down
85 changes: 74 additions & 11 deletions tests/unit/test_pep425tags.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import sys

import pytest

from mock import patch

from pip._internal import pep425tags
Expand Down Expand Up @@ -114,44 +116,58 @@ def test_manual_abi_dm_flags(self):
self.abi_tag_unicode('dm', {'Py_DEBUG': True, 'WITH_PYMALLOC': True})


class TestManylinux1Tags(object):

@pytest.mark.parametrize('is_manylinux_compatible', [
pep425tags.is_manylinux1_compatible,
pep425tags.is_manylinux2010_compatible,
])
class TestManylinuxTags(object):
"""
Tests common to all manylinux tags (e.g. manylinux1, manylinux2010,
...)
"""
@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_x86_64')
@patch('pip._internal.utils.glibc.have_compatible_glibc',
lambda major, minor: True)
def test_manylinux1_compatible_on_linux_x86_64(self):
def test_manylinux_compatible_on_linux_x86_64(self,
is_manylinux_compatible):
"""
Test that manylinux1 is enabled on linux_x86_64
Test that manylinuxes are enabled on linux_x86_64
"""
assert pep425tags.is_manylinux1_compatible()
assert is_manylinux_compatible()

@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_i686')
@patch('pip._internal.utils.glibc.have_compatible_glibc',
lambda major, minor: True)
def test_manylinux1_compatible_on_linux_i686(self):
def test_manylinux1_compatible_on_linux_i686(self,
is_manylinux_compatible):
"""
Test that manylinux1 is enabled on linux_i686
"""
assert pep425tags.is_manylinux1_compatible()
assert is_manylinux_compatible()

@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_x86_64')
@patch('pip._internal.utils.glibc.have_compatible_glibc',
lambda major, minor: False)
def test_manylinux1_2(self):
def test_manylinux1_2(self, is_manylinux_compatible):
"""
Test that manylinux1 is disabled with incompatible glibc
"""
assert not pep425tags.is_manylinux1_compatible()
assert not is_manylinux_compatible()

@patch('pip._internal.pep425tags.get_platform', lambda: 'arm6vl')
@patch('pip._internal.utils.glibc.have_compatible_glibc',
lambda major, minor: True)
def test_manylinux1_3(self):
def test_manylinux1_3(self, is_manylinux_compatible):
"""
Test that manylinux1 is disabled on arm6vl
"""
assert not pep425tags.is_manylinux1_compatible()
assert not is_manylinux_compatible()


class TestManylinux1Tags(object):

@patch('pip._internal.pep425tags.is_manylinux2010_compatible',
lambda: False)
@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_x86_64')
@patch('pip._internal.utils.glibc.have_compatible_glibc',
lambda major, minor: True)
Expand All @@ -172,3 +188,50 @@ def test_manylinux1_tag_is_first(self):
assert arches == ['manylinux1_x86_64', 'linux_x86_64', 'any']
else:
assert arches == ['manylinux1_x86_64', 'linux_x86_64']


class TestManylinux2010Tags(object):

@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_x86_64')
@patch('pip._internal.utils.glibc.have_compatible_glibc',
lambda major, minor: True)
@patch('sys.platform', 'linux2')
def test_manylinux2010_tag_is_first(self):
"""
Test that the more specific tag manylinux2010 comes first.
"""
groups = {}
for pyimpl, abi, arch in pep425tags.get_supported():
groups.setdefault((pyimpl, abi), []).append(arch)

for arches in groups.values():
if arches == ['any']:
continue
# Expect the most specific arch first:
if len(arches) == 4:
assert arches == ['manylinux2010_x86_64',
'manylinux1_x86_64',
'linux_x86_64',
'any']
else:
assert arches == ['manylinux2010_x86_64',
'manylinux1_x86_64',
'linux_x86_64']

@pytest.mark.parametrize("manylinux2010,manylinux1", [
("manylinux2010_x86_64", "manylinux1_x86_64"),
("manylinux2010_i686", "manylinux1_i686"),
])
def test_manylinux2010_implies_manylinux1(self, manylinux2010, manylinux1):
"""
Specifying manylinux2010 implies manylinux1.
"""
groups = {}
supported = pep425tags.get_supported(platform=manylinux2010)
for pyimpl, abi, arch in supported:
groups.setdefault((pyimpl, abi), []).append(arch)

for arches in groups.values():
if arches == ['any']:
continue
assert arches[:2] == [manylinux2010, manylinux1]
2 changes: 1 addition & 1 deletion tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ def test_create_and_cleanup_work(self):


class TestGlibc(object):
def test_manylinux1_check_glibc_version(self):
def test_manylinux_check_glibc_version(self):
"""
Test that the check_glibc_version function is robust against weird
glibc version strings.
Expand Down

0 comments on commit 087d825

Please sign in to comment.