Skip to content

Manylinux2010 #5410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -157,6 +157,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 @@ -322,54 +322,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 @@ -539,7 +539,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