From e75015ca9e30e9cc413ea9da9f2b8c5e78441660 Mon Sep 17 00:00:00 2001 From: mattip Date: Wed, 8 Apr 2020 16:18:39 +0300 Subject: [PATCH 1/5] Support PEP 600 tags --- packaging/tags.py | 115 +++++++++++++++++++++++++++++++++------------ tests/test_tags.py | 73 +++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 32 deletions(-) diff --git a/packaging/tags.py b/packaging/tags.py index 9064910b..e1186373 100644 --- a/packaging/tags.py +++ b/packaging/tags.py @@ -13,6 +13,7 @@ EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] del imp +import collections import logging import os import platform @@ -57,6 +58,24 @@ _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 +_LEGACY_MANYLINUX_MAP = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + (2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + (2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + (2, 5): "manylinux1", +} + +# If glibc ever changes its major version, we need to know what the last +# minor version was, so we can build the complete list of all versions. +# For now, guess what the highest minor version might be, assume it will +# be 50 for testing. Once this actually happens, update the dictionary +# with the actual value. +_LAST_GLIBC_MINOR = collections.defaultdict(lambda: 50) # type: Dict[int, int] +glibcVersion = collections.namedtuple("Version", ["major", "minor"]) + + class Tag(object): """ A representation of the tag triple for a wheel. @@ -494,9 +513,16 @@ def _glibc_version_string_ctypes(): # Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, required_major, minimum_minor): +def _check_glibc_version(version_str, tag_major, tag_minor): # type: (str, int, int) -> bool - # Parse string and check against requested version. + # Check against requested version. + sys_major, sys_minor = _parse_glibc_version(version_str) + return (sys_major, sys_minor) >= (tag_major, tag_minor) + + +def _parse_glibc_version(version_str): + # type: (str) -> Tuple[int, int] + # Parse glibc version # # We use a regexp instead of str.split because we want to discard any # random junk that might come after the minor version -- this might happen @@ -509,19 +535,24 @@ def _check_glibc_version(version_str, required_major, minimum_minor): " got: %s" % version_str, RuntimeWarning, ) - return False - return ( - int(m.group("major")) == required_major - and int(m.group("minor")) >= minimum_minor - ) + return -1, -1 + return (int(m.group("major")), int(m.group("minor"))) -def _have_compatible_glibc(required_major, minimum_minor): +def _have_compatible_glibc(tag_major, tag_minor): # type: (int, int) -> bool version_str = _glibc_version_string() if version_str is None: return False - return _check_glibc_version(version_str, required_major, minimum_minor) + return _check_glibc_version(version_str, tag_major, tag_minor) + + +def _get_glibc_version(): + # type: () -> Tuple[int, int] + version_str = _glibc_version_string() + if version_str is None: + return -1, -1 + return _parse_glibc_version(version_str) # Python does not provide platform information at sufficient granularity to @@ -639,7 +670,49 @@ def _have_compatible_manylinux_abi(arch): return _is_linux_armhf() if arch == "i686": return _is_linux_i686() - return True + return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} + + +def _manylinux_tags(linux, arch): + # type: (str, str) -> Iterator[str] + # Oldest glibc to be supported is (2, 17). + too_old_glibc2 = glibcVersion(2, 16) + if arch in {"x86_64", "i686"}: + # On x86/i686 also oldest glibc to be supported is (2, 5). + too_old_glibc2 = glibcVersion(2, 4) + current_glibc = glibcVersion(*_get_glibc_version()) + is_compat = False + glibc_max_list = [current_glibc] + # We can assume compatibility across glibc major versions. + # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 + # + # Build a list of maximum glibc versions so that we can + # output the canonical list of all glibc from current_glibc + # down to too_old_glibc2, including all intermediary versions + for glibc_major in range(current_glibc.major - 1, 1, -1): + glibc_max_list.append(glibcVersion(glibc_major, _LAST_GLIBC_MINOR[glibc_major])) + for glibc_max in glibc_max_list: + if glibc_max.major == too_old_glibc2.major: + min_minor = too_old_glibc2.minor + else: + # For other glibc major versions oldest supported is (x, 0). + min_minor = -1 + for glibc_minor in range(glibc_max.minor, min_minor, -1): + glibc_version = (glibc_max.major, glibc_minor) + tag = "manylinux_{}_{}".format(*glibc_version) + # Once _is_manylinux_compatible() is True, it is True for any + # lower manylinux tag for this glibc major version. + is_compat = is_compat or _is_manylinux_compatible(tag, glibc_version) + if is_compat: + yield linux.replace("linux", tag) + # Handle the manylinux1, manylinux2010, manylinux2014 tags. + if glibc_version in _LEGACY_MANYLINUX_MAP: + legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] + is_compat = is_compat or _is_manylinux_compatible( + legacy_tag, glibc_version + ) + if is_compat: + yield linux.replace("linux", legacy_tag) def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): @@ -650,28 +723,10 @@ def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): linux = "linux_i686" elif linux == "linux_aarch64": linux = "linux_armv7l" - manylinux_support = [] _, arch = linux.split("_", 1) if _have_compatible_manylinux_abi(arch): - if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: - manylinux_support.append( - ("manylinux2014", (2, 17)) - ) # CentOS 7 w/ glibc 2.17 (PEP 599) - if arch in {"x86_64", "i686"}: - manylinux_support.append( - ("manylinux2010", (2, 12)) - ) # CentOS 6 w/ glibc 2.12 (PEP 571) - manylinux_support.append( - ("manylinux1", (2, 5)) - ) # CentOS 5 w/ glibc 2.5 (PEP 513) - manylinux_support_iter = iter(manylinux_support) - for name, glibc_version in manylinux_support_iter: - if _is_manylinux_compatible(name, glibc_version): - yield linux.replace("linux", name) - break - # Support for a later manylinux implies support for an earlier version. - for name, _ in manylinux_support_iter: - yield linux.replace("linux", name) + for tag in _manylinux_tags(linux, arch): + yield tag yield linux diff --git a/tests/test_tags.py b/tests/test_tags.py index 4840e196..d9ec035d 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -326,7 +326,7 @@ def test_is_manylinux_compatible_glibc_support( ("2.4", 2, 4, True), ("2.4", 2, 5, False), ("2.4", 2, 3, True), - ("3.4", 2, 4, False), + ("3.4", 2, 4, True), ], ) def test_check_glibc_version(self, version_str, major, minor, expected): @@ -373,6 +373,13 @@ def test_glibc_version_string_confstr(self, monkeypatch): monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) assert tags._glibc_version_string_confstr() == "2.20" + def test_glibc_version_string_fail(self, monkeypatch): + monkeypatch.setattr(os, "confstr", lambda x: None, raising=False) + monkeypatch.setitem(sys.modules, "ctypes", None) + assert tags._glibc_version_string() is None + assert tags._have_compatible_glibc(2, 5) is False + assert tags._get_glibc_version() == (-1, -1) + @pytest.mark.parametrize( "failure", [pretend.raiser(ValueError), pretend.raiser(OSError), lambda x: "XXX"], @@ -433,12 +440,14 @@ def test_linux_platforms_32_64bit_on_64bit_os( self, arch, is_32bit, expected, monkeypatch ): monkeypatch.setattr(distutils.util, "get_platform", lambda: arch) + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda *args: False) linux_platform = list(tags._linux_platforms(is_32bit=is_32bit))[-1] assert linux_platform == expected def test_linux_platforms_manylinux_unsupported(self, monkeypatch): monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda *args: False) linux_platform = list(tags._linux_platforms(is_32bit=False)) assert linux_platform == ["linux_x86_64"] @@ -450,6 +459,7 @@ def test_linux_platforms_manylinux1(self, is_x86, monkeypatch): if platform.system() != "Linux" or not is_x86: monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") monkeypatch.setattr(platform, "machine", lambda: "x86_64") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) platforms = list(tags._linux_platforms(is_32bit=False)) arch = platform.machine() assert platforms == ["manylinux1_" + arch, "linux_" + arch] @@ -461,9 +471,21 @@ def test_linux_platforms_manylinux2010(self, is_x86, monkeypatch): if platform.system() != "Linux" or not is_x86: monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") monkeypatch.setattr(platform, "machine", lambda: "x86_64") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) platforms = list(tags._linux_platforms(is_32bit=False)) arch = platform.machine() - expected = ["manylinux2010_" + arch, "manylinux1_" + arch, "linux_" + arch] + expected = [ + "manylinux2010_" + arch, + "manylinux_2_11_" + arch, + "manylinux_2_10_" + arch, + "manylinux_2_9_" + arch, + "manylinux_2_8_" + arch, + "manylinux_2_7_" + arch, + "manylinux_2_6_" + arch, + "manylinux_2_5_" + arch, + "manylinux1_" + arch, + "linux_" + arch, + ] assert platforms == expected def test_linux_platforms_manylinux2014(self, is_x86, monkeypatch): @@ -473,17 +495,31 @@ def test_linux_platforms_manylinux2014(self, is_x86, monkeypatch): if platform.system() != "Linux" or not is_x86: monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") monkeypatch.setattr(platform, "machine", lambda: "x86_64") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) platforms = list(tags._linux_platforms(is_32bit=False)) arch = platform.machine() expected = [ "manylinux2014_" + arch, + "manylinux_2_16_" + arch, + "manylinux_2_15_" + arch, + "manylinux_2_14_" + arch, + "manylinux_2_13_" + arch, + "manylinux_2_12_" + arch, "manylinux2010_" + arch, + "manylinux_2_11_" + arch, + "manylinux_2_10_" + arch, + "manylinux_2_9_" + arch, + "manylinux_2_8_" + arch, + "manylinux_2_7_" + arch, + "manylinux_2_6_" + arch, + "manylinux_2_5_" + arch, "manylinux1_" + arch, "linux_" + arch, ] assert platforms == expected def test_linux_platforms_manylinux2014_armhf_abi(self, monkeypatch): + monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.30") monkeypatch.setattr( tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux2014" ) @@ -498,6 +534,7 @@ def test_linux_platforms_manylinux2014_armhf_abi(self, monkeypatch): assert platforms == expected def test_linux_platforms_manylinux2014_i386_abi(self, monkeypatch): + monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.20") monkeypatch.setattr( tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux2014" ) @@ -509,18 +546,50 @@ def test_linux_platforms_manylinux2014_i386_abi(self, monkeypatch): ) platforms = list(tags._linux_platforms(is_32bit=True)) expected = [ + # "manylinux_2_17_i686", # rejected since it comes before manylinux2014 "manylinux2014_i686", + "manylinux_2_16_i686", + "manylinux_2_15_i686", + "manylinux_2_14_i686", + "manylinux_2_13_i686", + "manylinux_2_12_i686", "manylinux2010_i686", + "manylinux_2_11_i686", + "manylinux_2_10_i686", + "manylinux_2_9_i686", + "manylinux_2_8_i686", + "manylinux_2_7_i686", + "manylinux_2_6_i686", + "manylinux_2_5_i686", "manylinux1_i686", "linux_i686", ] assert platforms == expected + def test_linux_platforms_manylinux_glibc3(self, monkeypatch): + # test for a future glic 3.x version + monkeypatch.setattr(tags, "_glibc_version_string", lambda: "3.2") + monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda name, _: True) + monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_aarch64") + monkeypatch.setattr( + sys, + "executable", + os.path.join(os.path.dirname(__file__), "hello-world-aarch64"), + ) + platforms = list(tags._linux_platforms()) + expected = ( + ["manylinux_3_2_aarch64", "manylinux_3_1_aarch64", "manylinux_3_0_aarch64"] + + ["manylinux_2_{}_aarch64".format(i) for i in range(50, 16, -1)] + + ["manylinux2014_aarch64", "linux_aarch64"] + ) + assert platforms == expected + def test_linux_platforms_manylinux2014_armv6l(self, monkeypatch): monkeypatch.setattr( tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux2014" ) monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_armv6l") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) platforms = list(tags._linux_platforms(is_32bit=True)) expected = ["linux_armv6l"] assert platforms == expected From 32293aaf59670d1875dc6446832c2c65dd5bf8ec Mon Sep 17 00:00:00 2001 From: mattip Date: Sun, 3 May 2020 22:47:33 +0300 Subject: [PATCH 2/5] refactor for _manylinux module changes --- packaging/tags.py | 72 ++++++++-------- tests/test_tags.py | 210 +++++++++++++++++++++++++++++++-------------- 2 files changed, 181 insertions(+), 101 deletions(-) diff --git a/packaging/tags.py b/packaging/tags.py index e1186373..e16aa245 100644 --- a/packaging/tags.py +++ b/packaging/tags.py @@ -435,19 +435,35 @@ def mac_platforms(version=None, arch=None): ) -# From PEP 513. -def _is_manylinux_compatible(name, glibc_version): - # type: (str, GlibcVersion) -> bool +# From PEP 513, PEP 600 +def _is_manylinux_compatible(name, arch, glibc_version): + # type: (str, str, GlibcVersion) -> bool + sys_glibc = _get_glibc_version() + if sys_glibc < glibc_version: + return False # Check for presence of _manylinux module. try: import _manylinux # noqa - - return bool(getattr(_manylinux, name + "_compatible")) - except (ImportError, AttributeError): - # Fall through to heuristic check below. + except ImportError: pass - - return _have_compatible_glibc(*glibc_version) + else: + if hasattr(_manylinux, "manylinux_compatible"): + result = _manylinux.manylinux_compatible( + glibc_version[0], glibc_version[1], arch + ) + if result is not None: + return bool(result) + else: + if glibc_version == (2, 5): + if hasattr(_manylinux, "manylinux1_compatible"): + return bool(_manylinux.manylinux1_compatible) + if glibc_version == (2, 12): + if hasattr(_manylinux, "manylinux2010_compatible"): + return bool(_manylinux.manylinux2010_compatible) + if glibc_version == (2, 17): + if hasattr(_manylinux, "manylinux2014_compatible"): + return bool(_manylinux.manylinux2014_compatible) + return True def _glibc_version_string(): @@ -512,17 +528,9 @@ def _glibc_version_string_ctypes(): return version_str -# Separated out from have_compatible_glibc for easier unit testing. -def _check_glibc_version(version_str, tag_major, tag_minor): - # type: (str, int, int) -> bool - # Check against requested version. - sys_major, sys_minor = _parse_glibc_version(version_str) - return (sys_major, sys_minor) >= (tag_major, tag_minor) - - def _parse_glibc_version(version_str): # type: (str) -> Tuple[int, int] - # Parse glibc version + # Parse glibc version. # # We use a regexp instead of str.split because we want to discard any # random junk that might come after the minor version -- this might happen @@ -539,20 +547,19 @@ def _parse_glibc_version(version_str): return (int(m.group("major")), int(m.group("minor"))) -def _have_compatible_glibc(tag_major, tag_minor): - # type: (int, int) -> bool - version_str = _glibc_version_string() - if version_str is None: - return False - return _check_glibc_version(version_str, tag_major, tag_minor) +_glibc_version = [] # type: List[Tuple[int, int]] def _get_glibc_version(): # type: () -> Tuple[int, int] + if _glibc_version: + return _glibc_version[0] version_str = _glibc_version_string() if version_str is None: - return -1, -1 - return _parse_glibc_version(version_str) + _glibc_version.append((-1, -1)) + else: + _glibc_version.append(_parse_glibc_version(version_str)) + return _glibc_version[0] # Python does not provide platform information at sufficient granularity to @@ -681,7 +688,6 @@ def _manylinux_tags(linux, arch): # On x86/i686 also oldest glibc to be supported is (2, 5). too_old_glibc2 = glibcVersion(2, 4) current_glibc = glibcVersion(*_get_glibc_version()) - is_compat = False glibc_max_list = [current_glibc] # We can assume compatibility across glibc major versions. # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 @@ -700,18 +706,12 @@ def _manylinux_tags(linux, arch): for glibc_minor in range(glibc_max.minor, min_minor, -1): glibc_version = (glibc_max.major, glibc_minor) tag = "manylinux_{}_{}".format(*glibc_version) - # Once _is_manylinux_compatible() is True, it is True for any - # lower manylinux tag for this glibc major version. - is_compat = is_compat or _is_manylinux_compatible(tag, glibc_version) - if is_compat: + if _is_manylinux_compatible(tag, arch, glibc_version): yield linux.replace("linux", tag) - # Handle the manylinux1, manylinux2010, manylinux2014 tags. + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. if glibc_version in _LEGACY_MANYLINUX_MAP: legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] - is_compat = is_compat or _is_manylinux_compatible( - legacy_tag, glibc_version - ) - if is_compat: + if _is_manylinux_compatible(legacy_tag, arch, glibc_version): yield linux.replace("linux", legacy_tag) diff --git a/tests/test_tags.py b/tests/test_tags.py index d9ec035d..4f39edeb 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -39,7 +39,7 @@ def is_x86(): @pytest.fixture def manylinux_module(monkeypatch): - monkeypatch.setattr(tags, "_have_compatible_glibc", lambda *args: False) + monkeypatch.setattr(tags, "_get_glibc_version", lambda *args: (2, 20)) module_name = "_manylinux" module = types.ModuleType(module_name) monkeypatch.setitem(sys.modules, module_name, module) @@ -285,26 +285,27 @@ def test_mac_platforms(self): class TestManylinuxPlatform: + def teardown_method(self): + # Clear the version cache + tags._glibc_version = [] + def test_module_declaration_true(self, manylinux_module): manylinux_module.manylinux1_compatible = True - assert tags._is_manylinux_compatible("manylinux1", (2, 5)) + assert tags._is_manylinux_compatible("manylinux1", "x86_64", (2, 5)) def test_module_declaration_false(self, manylinux_module): manylinux_module.manylinux1_compatible = False - assert not tags._is_manylinux_compatible("manylinux1", (2, 5)) + assert not tags._is_manylinux_compatible("manylinux1", "x86_64", (2, 5)) - def test_module_declaration_missing_attribute(self, manylinux_module): - try: + def test_module_declaration_missing_attribute(self, monkeypatch, manylinux_module): + oldval = getattr(manylinux_module, "manylinux1_compatible", None) + if oldval: del manylinux_module.manylinux1_compatible - except AttributeError: - pass - assert not tags._is_manylinux_compatible("manylinux1", (2, 5)) - - def test_is_manylinux_compatible_module_support( - self, manylinux_module, monkeypatch - ): - monkeypatch.setitem(sys.modules, manylinux_module.__name__, None) - assert not tags._is_manylinux_compatible("manylinux1", (2, 5)) + try: + assert tags._is_manylinux_compatible("manylinux1", "x86_64", (2, 5)) + finally: + if oldval: + manylinux_module.manylinux1_compatible = oldval @pytest.mark.parametrize( "version,compatible", (((2, 0), True), ((2, 5), True), ((2, 10), False)) @@ -313,29 +314,16 @@ def test_is_manylinux_compatible_glibc_support( self, version, compatible, monkeypatch ): monkeypatch.setitem(sys.modules, "_manylinux", None) - monkeypatch.setattr( - tags, - "_have_compatible_glibc", - lambda major, minor: (major, minor) <= (2, 5), + monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 5)) + assert ( + bool(tags._is_manylinux_compatible("manylinux1", "any", version)) + == compatible ) - assert bool(tags._is_manylinux_compatible("manylinux1", version)) == compatible - - @pytest.mark.parametrize( - "version_str,major,minor,expected", - [ - ("2.4", 2, 4, True), - ("2.4", 2, 5, False), - ("2.4", 2, 3, True), - ("3.4", 2, 4, True), - ], - ) - def test_check_glibc_version(self, version_str, major, minor, expected): - assert expected == tags._check_glibc_version(version_str, major, minor) @pytest.mark.parametrize("version_str", ["glibc-2.4.5", "2"]) def test_check_glibc_version_warning(self, version_str): with warnings.catch_warnings(record=True) as w: - tags._check_glibc_version(version_str, 2, 4) + tags._parse_glibc_version(version_str) assert len(w) == 1 assert issubclass(w[0].category, RuntimeWarning) @@ -377,7 +365,6 @@ def test_glibc_version_string_fail(self, monkeypatch): monkeypatch.setattr(os, "confstr", lambda x: None, raising=False) monkeypatch.setitem(sys.modules, "ctypes", None) assert tags._glibc_version_string() is None - assert tags._have_compatible_glibc(2, 5) is False assert tags._get_glibc_version() == (-1, -1) @pytest.mark.parametrize( @@ -414,18 +401,18 @@ def test_get_config_var_does_log(self, monkeypatch): ] @pytest.mark.skipif(platform.system() != "Linux", reason="requires Linux") - def test_have_compatible_glibc_linux(self): + def test_is_manylinux_compatible_old(self): # Assuming no one is running this test with a version of glibc released in # 1997. - assert tags._have_compatible_glibc(2, 0) + assert tags._is_manylinux_compatible("any", "any", (2, 0)) - def test_have_compatible_glibc(self, monkeypatch): + def test_is_manylinux_compatible(self, monkeypatch): monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.4") - assert tags._have_compatible_glibc(2, 4) + assert tags._is_manylinux_compatible("", "any", (2, 4)) def test_glibc_version_string_none(self, monkeypatch): monkeypatch.setattr(tags, "_glibc_version_string", lambda: None) - assert not tags._have_compatible_glibc(2, 4) + assert not tags._is_manylinux_compatible("any", "any", (2, 4)) @pytest.mark.parametrize( "arch,is_32bit,expected", @@ -454,27 +441,23 @@ def test_linux_platforms_manylinux_unsupported(self, monkeypatch): def test_linux_platforms_manylinux1(self, is_x86, monkeypatch): monkeypatch.setattr( - tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux1" + tags, "_is_manylinux_compatible", lambda name, *args: name == "manylinux1" ) - if platform.system() != "Linux" or not is_x86: - monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") - monkeypatch.setattr(platform, "machine", lambda: "x86_64") - monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) + monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") + monkeypatch.setattr(platform, "machine", lambda: "x86_64") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) platforms = list(tags._linux_platforms(is_32bit=False)) arch = platform.machine() assert platforms == ["manylinux1_" + arch, "linux_" + arch] def test_linux_platforms_manylinux2010(self, is_x86, monkeypatch): - monkeypatch.setattr( - tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux2010" - ) - if platform.system() != "Linux" or not is_x86: - monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") - monkeypatch.setattr(platform, "machine", lambda: "x86_64") - monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) + monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") + monkeypatch.setattr(platform, "machine", lambda: "x86_64") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.12", raising=False) platforms = list(tags._linux_platforms(is_32bit=False)) arch = platform.machine() expected = [ + "manylinux_2_12_" + arch, "manylinux2010_" + arch, "manylinux_2_11_" + arch, "manylinux_2_10_" + arch, @@ -489,16 +472,13 @@ def test_linux_platforms_manylinux2010(self, is_x86, monkeypatch): assert platforms == expected def test_linux_platforms_manylinux2014(self, is_x86, monkeypatch): - monkeypatch.setattr( - tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux2014" - ) - if platform.system() != "Linux" or not is_x86: - monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") - monkeypatch.setattr(platform, "machine", lambda: "x86_64") - monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) + monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") + monkeypatch.setattr(platform, "machine", lambda: "x86_64") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.17", raising=False) platforms = list(tags._linux_platforms(is_32bit=False)) arch = platform.machine() expected = [ + "manylinux_2_17_" + arch, "manylinux2014_" + arch, "manylinux_2_16_" + arch, "manylinux_2_15_" + arch, @@ -521,7 +501,9 @@ def test_linux_platforms_manylinux2014(self, is_x86, monkeypatch): def test_linux_platforms_manylinux2014_armhf_abi(self, monkeypatch): monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.30") monkeypatch.setattr( - tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux2014" + tags, + "_is_manylinux_compatible", + lambda name, *args: name == "manylinux2014", ) monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_armv7l") monkeypatch.setattr( @@ -534,10 +516,7 @@ def test_linux_platforms_manylinux2014_armhf_abi(self, monkeypatch): assert platforms == expected def test_linux_platforms_manylinux2014_i386_abi(self, monkeypatch): - monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.20") - monkeypatch.setattr( - tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux2014" - ) + monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.17") monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") monkeypatch.setattr( sys, @@ -546,7 +525,7 @@ def test_linux_platforms_manylinux2014_i386_abi(self, monkeypatch): ) platforms = list(tags._linux_platforms(is_32bit=True)) expected = [ - # "manylinux_2_17_i686", # rejected since it comes before manylinux2014 + "manylinux_2_17_i686", "manylinux2014_i686", "manylinux_2_16_i686", "manylinux_2_15_i686", @@ -569,7 +548,7 @@ def test_linux_platforms_manylinux2014_i386_abi(self, monkeypatch): def test_linux_platforms_manylinux_glibc3(self, monkeypatch): # test for a future glic 3.x version monkeypatch.setattr(tags, "_glibc_version_string", lambda: "3.2") - monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda name, _: True) + monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda name, *args: True) monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_aarch64") monkeypatch.setattr( sys, @@ -601,7 +580,7 @@ def test_linux_platforms_manylinux2014_armv6l(self, monkeypatch): def test_linux_platforms_not_manylinux_abi( self, monkeypatch, machine, abi, alt_machine ): - monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda name, _: True) + monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda name, _: False) monkeypatch.setattr( distutils.util, "get_platform", lambda: "linux_{}".format(machine) ) @@ -1146,6 +1125,10 @@ def test_default_platforms(self, monkeypatch): class TestSysTags: + def teardown_method(self): + # Clear the version cache + tags._glibc_version = [] + @pytest.mark.parametrize( "name,expected", [("CPython", "cp"), ("PyPy", "pp"), ("Jython", "jy"), ("IronPython", "ip")], @@ -1218,3 +1201,100 @@ def test_generic(self, monkeypatch): "py" + tags._version_nodot((sys.version_info[0], 0)), "none", "any" ) assert result[-1] == expected + + def test_linux_platforms_manylinux2014_armv6l(self, monkeypatch, manylinux_module): + monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_armv6l") + monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) + platforms = list(tags._linux_platforms(is_32bit=True)) + expected = ["linux_armv6l"] + assert platforms == expected + + def test_skip_manylinux_2014(self, monkeypatch, manylinux_module): + monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_ppc64") + monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 20)) + expected = [ + "manylinux_2_20_ppc64", + "manylinux_2_19_ppc64", + "manylinux_2_18_ppc64", + # "manylinux2014_ppc64", # this one is skipped + # "manylinux_2_17_ppc64", # this one is also skipped + "linux_ppc64", + ] + manylinux_module.manylinux2014_compatible = False + try: + platforms = list(tags._linux_platforms()) + assert platforms == expected + finally: + delattr(manylinux_module, "manylinux2014_compatible") + + @pytest.mark.parametrize( + "machine, abi, alt_machine", + [("x86_64", "x32", "i686"), ("armv7l", "armel", "armv7l")], + ) + def test_linux_platforms_not_manylinux_abi( + self, monkeypatch, manylinux_module, machine, abi, alt_machine + ): + monkeypatch.setattr( + distutils.util, "get_platform", lambda: "linux_{}".format(machine) + ) + monkeypatch.setattr( + sys, + "executable", + os.path.join( + os.path.dirname(__file__), "hello-world-{}-{}".format(machine, abi) + ), + ) + platforms = list(tags._linux_platforms(is_32bit=True)) + expected = ["linux_{}".format(alt_machine)] + assert platforms == expected + + @pytest.mark.parametrize( + "machine, major, minor, tf", [("x86_64", 2, 20, False), ("s390x", 2, 22, True)] + ) + def test_linux_use_manylinux_compatible( + self, monkeypatch, manylinux_module, machine, major, minor, tf + ): + def manylinux_compatible(tag_major, tag_minor, tag_arch): + if tag_major == 2 and tag_minor == 22: + return tag_arch == "s390x" + return False + + monkeypatch.setattr(tags, "_get_glibc_version", lambda: (major, minor)) + monkeypatch.setattr( + distutils.util, "get_platform", lambda: "linux_{}".format(machine) + ) + manylinux_module.manylinux_compatible = manylinux_compatible + try: + platforms = list(tags._linux_platforms()) + if tf: + expected = ["manylinux_2_22_{}".format(machine)] + else: + expected = [] + expected.append("linux_{}".format(machine)) + assert platforms == expected + finally: + delattr(manylinux_module, "manylinux_compatible") + + def test_linux_use_manylinux_compatible_none(self, monkeypatch, manylinux_module): + def manylinux_compatible(tag_major, tag_minor, tag_arch): + if tag_major == 2 and tag_minor < 25: + return False + return None + + monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 30)) + monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") + manylinux_module.manylinux_compatible = manylinux_compatible + try: + platforms = list(tags._linux_platforms()) + expected = [ + "manylinux_2_30_x86_64", + "manylinux_2_29_x86_64", + "manylinux_2_28_x86_64", + "manylinux_2_27_x86_64", + "manylinux_2_26_x86_64", + "manylinux_2_25_x86_64", + "linux_x86_64", + ] + assert platforms == expected + finally: + delattr(manylinux_module, "manylinux_compatible") From 002af442e84c2a3f5af21280131fd223f88a4c63 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 4 May 2020 01:59:49 +0300 Subject: [PATCH 3/5] parametrize tests for coverage --- tests/test_tags.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/test_tags.py b/tests/test_tags.py index 4f39edeb..bf21d234 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -289,23 +289,27 @@ def teardown_method(self): # Clear the version cache tags._glibc_version = [] - def test_module_declaration_true(self, manylinux_module): - manylinux_module.manylinux1_compatible = True - assert tags._is_manylinux_compatible("manylinux1", "x86_64", (2, 5)) - - def test_module_declaration_false(self, manylinux_module): - manylinux_module.manylinux1_compatible = False - assert not tags._is_manylinux_compatible("manylinux1", "x86_64", (2, 5)) - - def test_module_declaration_missing_attribute(self, monkeypatch, manylinux_module): - oldval = getattr(manylinux_module, "manylinux1_compatible", None) - if oldval: - del manylinux_module.manylinux1_compatible - try: - assert tags._is_manylinux_compatible("manylinux1", "x86_64", (2, 5)) - finally: - if oldval: - manylinux_module.manylinux1_compatible = oldval + @pytest.mark.parametrize("tf", (True, False)) + @pytest.mark.parametrize( + "attribute,glibc", (("1", (2, 5)), ("2010", (2, 12)), ("2014", (2, 17))) + ) + def test_module_declaration(self, manylinux_module, attribute, glibc, tf): + manylinux = "manylinux{}_compatible".format(attribute) + setattr(manylinux_module, manylinux, tf) + res = tags._is_manylinux_compatible(manylinux, "x86_64", glibc) + assert tf is res + delattr(manylinux_module, manylinux) + + @pytest.mark.parametrize( + "attribute,glibc", (("1", (2, 5)), ("2010", (2, 12)), ("2014", (2, 17))) + ) + def test_module_declaration_missing_attribute( + self, monkeypatch, manylinux_module, attribute, glibc + ): + manylinux = "manylinux{}_compatible".format(attribute) + if hasattr(manylinux_module, manylinux): + delattr(manylinux_module, manylinux) + assert tags._is_manylinux_compatible(manylinux, "x86_64", glibc) @pytest.mark.parametrize( "version,compatible", (((2, 0), True), ((2, 5), True), ((2, 10), False)) From 1f97b897348e367db4967346f693c8c9686f5113 Mon Sep 17 00:00:00 2001 From: mattip Date: Sat, 23 May 2020 23:40:15 +0300 Subject: [PATCH 4/5] fixes from review --- packaging/tags.py | 4 +-- tests/test_tags.py | 63 +++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/packaging/tags.py b/packaging/tags.py index e16aa245..ba942aaa 100644 --- a/packaging/tags.py +++ b/packaging/tags.py @@ -682,7 +682,7 @@ def _have_compatible_manylinux_abi(arch): def _manylinux_tags(linux, arch): # type: (str, str) -> Iterator[str] - # Oldest glibc to be supported is (2, 17). + # Oldest glibc to be supported regardless of architecture is (2, 17). too_old_glibc2 = glibcVersion(2, 16) if arch in {"x86_64", "i686"}: # On x86/i686 also oldest glibc to be supported is (2, 5). @@ -694,7 +694,7 @@ def _manylinux_tags(linux, arch): # # Build a list of maximum glibc versions so that we can # output the canonical list of all glibc from current_glibc - # down to too_old_glibc2, including all intermediary versions + # down to too_old_glibc2, including all intermediary versions. for glibc_major in range(current_glibc.major - 1, 1, -1): glibc_max_list.append(glibcVersion(glibc_major, _LAST_GLIBC_MINOR[glibc_major])) for glibc_max in glibc_max_list: diff --git a/tests/test_tags.py b/tests/test_tags.py index bf21d234..20685b28 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -293,12 +293,11 @@ def teardown_method(self): @pytest.mark.parametrize( "attribute,glibc", (("1", (2, 5)), ("2010", (2, 12)), ("2014", (2, 17))) ) - def test_module_declaration(self, manylinux_module, attribute, glibc, tf): + def test_module_declaration(self, monkeypatch, manylinux_module, attribute, glibc, tf): manylinux = "manylinux{}_compatible".format(attribute) - setattr(manylinux_module, manylinux, tf) + monkeypatch.setattr(manylinux_module, manylinux, tf, raising=False) res = tags._is_manylinux_compatible(manylinux, "x86_64", glibc) assert tf is res - delattr(manylinux_module, manylinux) @pytest.mark.parametrize( "attribute,glibc", (("1", (2, 5)), ("2010", (2, 12)), ("2014", (2, 17))) @@ -307,8 +306,7 @@ def test_module_declaration_missing_attribute( self, monkeypatch, manylinux_module, attribute, glibc ): manylinux = "manylinux{}_compatible".format(attribute) - if hasattr(manylinux_module, manylinux): - delattr(manylinux_module, manylinux) + monkeypatch.delattr(manylinux_module, manylinux, raising=False) assert tags._is_manylinux_compatible(manylinux, "x86_64", glibc) @pytest.mark.parametrize( @@ -1216,6 +1214,7 @@ def test_linux_platforms_manylinux2014_armv6l(self, monkeypatch, manylinux_modul def test_skip_manylinux_2014(self, monkeypatch, manylinux_module): monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_ppc64") monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 20)) + monkeypatch.setattr(manylinux_module, 'manylinux2014_compatible', False, raising=False) expected = [ "manylinux_2_20_ppc64", "manylinux_2_19_ppc64", @@ -1224,12 +1223,8 @@ def test_skip_manylinux_2014(self, monkeypatch, manylinux_module): # "manylinux_2_17_ppc64", # this one is also skipped "linux_ppc64", ] - manylinux_module.manylinux2014_compatible = False - try: - platforms = list(tags._linux_platforms()) - assert platforms == expected - finally: - delattr(manylinux_module, "manylinux2014_compatible") + platforms = list(tags._linux_platforms()) + assert platforms == expected @pytest.mark.parametrize( "machine, abi, alt_machine", @@ -1267,17 +1262,14 @@ def manylinux_compatible(tag_major, tag_minor, tag_arch): monkeypatch.setattr( distutils.util, "get_platform", lambda: "linux_{}".format(machine) ) - manylinux_module.manylinux_compatible = manylinux_compatible - try: - platforms = list(tags._linux_platforms()) - if tf: - expected = ["manylinux_2_22_{}".format(machine)] - else: - expected = [] - expected.append("linux_{}".format(machine)) - assert platforms == expected - finally: - delattr(manylinux_module, "manylinux_compatible") + monkeypatch.setattr(manylinux_module, 'manylinux_compatible', manylinux_compatible, raising=False) + platforms = list(tags._linux_platforms()) + if tf: + expected = ["manylinux_2_22_{}".format(machine)] + else: + expected = [] + expected.append("linux_{}".format(machine)) + assert platforms == expected def test_linux_use_manylinux_compatible_none(self, monkeypatch, manylinux_module): def manylinux_compatible(tag_major, tag_minor, tag_arch): @@ -1287,18 +1279,15 @@ def manylinux_compatible(tag_major, tag_minor, tag_arch): monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 30)) monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") - manylinux_module.manylinux_compatible = manylinux_compatible - try: - platforms = list(tags._linux_platforms()) - expected = [ - "manylinux_2_30_x86_64", - "manylinux_2_29_x86_64", - "manylinux_2_28_x86_64", - "manylinux_2_27_x86_64", - "manylinux_2_26_x86_64", - "manylinux_2_25_x86_64", - "linux_x86_64", - ] - assert platforms == expected - finally: - delattr(manylinux_module, "manylinux_compatible") + monkeypatch.setattr(manylinux_module, 'manylinux_compatible', manylinux_compatible, raising=False) + platforms = list(tags._linux_platforms()) + expected = [ + "manylinux_2_30_x86_64", + "manylinux_2_29_x86_64", + "manylinux_2_28_x86_64", + "manylinux_2_27_x86_64", + "manylinux_2_26_x86_64", + "manylinux_2_25_x86_64", + "linux_x86_64", + ] + assert platforms == expected From dab14e336612cfff0a7926c8323441ec76d08a30 Mon Sep 17 00:00:00 2001 From: mattip Date: Sun, 24 May 2020 00:03:47 +0300 Subject: [PATCH 5/5] blackify --- tests/test_tags.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/test_tags.py b/tests/test_tags.py index 20685b28..459a8c24 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -293,7 +293,9 @@ def teardown_method(self): @pytest.mark.parametrize( "attribute,glibc", (("1", (2, 5)), ("2010", (2, 12)), ("2014", (2, 17))) ) - def test_module_declaration(self, monkeypatch, manylinux_module, attribute, glibc, tf): + def test_module_declaration( + self, monkeypatch, manylinux_module, attribute, glibc, tf + ): manylinux = "manylinux{}_compatible".format(attribute) monkeypatch.setattr(manylinux_module, manylinux, tf, raising=False) res = tags._is_manylinux_compatible(manylinux, "x86_64", glibc) @@ -1214,7 +1216,9 @@ def test_linux_platforms_manylinux2014_armv6l(self, monkeypatch, manylinux_modul def test_skip_manylinux_2014(self, monkeypatch, manylinux_module): monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_ppc64") monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 20)) - monkeypatch.setattr(manylinux_module, 'manylinux2014_compatible', False, raising=False) + monkeypatch.setattr( + manylinux_module, "manylinux2014_compatible", False, raising=False + ) expected = [ "manylinux_2_20_ppc64", "manylinux_2_19_ppc64", @@ -1262,7 +1266,12 @@ def manylinux_compatible(tag_major, tag_minor, tag_arch): monkeypatch.setattr( distutils.util, "get_platform", lambda: "linux_{}".format(machine) ) - monkeypatch.setattr(manylinux_module, 'manylinux_compatible', manylinux_compatible, raising=False) + monkeypatch.setattr( + manylinux_module, + "manylinux_compatible", + manylinux_compatible, + raising=False, + ) platforms = list(tags._linux_platforms()) if tf: expected = ["manylinux_2_22_{}".format(machine)] @@ -1279,7 +1288,12 @@ def manylinux_compatible(tag_major, tag_minor, tag_arch): monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 30)) monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") - monkeypatch.setattr(manylinux_module, 'manylinux_compatible', manylinux_compatible, raising=False) + monkeypatch.setattr( + manylinux_module, + "manylinux_compatible", + manylinux_compatible, + raising=False, + ) platforms = list(tags._linux_platforms()) expected = [ "manylinux_2_30_x86_64",