Skip to content

Commit 087d825

Browse files
authored
Merge pull request #5410 from wtolson/manylinux2010
Manylinux2010
2 parents 212b964 + 9303895 commit 087d825

File tree

5 files changed

+174
-62
lines changed

5 files changed

+174
-62
lines changed

news/5008.feature

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Implement manylinux2010 platform tag support. manylinux2010 is the successor
2+
to manylinux1. It allows carefully compiled binary wheels to be installed
3+
on compatible Linux platforms.

src/pip/_internal/pep425tags.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,23 @@ def is_manylinux1_compatible():
158158
return pip._internal.utils.glibc.have_compatible_glibc(2, 5)
159159

160160

161+
def is_manylinux2010_compatible():
162+
# Only Linux, and only x86-64 / i686
163+
if get_platform() not in {"linux_x86_64", "linux_i686"}:
164+
return False
165+
166+
# Check for presence of _manylinux module
167+
try:
168+
import _manylinux
169+
return bool(_manylinux.manylinux2010_compatible)
170+
except (ImportError, AttributeError):
171+
# Fall through to heuristic check below
172+
pass
173+
174+
# Check glibc version. CentOS 6 uses glibc 2.12.
175+
return pip._internal.utils.glibc.have_compatible_glibc(2, 12)
176+
177+
161178
def get_darwin_arches(major, minor, machine):
162179
"""Return a list of supported arches (including group arches) for
163180
the given major, minor and machine architecture of an macOS machine.
@@ -263,6 +280,7 @@ def get_supported(versions=None, noarch=False, platform=None,
263280

264281
if not noarch:
265282
arch = platform or get_platform()
283+
arch_prefix, arch_sep, arch_suffix = arch.partition('_')
266284
if arch.startswith('macosx'):
267285
# support macosx-10.6-intel on macosx-10.9-x86_64
268286
match = _osx_arch_pat.match(arch)
@@ -276,8 +294,19 @@ def get_supported(versions=None, noarch=False, platform=None,
276294
else:
277295
# arch pattern didn't match (?!)
278296
arches = [arch]
279-
elif platform is None and is_manylinux1_compatible():
280-
arches = [arch.replace('linux', 'manylinux1'), arch]
297+
elif arch_prefix == 'manylinux2010':
298+
# manylinux1 wheels run on most manylinux2010 systems with the
299+
# exception of wheels depending on ncurses. PEP 571 states
300+
# manylinux1 wheels should be considered manylinux2010 wheels:
301+
# https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels
302+
arches = [arch, 'manylinux1' + arch_sep + arch_suffix]
303+
elif platform is None:
304+
arches = []
305+
if is_manylinux2010_compatible():
306+
arches.append('manylinux2010' + arch_sep + arch_suffix)
307+
if is_manylinux1_compatible():
308+
arches.append('manylinux1' + arch_sep + arch_suffix)
309+
arches.append(arch)
281310
else:
282311
arches = [arch]
283312

tests/functional/test_download.py

Lines changed: 65 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -321,54 +321,71 @@ def test_download_specify_platform(script, data):
321321
)
322322

323323

324-
def test_download_platform_manylinux(script, data):
325-
"""
326-
Test using "pip download --platform" to download a .whl archive
327-
supported for a specific platform.
328-
"""
329-
fake_wheel(data, 'fake-1.0-py2.py3-none-any.whl')
330-
# Confirm that universal wheels are returned even for specific
331-
# platforms.
332-
result = script.pip(
333-
'download', '--no-index', '--find-links', data.find_links,
334-
'--only-binary=:all:',
335-
'--dest', '.',
336-
'--platform', 'linux_x86_64',
337-
'fake',
338-
)
339-
assert (
340-
Path('scratch') / 'fake-1.0-py2.py3-none-any.whl'
341-
in result.files_created
342-
)
343-
344-
data.reset()
345-
fake_wheel(data, 'fake-1.0-py2.py3-none-manylinux1_x86_64.whl')
346-
result = script.pip(
347-
'download', '--no-index', '--find-links', data.find_links,
348-
'--only-binary=:all:',
349-
'--dest', '.',
350-
'--platform', 'manylinux1_x86_64',
351-
'fake',
352-
)
353-
assert (
354-
Path('scratch') /
355-
'fake-1.0-py2.py3-none-manylinux1_x86_64.whl'
356-
in result.files_created
357-
)
358-
359-
# When specifying the platform, manylinux1 needs to be the
360-
# explicit platform--it won't ever be added to the compatible
361-
# tags.
362-
data.reset()
363-
fake_wheel(data, 'fake-1.0-py2.py3-none-linux_x86_64.whl')
364-
result = script.pip(
365-
'download', '--no-index', '--find-links', data.find_links,
366-
'--only-binary=:all:',
367-
'--dest', '.',
368-
'--platform', 'linux_x86_64',
369-
'fake',
370-
expect_error=True,
371-
)
324+
class TestDownloadPlatformManylinuxes(object):
325+
"""
326+
"pip download --platform" downloads a .whl archive supported for
327+
manylinux platforms.
328+
"""
329+
330+
@pytest.mark.parametrize("platform", [
331+
"linux_x86_64",
332+
"manylinux1_x86_64",
333+
"manylinux2010_x86_64",
334+
])
335+
def test_download_universal(self, platform, script, data):
336+
"""
337+
Universal wheels are returned even for specific platforms.
338+
"""
339+
fake_wheel(data, 'fake-1.0-py2.py3-none-any.whl')
340+
result = script.pip(
341+
'download', '--no-index', '--find-links', data.find_links,
342+
'--only-binary=:all:',
343+
'--dest', '.',
344+
'--platform', platform,
345+
'fake',
346+
)
347+
assert (
348+
Path('scratch') / 'fake-1.0-py2.py3-none-any.whl'
349+
in result.files_created
350+
)
351+
352+
@pytest.mark.parametrize("wheel_abi,platform", [
353+
("manylinux1_x86_64", "manylinux1_x86_64"),
354+
("manylinux1_x86_64", "manylinux2010_x86_64"),
355+
("manylinux2010_x86_64", "manylinux2010_x86_64"),
356+
])
357+
def test_download_compatible_manylinuxes(
358+
self, wheel_abi, platform, script, data,
359+
):
360+
"""
361+
Earlier manylinuxes are compatible with later manylinuxes.
362+
"""
363+
wheel = 'fake-1.0-py2.py3-none-{}.whl'.format(wheel_abi)
364+
fake_wheel(data, wheel)
365+
result = script.pip(
366+
'download', '--no-index', '--find-links', data.find_links,
367+
'--only-binary=:all:',
368+
'--dest', '.',
369+
'--platform', platform,
370+
'fake',
371+
)
372+
assert Path('scratch') / wheel in result.files_created
373+
374+
def test_explicit_platform_only(self, data, script):
375+
"""
376+
When specifying the platform, manylinux1 needs to be the
377+
explicit platform--it won't ever be added to the compatible
378+
tags.
379+
"""
380+
fake_wheel(data, 'fake-1.0-py2.py3-none-linux_x86_64.whl')
381+
script.pip(
382+
'download', '--no-index', '--find-links', data.find_links,
383+
'--only-binary=:all:',
384+
'--dest', '.',
385+
'--platform', 'linux_x86_64',
386+
'fake',
387+
expect_error=True,
388+
)
372389

373390

374391
def test_download_specify_python_version(script, data):

tests/unit/test_pep425tags.py

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import sys
22

3+
import pytest
4+
35
from mock import patch
46

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

116118

117-
class TestManylinux1Tags(object):
118-
119+
@pytest.mark.parametrize('is_manylinux_compatible', [
120+
pep425tags.is_manylinux1_compatible,
121+
pep425tags.is_manylinux2010_compatible,
122+
])
123+
class TestManylinuxTags(object):
124+
"""
125+
Tests common to all manylinux tags (e.g. manylinux1, manylinux2010,
126+
...)
127+
"""
119128
@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_x86_64')
120129
@patch('pip._internal.utils.glibc.have_compatible_glibc',
121130
lambda major, minor: True)
122-
def test_manylinux1_compatible_on_linux_x86_64(self):
131+
def test_manylinux_compatible_on_linux_x86_64(self,
132+
is_manylinux_compatible):
123133
"""
124-
Test that manylinux1 is enabled on linux_x86_64
134+
Test that manylinuxes are enabled on linux_x86_64
125135
"""
126-
assert pep425tags.is_manylinux1_compatible()
136+
assert is_manylinux_compatible()
127137

128138
@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_i686')
129139
@patch('pip._internal.utils.glibc.have_compatible_glibc',
130140
lambda major, minor: True)
131-
def test_manylinux1_compatible_on_linux_i686(self):
141+
def test_manylinux1_compatible_on_linux_i686(self,
142+
is_manylinux_compatible):
132143
"""
133144
Test that manylinux1 is enabled on linux_i686
134145
"""
135-
assert pep425tags.is_manylinux1_compatible()
146+
assert is_manylinux_compatible()
136147

137148
@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_x86_64')
138149
@patch('pip._internal.utils.glibc.have_compatible_glibc',
139150
lambda major, minor: False)
140-
def test_manylinux1_2(self):
151+
def test_manylinux1_2(self, is_manylinux_compatible):
141152
"""
142153
Test that manylinux1 is disabled with incompatible glibc
143154
"""
144-
assert not pep425tags.is_manylinux1_compatible()
155+
assert not is_manylinux_compatible()
145156

146157
@patch('pip._internal.pep425tags.get_platform', lambda: 'arm6vl')
147158
@patch('pip._internal.utils.glibc.have_compatible_glibc',
148159
lambda major, minor: True)
149-
def test_manylinux1_3(self):
160+
def test_manylinux1_3(self, is_manylinux_compatible):
150161
"""
151162
Test that manylinux1 is disabled on arm6vl
152163
"""
153-
assert not pep425tags.is_manylinux1_compatible()
164+
assert not is_manylinux_compatible()
154165

166+
167+
class TestManylinux1Tags(object):
168+
169+
@patch('pip._internal.pep425tags.is_manylinux2010_compatible',
170+
lambda: False)
155171
@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_x86_64')
156172
@patch('pip._internal.utils.glibc.have_compatible_glibc',
157173
lambda major, minor: True)
@@ -172,3 +188,50 @@ def test_manylinux1_tag_is_first(self):
172188
assert arches == ['manylinux1_x86_64', 'linux_x86_64', 'any']
173189
else:
174190
assert arches == ['manylinux1_x86_64', 'linux_x86_64']
191+
192+
193+
class TestManylinux2010Tags(object):
194+
195+
@patch('pip._internal.pep425tags.get_platform', lambda: 'linux_x86_64')
196+
@patch('pip._internal.utils.glibc.have_compatible_glibc',
197+
lambda major, minor: True)
198+
@patch('sys.platform', 'linux2')
199+
def test_manylinux2010_tag_is_first(self):
200+
"""
201+
Test that the more specific tag manylinux2010 comes first.
202+
"""
203+
groups = {}
204+
for pyimpl, abi, arch in pep425tags.get_supported():
205+
groups.setdefault((pyimpl, abi), []).append(arch)
206+
207+
for arches in groups.values():
208+
if arches == ['any']:
209+
continue
210+
# Expect the most specific arch first:
211+
if len(arches) == 4:
212+
assert arches == ['manylinux2010_x86_64',
213+
'manylinux1_x86_64',
214+
'linux_x86_64',
215+
'any']
216+
else:
217+
assert arches == ['manylinux2010_x86_64',
218+
'manylinux1_x86_64',
219+
'linux_x86_64']
220+
221+
@pytest.mark.parametrize("manylinux2010,manylinux1", [
222+
("manylinux2010_x86_64", "manylinux1_x86_64"),
223+
("manylinux2010_i686", "manylinux1_i686"),
224+
])
225+
def test_manylinux2010_implies_manylinux1(self, manylinux2010, manylinux1):
226+
"""
227+
Specifying manylinux2010 implies manylinux1.
228+
"""
229+
groups = {}
230+
supported = pep425tags.get_supported(platform=manylinux2010)
231+
for pyimpl, abi, arch in supported:
232+
groups.setdefault((pyimpl, abi), []).append(arch)
233+
234+
for arches in groups.values():
235+
if arches == ['any']:
236+
continue
237+
assert arches[:2] == [manylinux2010, manylinux1]

tests/unit/test_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ def test_create_and_cleanup_work(self):
549549

550550

551551
class TestGlibc(object):
552-
def test_manylinux1_check_glibc_version(self):
552+
def test_manylinux_check_glibc_version(self):
553553
"""
554554
Test that the check_glibc_version function is robust against weird
555555
glibc version strings.

0 commit comments

Comments
 (0)