1# This file is dual licensed under the terms of the Apache License, Version 2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 3# for complete details. 4 5try: 6 import collections.abc as collections_abc 7except ImportError: 8 import collections as collections_abc 9 10try: 11 import ctypes 12except ImportError: 13 ctypes = None 14import distutils.util 15 16import os 17import platform 18import re 19import sys 20import sysconfig 21import types 22import warnings 23 24import pretend 25import pytest 26 27from packaging import tags 28 29 30@pytest.fixture 31def example_tag(): 32 return tags.Tag("py3", "none", "any") 33 34 35@pytest.fixture 36def is_x86(): 37 return re.match(r"(i\d86|x86_64)", platform.machine()) is not None 38 39 40@pytest.fixture 41def manylinux_module(monkeypatch): 42 monkeypatch.setattr(tags, "_get_glibc_version", lambda *args: (2, 20)) 43 module_name = "_manylinux" 44 module = types.ModuleType(module_name) 45 monkeypatch.setitem(sys.modules, module_name, module) 46 return module 47 48 49@pytest.fixture 50def mock_interpreter_name(monkeypatch): 51 def mock(name): 52 if hasattr(sys, "implementation") and sys.implementation.name != name.lower(): 53 monkeypatch.setattr(sys.implementation, "name", name.lower()) 54 return True 55 elif platform.python_implementation() != name: 56 monkeypatch.setattr(platform, "python_implementation", lambda: name) 57 return True 58 return False 59 60 return mock 61 62 63class TestTag: 64 def test_lowercasing(self): 65 tag = tags.Tag("PY3", "None", "ANY") 66 assert tag.interpreter == "py3" 67 assert tag.abi == "none" 68 assert tag.platform == "any" 69 70 def test_equality(self): 71 args = "py3", "none", "any" 72 assert tags.Tag(*args) == tags.Tag(*args) 73 74 def test_equality_fails_with_non_tag(self): 75 assert not tags.Tag("py3", "none", "any") == "non-tag" 76 77 def test_hashing(self, example_tag): 78 tags = {example_tag} # Should not raise TypeError. 79 assert example_tag in tags 80 81 def test_hash_equality(self, example_tag): 82 equal_tag = tags.Tag("py3", "none", "any") 83 assert example_tag == equal_tag # Sanity check. 84 assert example_tag.__hash__() == equal_tag.__hash__() 85 86 def test_str(self, example_tag): 87 assert str(example_tag) == "py3-none-any" 88 89 def test_repr(self, example_tag): 90 assert repr(example_tag) == "<py3-none-any @ {tag_id}>".format( 91 tag_id=id(example_tag) 92 ) 93 94 def test_attribute_access(self, example_tag): 95 assert example_tag.interpreter == "py3" 96 assert example_tag.abi == "none" 97 assert example_tag.platform == "any" 98 99 100class TestWarnKeywordOnlyParameter: 101 def test_no_argument(self): 102 assert not tags._warn_keyword_parameter("test_warn_keyword_parameters", {}) 103 104 def test_false(self): 105 assert not tags._warn_keyword_parameter( 106 "test_warn_keyword_parameters", {"warn": False} 107 ) 108 109 def test_true(self): 110 assert tags._warn_keyword_parameter( 111 "test_warn_keyword_parameters", {"warn": True} 112 ) 113 114 def test_too_many_arguments(self): 115 message_re = re.compile(r"too_many.+{!r}".format("whatever")) 116 with pytest.raises(TypeError, match=message_re): 117 tags._warn_keyword_parameter("too_many", {"warn": True, "whatever": True}) 118 119 def test_wrong_argument(self): 120 message_re = re.compile(r"missing.+{!r}".format("unexpected")) 121 with pytest.raises(TypeError, match=message_re): 122 tags._warn_keyword_parameter("missing", {"unexpected": True}) 123 124 125class TestParseTag: 126 def test_simple(self, example_tag): 127 parsed_tags = tags.parse_tag(str(example_tag)) 128 assert parsed_tags == {example_tag} 129 130 def test_multi_interpreter(self, example_tag): 131 expected = {example_tag, tags.Tag("py2", "none", "any")} 132 given = tags.parse_tag("py2.py3-none-any") 133 assert given == expected 134 135 def test_multi_platform(self): 136 expected = { 137 tags.Tag("cp37", "cp37m", platform) 138 for platform in ( 139 "macosx_10_6_intel", 140 "macosx_10_9_intel", 141 "macosx_10_9_x86_64", 142 "macosx_10_10_intel", 143 "macosx_10_10_x86_64", 144 ) 145 } 146 given = tags.parse_tag( 147 "cp37-cp37m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64." 148 "macosx_10_10_intel.macosx_10_10_x86_64" 149 ) 150 assert given == expected 151 152 153class TestInterpreterName: 154 def test_sys_implementation_name(self, monkeypatch): 155 class MockImplementation(object): 156 pass 157 158 mock_implementation = MockImplementation() 159 mock_implementation.name = "sillywalk" 160 monkeypatch.setattr(sys, "implementation", mock_implementation, raising=False) 161 assert tags.interpreter_name() == "sillywalk" 162 163 def test_platform(self, monkeypatch): 164 monkeypatch.delattr(sys, "implementation", raising=False) 165 name = "SillyWalk" 166 monkeypatch.setattr(platform, "python_implementation", lambda: name) 167 assert tags.interpreter_name() == name.lower() 168 169 def test_interpreter_short_names(self, mock_interpreter_name, monkeypatch): 170 mock_interpreter_name("cpython") 171 assert tags.interpreter_name() == "cp" 172 173 174class TestInterpreterVersion: 175 def test_warn(self, monkeypatch): 176 class MockConfigVar(object): 177 def __init__(self, return_): 178 self.warn = None 179 self._return = return_ 180 181 def __call__(self, name, warn): 182 self.warn = warn 183 return self._return 184 185 mock_config_var = MockConfigVar("38") 186 monkeypatch.setattr(tags, "_get_config_var", mock_config_var) 187 tags.interpreter_version(warn=True) 188 assert mock_config_var.warn 189 190 def test_python_version_nodot(self, monkeypatch): 191 monkeypatch.setattr(tags, "_get_config_var", lambda var, warn: "NN") 192 assert tags.interpreter_version() == "NN" 193 194 @pytest.mark.parametrize( 195 "version_info,version_str", 196 [ 197 ((1, 2, 3), "12"), 198 ((1, 12, 3), "1_12"), 199 ((11, 2, 3), "11_2"), 200 ((11, 12, 3), "11_12"), 201 ((1, 2, 13), "12"), 202 ], 203 ) 204 def test_sys_version_info(self, version_info, version_str, monkeypatch): 205 monkeypatch.setattr(tags, "_get_config_var", lambda *args, **kwargs: None) 206 monkeypatch.setattr(sys, "version_info", version_info) 207 assert tags.interpreter_version() == version_str 208 209 210class TestMacOSPlatforms: 211 @pytest.mark.parametrize( 212 "arch, is_32bit, expected", 213 [ 214 ("i386", True, "i386"), 215 ("ppc", True, "ppc"), 216 ("x86_64", False, "x86_64"), 217 ("x86_64", True, "i386"), 218 ("ppc64", False, "ppc64"), 219 ("ppc64", True, "ppc"), 220 ], 221 ) 222 def test_architectures(self, arch, is_32bit, expected): 223 assert tags._mac_arch(arch, is_32bit=is_32bit) == expected 224 225 @pytest.mark.parametrize( 226 "version,arch,expected", 227 [ 228 ( 229 (10, 15), 230 "x86_64", 231 ["x86_64", "intel", "fat64", "fat32", "universal2", "universal"], 232 ), 233 ( 234 (10, 4), 235 "x86_64", 236 ["x86_64", "intel", "fat64", "fat32", "universal2", "universal"], 237 ), 238 ((10, 3), "x86_64", []), 239 ((10, 15), "i386", ["i386", "intel", "fat32", "fat", "universal"]), 240 ((10, 4), "i386", ["i386", "intel", "fat32", "fat", "universal"]), 241 ((10, 3), "i386", []), 242 ((10, 15), "ppc64", []), 243 ((10, 6), "ppc64", []), 244 ((10, 5), "ppc64", ["ppc64", "fat64", "universal"]), 245 ((10, 3), "ppc64", []), 246 ((10, 15), "ppc", []), 247 ((10, 7), "ppc", []), 248 ((10, 6), "ppc", ["ppc", "fat32", "fat", "universal"]), 249 ((10, 0), "ppc", ["ppc", "fat32", "fat", "universal"]), 250 ((11, 0), "riscv", ["riscv"]), 251 ( 252 (11, 0), 253 "x86_64", 254 ["x86_64", "intel", "fat64", "fat32", "universal2", "universal"], 255 ), 256 ((11, 0), "arm64", ["arm64", "universal2"]), 257 ((11, 1), "arm64", ["arm64", "universal2"]), 258 ((12, 0), "arm64", ["arm64", "universal2"]), 259 ], 260 ) 261 def test_binary_formats(self, version, arch, expected): 262 assert tags._mac_binary_formats(version, arch) == expected 263 264 def test_version_detection(self, monkeypatch): 265 if platform.system() != "Darwin": 266 monkeypatch.setattr( 267 platform, "mac_ver", lambda: ("10.14", ("", "", ""), "x86_64") 268 ) 269 version = platform.mac_ver()[0].split(".") 270 expected = "macosx_{major}_{minor}".format(major=version[0], minor=version[1]) 271 platforms = list(tags.mac_platforms(arch="x86_64")) 272 assert platforms[0].startswith(expected) 273 274 @pytest.mark.parametrize("arch", ["x86_64", "i386"]) 275 def test_arch_detection(self, arch, monkeypatch): 276 if platform.system() != "Darwin" or platform.mac_ver()[2] != arch: 277 monkeypatch.setattr( 278 platform, "mac_ver", lambda: ("10.14", ("", "", ""), arch) 279 ) 280 monkeypatch.setattr(tags, "_mac_arch", lambda *args: arch) 281 assert next(tags.mac_platforms((10, 14))).endswith(arch) 282 283 def test_mac_platforms(self): 284 platforms = list(tags.mac_platforms((10, 5), "x86_64")) 285 assert platforms == [ 286 "macosx_10_5_x86_64", 287 "macosx_10_5_intel", 288 "macosx_10_5_fat64", 289 "macosx_10_5_fat32", 290 "macosx_10_5_universal2", 291 "macosx_10_5_universal", 292 "macosx_10_4_x86_64", 293 "macosx_10_4_intel", 294 "macosx_10_4_fat64", 295 "macosx_10_4_fat32", 296 "macosx_10_4_universal2", 297 "macosx_10_4_universal", 298 ] 299 300 assert len(list(tags.mac_platforms((10, 17), "x86_64"))) == 14 * 6 301 302 assert not list(tags.mac_platforms((10, 0), "x86_64")) 303 304 @pytest.mark.parametrize("major,minor", [(11, 0), (11, 3), (12, 0), (12, 3)]) 305 def test_macos_11(self, major, minor): 306 platforms = list(tags.mac_platforms((major, minor), "x86_64")) 307 assert "macosx_11_0_arm64" not in platforms 308 assert "macosx_11_0_x86_64" in platforms 309 assert "macosx_11_3_x86_64" not in platforms 310 assert "macosx_11_0_universal" in platforms 311 assert "macosx_11_0_universal2" in platforms 312 # Mac OS "10.16" is the version number that binaries compiled against an old 313 # (pre 11.0) SDK will see. It can also be enabled explicitly for a process 314 # with the environment variable SYSTEM_VERSION_COMPAT=1. 315 assert "macosx_10_16_x86_64" in platforms 316 assert "macosx_10_15_x86_64" in platforms 317 assert "macosx_10_4_x86_64" in platforms 318 assert "macosx_10_3_x86_64" not in platforms 319 if major >= 12: 320 assert "macosx_12_0_x86_64" in platforms 321 assert "macosx_12_0_universal" in platforms 322 assert "macosx_12_0_universal2" in platforms 323 324 platforms = list(tags.mac_platforms((major, minor), "arm64")) 325 assert "macosx_11_0_arm64" in platforms 326 assert "macosx_11_3_arm64" not in platforms 327 assert "macosx_11_0_universal" not in platforms 328 assert "macosx_11_0_universal2" in platforms 329 assert "macosx_10_15_x86_64" not in platforms 330 assert "macosx_10_4_x86_64" not in platforms 331 assert "macosx_10_3_x86_64" not in platforms 332 if major >= 12: 333 assert "macosx_12_0_arm64" in platforms 334 assert "macosx_12_0_universal2" in platforms 335 336 337class TestManylinuxPlatform: 338 def teardown_method(self): 339 # Clear the version cache 340 tags._glibc_version = [] 341 342 @pytest.mark.parametrize("tf", (True, False)) 343 @pytest.mark.parametrize( 344 "attribute,glibc", (("1", (2, 5)), ("2010", (2, 12)), ("2014", (2, 17))) 345 ) 346 def test_module_declaration( 347 self, monkeypatch, manylinux_module, attribute, glibc, tf 348 ): 349 manylinux = "manylinux{}_compatible".format(attribute) 350 monkeypatch.setattr(manylinux_module, manylinux, tf, raising=False) 351 res = tags._is_manylinux_compatible(manylinux, "x86_64", glibc) 352 assert tf is res 353 354 @pytest.mark.parametrize( 355 "attribute,glibc", (("1", (2, 5)), ("2010", (2, 12)), ("2014", (2, 17))) 356 ) 357 def test_module_declaration_missing_attribute( 358 self, monkeypatch, manylinux_module, attribute, glibc 359 ): 360 manylinux = "manylinux{}_compatible".format(attribute) 361 monkeypatch.delattr(manylinux_module, manylinux, raising=False) 362 assert tags._is_manylinux_compatible(manylinux, "x86_64", glibc) 363 364 @pytest.mark.parametrize( 365 "version,compatible", (((2, 0), True), ((2, 5), True), ((2, 10), False)) 366 ) 367 def test_is_manylinux_compatible_glibc_support( 368 self, version, compatible, monkeypatch 369 ): 370 monkeypatch.setitem(sys.modules, "_manylinux", None) 371 monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 5)) 372 assert ( 373 bool(tags._is_manylinux_compatible("manylinux1", "any", version)) 374 == compatible 375 ) 376 377 @pytest.mark.parametrize("version_str", ["glibc-2.4.5", "2"]) 378 def test_check_glibc_version_warning(self, version_str): 379 with warnings.catch_warnings(record=True) as w: 380 tags._parse_glibc_version(version_str) 381 assert len(w) == 1 382 assert issubclass(w[0].category, RuntimeWarning) 383 384 @pytest.mark.skipif(not ctypes, reason="requires ctypes") 385 @pytest.mark.parametrize( 386 "version_str,expected", 387 [ 388 # Be very explicit about bytes and Unicode for Python 2 testing. 389 (b"2.4", "2.4"), 390 (u"2.4", "2.4"), 391 ], 392 ) 393 def test_glibc_version_string(self, version_str, expected, monkeypatch): 394 class LibcVersion: 395 def __init__(self, version_str): 396 self.version_str = version_str 397 398 def __call__(self): 399 return version_str 400 401 class ProcessNamespace: 402 def __init__(self, libc_version): 403 self.gnu_get_libc_version = libc_version 404 405 process_namespace = ProcessNamespace(LibcVersion(version_str)) 406 monkeypatch.setattr(ctypes, "CDLL", lambda _: process_namespace) 407 monkeypatch.setattr(tags, "_glibc_version_string_confstr", lambda: False) 408 409 assert tags._glibc_version_string() == expected 410 411 del process_namespace.gnu_get_libc_version 412 assert tags._glibc_version_string() is None 413 414 def test_glibc_version_string_confstr(self, monkeypatch): 415 monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) 416 assert tags._glibc_version_string_confstr() == "2.20" 417 418 def test_glibc_version_string_fail(self, monkeypatch): 419 monkeypatch.setattr(os, "confstr", lambda x: None, raising=False) 420 monkeypatch.setitem(sys.modules, "ctypes", None) 421 assert tags._glibc_version_string() is None 422 assert tags._get_glibc_version() == (-1, -1) 423 424 @pytest.mark.parametrize( 425 "failure", 426 [pretend.raiser(ValueError), pretend.raiser(OSError), lambda x: "XXX"], 427 ) 428 def test_glibc_version_string_confstr_fail(self, monkeypatch, failure): 429 monkeypatch.setattr(os, "confstr", failure, raising=False) 430 assert tags._glibc_version_string_confstr() is None 431 432 def test_glibc_version_string_confstr_missing(self, monkeypatch): 433 monkeypatch.delattr(os, "confstr", raising=False) 434 assert tags._glibc_version_string_confstr() is None 435 436 def test_glibc_version_string_ctypes_missing(self, monkeypatch): 437 monkeypatch.setitem(sys.modules, "ctypes", None) 438 assert tags._glibc_version_string_ctypes() is None 439 440 def test_glibc_version_string_ctypes_raise_oserror(self, monkeypatch): 441 def patched_cdll(name): 442 raise OSError("Dynamic loading not supported") 443 444 monkeypatch.setattr(ctypes, "CDLL", patched_cdll) 445 assert tags._glibc_version_string_ctypes() is None 446 447 def test_get_config_var_does_not_log(self, monkeypatch): 448 debug = pretend.call_recorder(lambda *a: None) 449 monkeypatch.setattr(tags.logger, "debug", debug) 450 tags._get_config_var("missing") 451 assert debug.calls == [] 452 453 def test_get_config_var_does_log(self, monkeypatch): 454 debug = pretend.call_recorder(lambda *a: None) 455 monkeypatch.setattr(tags.logger, "debug", debug) 456 tags._get_config_var("missing", warn=True) 457 assert debug.calls == [ 458 pretend.call( 459 "Config variable '%s' is unset, Python ABI tag may be incorrect", 460 "missing", 461 ) 462 ] 463 464 @pytest.mark.skipif(platform.system() != "Linux", reason="requires Linux") 465 def test_is_manylinux_compatible_old(self): 466 # Assuming no one is running this test with a version of glibc released in 467 # 1997. 468 assert tags._is_manylinux_compatible("any", "any", (2, 0)) 469 470 def test_is_manylinux_compatible(self, monkeypatch): 471 monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.4") 472 assert tags._is_manylinux_compatible("", "any", (2, 4)) 473 474 def test_glibc_version_string_none(self, monkeypatch): 475 monkeypatch.setattr(tags, "_glibc_version_string", lambda: None) 476 assert not tags._is_manylinux_compatible("any", "any", (2, 4)) 477 478 @pytest.mark.parametrize( 479 "arch,is_32bit,expected", 480 [ 481 ("linux-x86_64", False, "linux_x86_64"), 482 ("linux-x86_64", True, "linux_i686"), 483 ("linux-aarch64", False, "linux_aarch64"), 484 ("linux-aarch64", True, "linux_armv7l"), 485 ], 486 ) 487 def test_linux_platforms_32_64bit_on_64bit_os( 488 self, arch, is_32bit, expected, monkeypatch 489 ): 490 monkeypatch.setattr(distutils.util, "get_platform", lambda: arch) 491 monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) 492 monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda *args: False) 493 linux_platform = list(tags._linux_platforms(is_32bit=is_32bit))[-1] 494 assert linux_platform == expected 495 496 def test_linux_platforms_manylinux_unsupported(self, monkeypatch): 497 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") 498 monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) 499 monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda *args: False) 500 linux_platform = list(tags._linux_platforms(is_32bit=False)) 501 assert linux_platform == ["linux_x86_64"] 502 503 def test_linux_platforms_manylinux1(self, is_x86, monkeypatch): 504 monkeypatch.setattr( 505 tags, "_is_manylinux_compatible", lambda name, *args: name == "manylinux1" 506 ) 507 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") 508 monkeypatch.setattr(platform, "machine", lambda: "x86_64") 509 monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) 510 platforms = list(tags._linux_platforms(is_32bit=False)) 511 arch = platform.machine() 512 assert platforms == ["manylinux1_" + arch, "linux_" + arch] 513 514 def test_linux_platforms_manylinux2010(self, is_x86, monkeypatch): 515 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") 516 monkeypatch.setattr(platform, "machine", lambda: "x86_64") 517 monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.12", raising=False) 518 platforms = list(tags._linux_platforms(is_32bit=False)) 519 arch = platform.machine() 520 expected = [ 521 "manylinux_2_12_" + arch, 522 "manylinux2010_" + arch, 523 "manylinux_2_11_" + arch, 524 "manylinux_2_10_" + arch, 525 "manylinux_2_9_" + arch, 526 "manylinux_2_8_" + arch, 527 "manylinux_2_7_" + arch, 528 "manylinux_2_6_" + arch, 529 "manylinux_2_5_" + arch, 530 "manylinux1_" + arch, 531 "linux_" + arch, 532 ] 533 assert platforms == expected 534 535 def test_linux_platforms_manylinux2014(self, is_x86, monkeypatch): 536 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") 537 monkeypatch.setattr(platform, "machine", lambda: "x86_64") 538 monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.17", raising=False) 539 platforms = list(tags._linux_platforms(is_32bit=False)) 540 arch = platform.machine() 541 expected = [ 542 "manylinux_2_17_" + arch, 543 "manylinux2014_" + arch, 544 "manylinux_2_16_" + arch, 545 "manylinux_2_15_" + arch, 546 "manylinux_2_14_" + arch, 547 "manylinux_2_13_" + arch, 548 "manylinux_2_12_" + arch, 549 "manylinux2010_" + arch, 550 "manylinux_2_11_" + arch, 551 "manylinux_2_10_" + arch, 552 "manylinux_2_9_" + arch, 553 "manylinux_2_8_" + arch, 554 "manylinux_2_7_" + arch, 555 "manylinux_2_6_" + arch, 556 "manylinux_2_5_" + arch, 557 "manylinux1_" + arch, 558 "linux_" + arch, 559 ] 560 assert platforms == expected 561 562 def test_linux_platforms_manylinux2014_armhf_abi(self, monkeypatch): 563 monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.30") 564 monkeypatch.setattr( 565 tags, 566 "_is_manylinux_compatible", 567 lambda name, *args: name == "manylinux2014", 568 ) 569 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_armv7l") 570 monkeypatch.setattr( 571 sys, 572 "executable", 573 os.path.join(os.path.dirname(__file__), "hello-world-armv7l-armhf"), 574 ) 575 platforms = list(tags._linux_platforms(is_32bit=True)) 576 expected = ["manylinux2014_armv7l", "linux_armv7l"] 577 assert platforms == expected 578 579 def test_linux_platforms_manylinux2014_i386_abi(self, monkeypatch): 580 monkeypatch.setattr(tags, "_glibc_version_string", lambda: "2.17") 581 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") 582 monkeypatch.setattr( 583 sys, 584 "executable", 585 os.path.join(os.path.dirname(__file__), "hello-world-x86_64-i386"), 586 ) 587 platforms = list(tags._linux_platforms(is_32bit=True)) 588 expected = [ 589 "manylinux_2_17_i686", 590 "manylinux2014_i686", 591 "manylinux_2_16_i686", 592 "manylinux_2_15_i686", 593 "manylinux_2_14_i686", 594 "manylinux_2_13_i686", 595 "manylinux_2_12_i686", 596 "manylinux2010_i686", 597 "manylinux_2_11_i686", 598 "manylinux_2_10_i686", 599 "manylinux_2_9_i686", 600 "manylinux_2_8_i686", 601 "manylinux_2_7_i686", 602 "manylinux_2_6_i686", 603 "manylinux_2_5_i686", 604 "manylinux1_i686", 605 "linux_i686", 606 ] 607 assert platforms == expected 608 609 def test_linux_platforms_manylinux_glibc3(self, monkeypatch): 610 # test for a future glic 3.x version 611 monkeypatch.setattr(tags, "_glibc_version_string", lambda: "3.2") 612 monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda name, *args: True) 613 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_aarch64") 614 monkeypatch.setattr( 615 sys, 616 "executable", 617 os.path.join(os.path.dirname(__file__), "hello-world-aarch64"), 618 ) 619 platforms = list(tags._linux_platforms(is_32bit=False)) 620 expected = ( 621 ["manylinux_3_2_aarch64", "manylinux_3_1_aarch64", "manylinux_3_0_aarch64"] 622 + ["manylinux_2_{}_aarch64".format(i) for i in range(50, 16, -1)] 623 + ["manylinux2014_aarch64", "linux_aarch64"] 624 ) 625 assert platforms == expected 626 627 def test_linux_platforms_manylinux2014_armv6l(self, monkeypatch): 628 monkeypatch.setattr( 629 tags, "_is_manylinux_compatible", lambda name, _: name == "manylinux2014" 630 ) 631 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_armv6l") 632 monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) 633 platforms = list(tags._linux_platforms(is_32bit=True)) 634 expected = ["linux_armv6l"] 635 assert platforms == expected 636 637 @pytest.mark.parametrize( 638 "machine, abi, alt_machine", 639 [("x86_64", "x32", "i686"), ("armv7l", "armel", "armv7l")], 640 ) 641 def test_linux_platforms_not_manylinux_abi( 642 self, monkeypatch, machine, abi, alt_machine 643 ): 644 monkeypatch.setattr(tags, "_is_manylinux_compatible", lambda name, _: False) 645 monkeypatch.setattr( 646 distutils.util, "get_platform", lambda: "linux_{}".format(machine) 647 ) 648 monkeypatch.setattr( 649 sys, 650 "executable", 651 os.path.join( 652 os.path.dirname(__file__), "hello-world-{}-{}".format(machine, abi) 653 ), 654 ) 655 platforms = list(tags._linux_platforms(is_32bit=True)) 656 expected = ["linux_{}".format(alt_machine)] 657 assert platforms == expected 658 659 @pytest.mark.parametrize( 660 "machine, abi, elf_class, elf_data, elf_machine", 661 [ 662 ( 663 "x86_64", 664 "x32", 665 tags._ELFFileHeader.ELFCLASS32, 666 tags._ELFFileHeader.ELFDATA2LSB, 667 tags._ELFFileHeader.EM_X86_64, 668 ), 669 ( 670 "x86_64", 671 "i386", 672 tags._ELFFileHeader.ELFCLASS32, 673 tags._ELFFileHeader.ELFDATA2LSB, 674 tags._ELFFileHeader.EM_386, 675 ), 676 ( 677 "x86_64", 678 "amd64", 679 tags._ELFFileHeader.ELFCLASS64, 680 tags._ELFFileHeader.ELFDATA2LSB, 681 tags._ELFFileHeader.EM_X86_64, 682 ), 683 ( 684 "armv7l", 685 "armel", 686 tags._ELFFileHeader.ELFCLASS32, 687 tags._ELFFileHeader.ELFDATA2LSB, 688 tags._ELFFileHeader.EM_ARM, 689 ), 690 ( 691 "armv7l", 692 "armhf", 693 tags._ELFFileHeader.ELFCLASS32, 694 tags._ELFFileHeader.ELFDATA2LSB, 695 tags._ELFFileHeader.EM_ARM, 696 ), 697 ( 698 "s390x", 699 "s390x", 700 tags._ELFFileHeader.ELFCLASS64, 701 tags._ELFFileHeader.ELFDATA2MSB, 702 tags._ELFFileHeader.EM_S390, 703 ), 704 ], 705 ) 706 def test_get_elf_header( 707 self, monkeypatch, machine, abi, elf_class, elf_data, elf_machine 708 ): 709 path = os.path.join( 710 os.path.dirname(__file__), "hello-world-{}-{}".format(machine, abi) 711 ) 712 monkeypatch.setattr(sys, "executable", path) 713 elf_header = tags._get_elf_header() 714 assert elf_header.e_ident_class == elf_class 715 assert elf_header.e_ident_data == elf_data 716 assert elf_header.e_machine == elf_machine 717 718 @pytest.mark.parametrize( 719 "content", [None, "invalid-magic", "invalid-class", "invalid-data", "too-short"] 720 ) 721 def test_get_elf_header_bad_excutable(self, monkeypatch, content): 722 if content: 723 path = os.path.join( 724 os.path.dirname(__file__), "hello-world-{}".format(content) 725 ) 726 else: 727 path = None 728 monkeypatch.setattr(sys, "executable", path) 729 assert tags._get_elf_header() is None 730 731 def test_is_linux_armhf_not_elf(self, monkeypatch): 732 monkeypatch.setattr(tags, "_get_elf_header", lambda: None) 733 assert not tags._is_linux_armhf() 734 735 def test_is_linux_i686_not_elf(self, monkeypatch): 736 monkeypatch.setattr(tags, "_get_elf_header", lambda: None) 737 assert not tags._is_linux_i686() 738 739 740@pytest.mark.parametrize( 741 "platform_name,dispatch_func", 742 [ 743 ("Darwin", "mac_platforms"), 744 ("Linux", "_linux_platforms"), 745 ("Generic", "_generic_platforms"), 746 ], 747) 748def test__platform_tags(platform_name, dispatch_func, monkeypatch): 749 expected = ["sillywalk"] 750 monkeypatch.setattr(platform, "system", lambda: platform_name) 751 monkeypatch.setattr(tags, dispatch_func, lambda: expected) 752 assert tags._platform_tags() == expected 753 754 755class TestCPythonABI: 756 @pytest.mark.parametrize( 757 "py_debug,gettotalrefcount,result", 758 [(1, False, True), (0, False, False), (None, True, True)], 759 ) 760 def test_debug(self, py_debug, gettotalrefcount, result, monkeypatch): 761 config = {"Py_DEBUG": py_debug, "WITH_PYMALLOC": 0, "Py_UNICODE_SIZE": 2} 762 monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__) 763 if gettotalrefcount: 764 monkeypatch.setattr(sys, "gettotalrefcount", 1, raising=False) 765 expected = ["cp37d" if result else "cp37"] 766 assert tags._cpython_abis((3, 7)) == expected 767 768 def test_debug_file_extension(self, monkeypatch): 769 config = {"Py_DEBUG": None} 770 monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__) 771 monkeypatch.delattr(sys, "gettotalrefcount", raising=False) 772 monkeypatch.setattr(tags, "EXTENSION_SUFFIXES", {"_d.pyd"}) 773 assert tags._cpython_abis((3, 8)) == ["cp38d", "cp38"] 774 775 @pytest.mark.parametrize( 776 "debug,expected", [(True, ["cp38d", "cp38"]), (False, ["cp38"])] 777 ) 778 def test__debug_cp38(self, debug, expected, monkeypatch): 779 config = {"Py_DEBUG": debug} 780 monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__) 781 assert tags._cpython_abis((3, 8)) == expected 782 783 @pytest.mark.parametrize( 784 "pymalloc,version,result", 785 [ 786 (1, (3, 7), True), 787 (0, (3, 7), False), 788 (None, (3, 7), True), 789 (1, (3, 8), False), 790 ], 791 ) 792 def test_pymalloc(self, pymalloc, version, result, monkeypatch): 793 config = {"Py_DEBUG": 0, "WITH_PYMALLOC": pymalloc, "Py_UNICODE_SIZE": 2} 794 monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__) 795 base_abi = "cp{}{}".format(version[0], version[1]) 796 expected = [base_abi + "m" if result else base_abi] 797 assert tags._cpython_abis(version) == expected 798 799 @pytest.mark.parametrize( 800 "unicode_size,maxunicode,version,result", 801 [ 802 (4, 0x10FFFF, (3, 2), True), 803 (2, 0xFFFF, (3, 2), False), 804 (None, 0x10FFFF, (3, 2), True), 805 (None, 0xFFFF, (3, 2), False), 806 (4, 0x10FFFF, (3, 3), False), 807 ], 808 ) 809 def test_wide_unicode(self, unicode_size, maxunicode, version, result, monkeypatch): 810 config = {"Py_DEBUG": 0, "WITH_PYMALLOC": 0, "Py_UNICODE_SIZE": unicode_size} 811 monkeypatch.setattr(sysconfig, "get_config_var", config.__getitem__) 812 monkeypatch.setattr(sys, "maxunicode", maxunicode) 813 base_abi = "cp" + tags._version_nodot(version) 814 expected = [base_abi + "u" if result else base_abi] 815 assert tags._cpython_abis(version) == expected 816 817 818class TestCPythonTags: 819 def test_iterator_returned(self): 820 result_iterator = tags.cpython_tags( 821 (3, 8), ["cp38d", "cp38"], ["plat1", "plat2"] 822 ) 823 assert isinstance(result_iterator, collections_abc.Iterator) 824 825 def test_all_args(self): 826 result_iterator = tags.cpython_tags( 827 (3, 11), ["cp3_11d", "cp3_11"], ["plat1", "plat2"] 828 ) 829 result = list(result_iterator) 830 assert result == [ 831 tags.Tag("cp3_11", "cp3_11d", "plat1"), 832 tags.Tag("cp3_11", "cp3_11d", "plat2"), 833 tags.Tag("cp3_11", "cp3_11", "plat1"), 834 tags.Tag("cp3_11", "cp3_11", "plat2"), 835 tags.Tag("cp3_11", "abi3", "plat1"), 836 tags.Tag("cp3_11", "abi3", "plat2"), 837 tags.Tag("cp3_11", "none", "plat1"), 838 tags.Tag("cp3_11", "none", "plat2"), 839 tags.Tag("cp3_10", "abi3", "plat1"), 840 tags.Tag("cp3_10", "abi3", "plat2"), 841 tags.Tag("cp39", "abi3", "plat1"), 842 tags.Tag("cp39", "abi3", "plat2"), 843 tags.Tag("cp38", "abi3", "plat1"), 844 tags.Tag("cp38", "abi3", "plat2"), 845 tags.Tag("cp37", "abi3", "plat1"), 846 tags.Tag("cp37", "abi3", "plat2"), 847 tags.Tag("cp36", "abi3", "plat1"), 848 tags.Tag("cp36", "abi3", "plat2"), 849 tags.Tag("cp35", "abi3", "plat1"), 850 tags.Tag("cp35", "abi3", "plat2"), 851 tags.Tag("cp34", "abi3", "plat1"), 852 tags.Tag("cp34", "abi3", "plat2"), 853 tags.Tag("cp33", "abi3", "plat1"), 854 tags.Tag("cp33", "abi3", "plat2"), 855 tags.Tag("cp32", "abi3", "plat1"), 856 tags.Tag("cp32", "abi3", "plat2"), 857 ] 858 result_iterator = tags.cpython_tags( 859 (3, 8), ["cp38d", "cp38"], ["plat1", "plat2"] 860 ) 861 result = list(result_iterator) 862 assert result == [ 863 tags.Tag("cp38", "cp38d", "plat1"), 864 tags.Tag("cp38", "cp38d", "plat2"), 865 tags.Tag("cp38", "cp38", "plat1"), 866 tags.Tag("cp38", "cp38", "plat2"), 867 tags.Tag("cp38", "abi3", "plat1"), 868 tags.Tag("cp38", "abi3", "plat2"), 869 tags.Tag("cp38", "none", "plat1"), 870 tags.Tag("cp38", "none", "plat2"), 871 tags.Tag("cp37", "abi3", "plat1"), 872 tags.Tag("cp37", "abi3", "plat2"), 873 tags.Tag("cp36", "abi3", "plat1"), 874 tags.Tag("cp36", "abi3", "plat2"), 875 tags.Tag("cp35", "abi3", "plat1"), 876 tags.Tag("cp35", "abi3", "plat2"), 877 tags.Tag("cp34", "abi3", "plat1"), 878 tags.Tag("cp34", "abi3", "plat2"), 879 tags.Tag("cp33", "abi3", "plat1"), 880 tags.Tag("cp33", "abi3", "plat2"), 881 tags.Tag("cp32", "abi3", "plat1"), 882 tags.Tag("cp32", "abi3", "plat2"), 883 ] 884 885 result = list(tags.cpython_tags((3, 3), ["cp33m"], ["plat1", "plat2"])) 886 assert result == [ 887 tags.Tag("cp33", "cp33m", "plat1"), 888 tags.Tag("cp33", "cp33m", "plat2"), 889 tags.Tag("cp33", "abi3", "plat1"), 890 tags.Tag("cp33", "abi3", "plat2"), 891 tags.Tag("cp33", "none", "plat1"), 892 tags.Tag("cp33", "none", "plat2"), 893 tags.Tag("cp32", "abi3", "plat1"), 894 tags.Tag("cp32", "abi3", "plat2"), 895 ] 896 897 def test_python_version_defaults(self): 898 tag = next(tags.cpython_tags(abis=["abi3"], platforms=["any"])) 899 interpreter = "cp" + tags._version_nodot(sys.version_info[:2]) 900 assert interpreter == tag.interpreter 901 902 def test_abi_defaults(self, monkeypatch): 903 monkeypatch.setattr(tags, "_cpython_abis", lambda _1, _2: ["cp38"]) 904 result = list(tags.cpython_tags((3, 8), platforms=["any"])) 905 assert tags.Tag("cp38", "cp38", "any") in result 906 assert tags.Tag("cp38", "abi3", "any") in result 907 assert tags.Tag("cp38", "none", "any") in result 908 909 def test_abi_defaults_needs_underscore(self, monkeypatch): 910 monkeypatch.setattr(tags, "_cpython_abis", lambda _1, _2: ["cp3_11"]) 911 result = list(tags.cpython_tags((3, 11), platforms=["any"])) 912 assert tags.Tag("cp3_11", "cp3_11", "any") in result 913 assert tags.Tag("cp3_11", "abi3", "any") in result 914 assert tags.Tag("cp3_11", "none", "any") in result 915 916 def test_platforms_defaults(self, monkeypatch): 917 monkeypatch.setattr(tags, "_platform_tags", lambda: ["plat1"]) 918 result = list(tags.cpython_tags((3, 8), abis=["whatever"])) 919 assert tags.Tag("cp38", "whatever", "plat1") in result 920 921 def test_platforms_defaults_needs_underscore(self, monkeypatch): 922 monkeypatch.setattr(tags, "_platform_tags", lambda: ["plat1"]) 923 result = list(tags.cpython_tags((3, 11), abis=["whatever"])) 924 assert tags.Tag("cp3_11", "whatever", "plat1") in result 925 926 def test_major_only_python_version(self): 927 result = list(tags.cpython_tags((3,), ["abi"], ["plat"])) 928 assert result == [ 929 tags.Tag("cp3", "abi", "plat"), 930 tags.Tag("cp3", "none", "plat"), 931 ] 932 933 def test_major_only_python_version_with_default_abis(self): 934 result = list(tags.cpython_tags((3,), platforms=["plat"])) 935 assert result == [tags.Tag("cp3", "none", "plat")] 936 937 @pytest.mark.parametrize("abis", [[], ["abi3"], ["none"]]) 938 def test_skip_redundant_abis(self, abis): 939 results = list(tags.cpython_tags((3, 0), abis=abis, platforms=["any"])) 940 assert results == [tags.Tag("cp30", "none", "any")] 941 942 def test_abi3_python33(self): 943 results = list(tags.cpython_tags((3, 3), abis=["cp33"], platforms=["plat"])) 944 assert results == [ 945 tags.Tag("cp33", "cp33", "plat"), 946 tags.Tag("cp33", "abi3", "plat"), 947 tags.Tag("cp33", "none", "plat"), 948 tags.Tag("cp32", "abi3", "plat"), 949 ] 950 951 def test_no_excess_abi3_python32(self): 952 results = list(tags.cpython_tags((3, 2), abis=["cp32"], platforms=["plat"])) 953 assert results == [ 954 tags.Tag("cp32", "cp32", "plat"), 955 tags.Tag("cp32", "abi3", "plat"), 956 tags.Tag("cp32", "none", "plat"), 957 ] 958 959 def test_no_abi3_python31(self): 960 results = list(tags.cpython_tags((3, 1), abis=["cp31"], platforms=["plat"])) 961 assert results == [ 962 tags.Tag("cp31", "cp31", "plat"), 963 tags.Tag("cp31", "none", "plat"), 964 ] 965 966 def test_no_abi3_python27(self): 967 results = list(tags.cpython_tags((2, 7), abis=["cp27"], platforms=["plat"])) 968 assert results == [ 969 tags.Tag("cp27", "cp27", "plat"), 970 tags.Tag("cp27", "none", "plat"), 971 ] 972 973 974class TestGenericTags: 975 @pytest.mark.skipif( 976 not sysconfig.get_config_var("SOABI"), reason="SOABI not defined" 977 ) 978 def test__generic_abi_soabi_provided(self): 979 abi = sysconfig.get_config_var("SOABI").replace(".", "_").replace("-", "_") 980 assert [abi] == list(tags._generic_abi()) 981 982 def test__generic_abi(self, monkeypatch): 983 monkeypatch.setattr( 984 sysconfig, "get_config_var", lambda key: "cpython-37m-darwin" 985 ) 986 assert list(tags._generic_abi()) == ["cpython_37m_darwin"] 987 988 def test__generic_abi_no_soabi(self, monkeypatch): 989 monkeypatch.setattr(sysconfig, "get_config_var", lambda key: None) 990 assert not list(tags._generic_abi()) 991 992 def test_generic_platforms(self): 993 platform = distutils.util.get_platform().replace("-", "_") 994 platform = platform.replace(".", "_") 995 assert list(tags._generic_platforms()) == [platform] 996 997 def test_iterator_returned(self): 998 result_iterator = tags.generic_tags("sillywalk33", ["abi"], ["plat1", "plat2"]) 999 assert isinstance(result_iterator, collections_abc.Iterator) 1000 1001 def test_all_args(self): 1002 result_iterator = tags.generic_tags("sillywalk33", ["abi"], ["plat1", "plat2"]) 1003 result = list(result_iterator) 1004 assert result == [ 1005 tags.Tag("sillywalk33", "abi", "plat1"), 1006 tags.Tag("sillywalk33", "abi", "plat2"), 1007 tags.Tag("sillywalk33", "none", "plat1"), 1008 tags.Tag("sillywalk33", "none", "plat2"), 1009 ] 1010 1011 @pytest.mark.parametrize("abi", [[], ["none"]]) 1012 def test_abi_unspecified(self, abi): 1013 no_abi = list(tags.generic_tags("sillywalk34", abi, ["plat1", "plat2"])) 1014 assert no_abi == [ 1015 tags.Tag("sillywalk34", "none", "plat1"), 1016 tags.Tag("sillywalk34", "none", "plat2"), 1017 ] 1018 1019 def test_interpreter_default(self, monkeypatch): 1020 monkeypatch.setattr(tags, "interpreter_name", lambda: "sillywalk") 1021 monkeypatch.setattr(tags, "interpreter_version", lambda warn: "NN") 1022 result = list(tags.generic_tags(abis=["none"], platforms=["any"])) 1023 assert result == [tags.Tag("sillywalkNN", "none", "any")] 1024 1025 def test_abis_default(self, monkeypatch): 1026 monkeypatch.setattr(tags, "_generic_abi", lambda: iter(["abi"])) 1027 result = list(tags.generic_tags(interpreter="sillywalk", platforms=["any"])) 1028 assert result == [ 1029 tags.Tag("sillywalk", "abi", "any"), 1030 tags.Tag("sillywalk", "none", "any"), 1031 ] 1032 1033 def test_platforms_default(self, monkeypatch): 1034 monkeypatch.setattr(tags, "_platform_tags", lambda: ["plat"]) 1035 result = list(tags.generic_tags(interpreter="sillywalk", abis=["none"])) 1036 assert result == [tags.Tag("sillywalk", "none", "plat")] 1037 1038 1039class TestCompatibleTags: 1040 def test_all_args(self): 1041 result = list(tags.compatible_tags((3, 3), "cp33", ["plat1", "plat2"])) 1042 assert result == [ 1043 tags.Tag("py33", "none", "plat1"), 1044 tags.Tag("py33", "none", "plat2"), 1045 tags.Tag("py3", "none", "plat1"), 1046 tags.Tag("py3", "none", "plat2"), 1047 tags.Tag("py32", "none", "plat1"), 1048 tags.Tag("py32", "none", "plat2"), 1049 tags.Tag("py31", "none", "plat1"), 1050 tags.Tag("py31", "none", "plat2"), 1051 tags.Tag("py30", "none", "plat1"), 1052 tags.Tag("py30", "none", "plat2"), 1053 tags.Tag("cp33", "none", "any"), 1054 tags.Tag("py33", "none", "any"), 1055 tags.Tag("py3", "none", "any"), 1056 tags.Tag("py32", "none", "any"), 1057 tags.Tag("py31", "none", "any"), 1058 tags.Tag("py30", "none", "any"), 1059 ] 1060 1061 def test_all_args_needs_underscore(self): 1062 result = list(tags.compatible_tags((3, 11), "cp3_11", ["plat1", "plat2"])) 1063 assert result == [ 1064 tags.Tag("py3_11", "none", "plat1"), 1065 tags.Tag("py3_11", "none", "plat2"), 1066 tags.Tag("py3", "none", "plat1"), 1067 tags.Tag("py3", "none", "plat2"), 1068 tags.Tag("py3_10", "none", "plat1"), 1069 tags.Tag("py3_10", "none", "plat2"), 1070 tags.Tag("py39", "none", "plat1"), 1071 tags.Tag("py39", "none", "plat2"), 1072 tags.Tag("py38", "none", "plat1"), 1073 tags.Tag("py38", "none", "plat2"), 1074 tags.Tag("py37", "none", "plat1"), 1075 tags.Tag("py37", "none", "plat2"), 1076 tags.Tag("py36", "none", "plat1"), 1077 tags.Tag("py36", "none", "plat2"), 1078 tags.Tag("py35", "none", "plat1"), 1079 tags.Tag("py35", "none", "plat2"), 1080 tags.Tag("py34", "none", "plat1"), 1081 tags.Tag("py34", "none", "plat2"), 1082 tags.Tag("py33", "none", "plat1"), 1083 tags.Tag("py33", "none", "plat2"), 1084 tags.Tag("py32", "none", "plat1"), 1085 tags.Tag("py32", "none", "plat2"), 1086 tags.Tag("py31", "none", "plat1"), 1087 tags.Tag("py31", "none", "plat2"), 1088 tags.Tag("py30", "none", "plat1"), 1089 tags.Tag("py30", "none", "plat2"), 1090 tags.Tag("cp3_11", "none", "any"), 1091 tags.Tag("py3_11", "none", "any"), 1092 tags.Tag("py3", "none", "any"), 1093 tags.Tag("py3_10", "none", "any"), 1094 tags.Tag("py39", "none", "any"), 1095 tags.Tag("py38", "none", "any"), 1096 tags.Tag("py37", "none", "any"), 1097 tags.Tag("py36", "none", "any"), 1098 tags.Tag("py35", "none", "any"), 1099 tags.Tag("py34", "none", "any"), 1100 tags.Tag("py33", "none", "any"), 1101 tags.Tag("py32", "none", "any"), 1102 tags.Tag("py31", "none", "any"), 1103 tags.Tag("py30", "none", "any"), 1104 ] 1105 1106 def test_major_only_python_version(self): 1107 result = list(tags.compatible_tags((3,), "cp33", ["plat"])) 1108 assert result == [ 1109 tags.Tag("py3", "none", "plat"), 1110 tags.Tag("cp33", "none", "any"), 1111 tags.Tag("py3", "none", "any"), 1112 ] 1113 1114 def test_default_python_version(self, monkeypatch): 1115 monkeypatch.setattr(sys, "version_info", (3, 1)) 1116 result = list(tags.compatible_tags(interpreter="cp31", platforms=["plat"])) 1117 assert result == [ 1118 tags.Tag("py31", "none", "plat"), 1119 tags.Tag("py3", "none", "plat"), 1120 tags.Tag("py30", "none", "plat"), 1121 tags.Tag("cp31", "none", "any"), 1122 tags.Tag("py31", "none", "any"), 1123 tags.Tag("py3", "none", "any"), 1124 tags.Tag("py30", "none", "any"), 1125 ] 1126 1127 def test_default_python_version_needs_underscore(self, monkeypatch): 1128 monkeypatch.setattr(sys, "version_info", (3, 11)) 1129 result = list(tags.compatible_tags(interpreter="cp3_11", platforms=["plat"])) 1130 assert result == [ 1131 tags.Tag("py3_11", "none", "plat"), 1132 tags.Tag("py3", "none", "plat"), 1133 tags.Tag("py3_10", "none", "plat"), 1134 tags.Tag("py39", "none", "plat"), 1135 tags.Tag("py38", "none", "plat"), 1136 tags.Tag("py37", "none", "plat"), 1137 tags.Tag("py36", "none", "plat"), 1138 tags.Tag("py35", "none", "plat"), 1139 tags.Tag("py34", "none", "plat"), 1140 tags.Tag("py33", "none", "plat"), 1141 tags.Tag("py32", "none", "plat"), 1142 tags.Tag("py31", "none", "plat"), 1143 tags.Tag("py30", "none", "plat"), 1144 tags.Tag("cp3_11", "none", "any"), 1145 tags.Tag("py3_11", "none", "any"), 1146 tags.Tag("py3", "none", "any"), 1147 tags.Tag("py3_10", "none", "any"), 1148 tags.Tag("py39", "none", "any"), 1149 tags.Tag("py38", "none", "any"), 1150 tags.Tag("py37", "none", "any"), 1151 tags.Tag("py36", "none", "any"), 1152 tags.Tag("py35", "none", "any"), 1153 tags.Tag("py34", "none", "any"), 1154 tags.Tag("py33", "none", "any"), 1155 tags.Tag("py32", "none", "any"), 1156 tags.Tag("py31", "none", "any"), 1157 tags.Tag("py30", "none", "any"), 1158 ] 1159 1160 def test_default_interpreter(self): 1161 result = list(tags.compatible_tags((3, 1), platforms=["plat"])) 1162 assert result == [ 1163 tags.Tag("py31", "none", "plat"), 1164 tags.Tag("py3", "none", "plat"), 1165 tags.Tag("py30", "none", "plat"), 1166 tags.Tag("py31", "none", "any"), 1167 tags.Tag("py3", "none", "any"), 1168 tags.Tag("py30", "none", "any"), 1169 ] 1170 1171 def test_default_platforms(self, monkeypatch): 1172 monkeypatch.setattr(tags, "_platform_tags", lambda: iter(["plat", "plat2"])) 1173 result = list(tags.compatible_tags((3, 1), "cp31")) 1174 assert result == [ 1175 tags.Tag("py31", "none", "plat"), 1176 tags.Tag("py31", "none", "plat2"), 1177 tags.Tag("py3", "none", "plat"), 1178 tags.Tag("py3", "none", "plat2"), 1179 tags.Tag("py30", "none", "plat"), 1180 tags.Tag("py30", "none", "plat2"), 1181 tags.Tag("cp31", "none", "any"), 1182 tags.Tag("py31", "none", "any"), 1183 tags.Tag("py3", "none", "any"), 1184 tags.Tag("py30", "none", "any"), 1185 ] 1186 1187 1188class TestSysTags: 1189 def teardown_method(self): 1190 # Clear the version cache 1191 tags._glibc_version = [] 1192 1193 @pytest.mark.parametrize( 1194 "name,expected", 1195 [("CPython", "cp"), ("PyPy", "pp"), ("Jython", "jy"), ("IronPython", "ip")], 1196 ) 1197 def test_interpreter_name(self, name, expected, mock_interpreter_name): 1198 mock_interpreter_name(name) 1199 assert tags.interpreter_name() == expected 1200 1201 def test_iterator(self): 1202 assert isinstance(tags.sys_tags(), collections_abc.Iterator) 1203 1204 def test_mac_cpython(self, mock_interpreter_name, monkeypatch): 1205 if mock_interpreter_name("CPython"): 1206 monkeypatch.setattr(tags, "_cpython_abis", lambda *a: ["cp33m"]) 1207 if platform.system() != "Darwin": 1208 monkeypatch.setattr(platform, "system", lambda: "Darwin") 1209 monkeypatch.setattr(tags, "mac_platforms", lambda: ["macosx_10_5_x86_64"]) 1210 abis = tags._cpython_abis(sys.version_info[:2]) 1211 platforms = list(tags.mac_platforms()) 1212 result = list(tags.sys_tags()) 1213 assert len(abis) == 1 1214 assert result[0] == tags.Tag( 1215 "cp" + tags._version_nodot(sys.version_info[:2]), abis[0], platforms[0] 1216 ) 1217 assert result[-1] == tags.Tag( 1218 "py" + tags._version_nodot((sys.version_info[0], 0)), "none", "any" 1219 ) 1220 1221 def test_windows_cpython(self, mock_interpreter_name, monkeypatch): 1222 if mock_interpreter_name("CPython"): 1223 monkeypatch.setattr(tags, "_cpython_abis", lambda *a: ["cp33m"]) 1224 if platform.system() != "Windows": 1225 monkeypatch.setattr(platform, "system", lambda: "Windows") 1226 monkeypatch.setattr(tags, "_generic_platforms", lambda: ["win_amd64"]) 1227 abis = list(tags._cpython_abis(sys.version_info[:2])) 1228 platforms = list(tags._generic_platforms()) 1229 result = list(tags.sys_tags()) 1230 interpreter = "cp" + tags._version_nodot(sys.version_info[:2]) 1231 assert len(abis) == 1 1232 expected = tags.Tag(interpreter, abis[0], platforms[0]) 1233 assert result[0] == expected 1234 expected = tags.Tag( 1235 "py" + tags._version_nodot((sys.version_info[0], 0)), "none", "any" 1236 ) 1237 assert result[-1] == expected 1238 1239 def test_linux_cpython(self, mock_interpreter_name, monkeypatch): 1240 if mock_interpreter_name("CPython"): 1241 monkeypatch.setattr(tags, "_cpython_abis", lambda *a: ["cp33m"]) 1242 if platform.system() != "Linux": 1243 monkeypatch.setattr(platform, "system", lambda: "Linux") 1244 monkeypatch.setattr(tags, "_linux_platforms", lambda: ["linux_x86_64"]) 1245 abis = list(tags._cpython_abis(sys.version_info[:2])) 1246 platforms = list(tags._linux_platforms()) 1247 result = list(tags.sys_tags()) 1248 expected_interpreter = "cp" + tags._version_nodot(sys.version_info[:2]) 1249 assert len(abis) == 1 1250 assert result[0] == tags.Tag(expected_interpreter, abis[0], platforms[0]) 1251 expected = tags.Tag( 1252 "py" + tags._version_nodot((sys.version_info[0], 0)), "none", "any" 1253 ) 1254 assert result[-1] == expected 1255 1256 def test_generic(self, monkeypatch): 1257 monkeypatch.setattr(platform, "system", lambda: "Generic") 1258 monkeypatch.setattr(tags, "interpreter_name", lambda: "generic") 1259 1260 result = list(tags.sys_tags()) 1261 expected = tags.Tag( 1262 "py" + tags._version_nodot((sys.version_info[0], 0)), "none", "any" 1263 ) 1264 assert result[-1] == expected 1265 1266 def test_linux_platforms_manylinux2014_armv6l(self, monkeypatch, manylinux_module): 1267 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_armv6l") 1268 monkeypatch.setattr(os, "confstr", lambda x: "glibc 2.20", raising=False) 1269 platforms = list(tags._linux_platforms(is_32bit=True)) 1270 expected = ["linux_armv6l"] 1271 assert platforms == expected 1272 1273 def test_skip_manylinux_2014(self, monkeypatch, manylinux_module): 1274 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_ppc64") 1275 monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 20)) 1276 monkeypatch.setattr( 1277 manylinux_module, "manylinux2014_compatible", False, raising=False 1278 ) 1279 expected = [ 1280 "manylinux_2_20_ppc64", 1281 "manylinux_2_19_ppc64", 1282 "manylinux_2_18_ppc64", 1283 # "manylinux2014_ppc64", # this one is skipped 1284 # "manylinux_2_17_ppc64", # this one is also skipped 1285 "linux_ppc64", 1286 ] 1287 platforms = list(tags._linux_platforms()) 1288 assert platforms == expected 1289 1290 @pytest.mark.parametrize( 1291 "machine, abi, alt_machine", 1292 [("x86_64", "x32", "i686"), ("armv7l", "armel", "armv7l")], 1293 ) 1294 def test_linux_platforms_not_manylinux_abi( 1295 self, monkeypatch, manylinux_module, machine, abi, alt_machine 1296 ): 1297 monkeypatch.setattr( 1298 distutils.util, "get_platform", lambda: "linux_{}".format(machine) 1299 ) 1300 monkeypatch.setattr( 1301 sys, 1302 "executable", 1303 os.path.join( 1304 os.path.dirname(__file__), "hello-world-{}-{}".format(machine, abi) 1305 ), 1306 ) 1307 platforms = list(tags._linux_platforms(is_32bit=True)) 1308 expected = ["linux_{}".format(alt_machine)] 1309 assert platforms == expected 1310 1311 @pytest.mark.parametrize( 1312 "machine, major, minor, tf", [("x86_64", 2, 20, False), ("s390x", 2, 22, True)] 1313 ) 1314 def test_linux_use_manylinux_compatible( 1315 self, monkeypatch, manylinux_module, machine, major, minor, tf 1316 ): 1317 def manylinux_compatible(tag_major, tag_minor, tag_arch): 1318 if tag_major == 2 and tag_minor == 22: 1319 return tag_arch == "s390x" 1320 return False 1321 1322 monkeypatch.setattr(tags, "_get_glibc_version", lambda: (major, minor)) 1323 monkeypatch.setattr( 1324 distutils.util, "get_platform", lambda: "linux_{}".format(machine) 1325 ) 1326 monkeypatch.setattr( 1327 manylinux_module, 1328 "manylinux_compatible", 1329 manylinux_compatible, 1330 raising=False, 1331 ) 1332 platforms = list(tags._linux_platforms(is_32bit=False)) 1333 if tf: 1334 expected = ["manylinux_2_22_{}".format(machine)] 1335 else: 1336 expected = [] 1337 expected.append("linux_{}".format(machine)) 1338 assert platforms == expected 1339 1340 def test_linux_use_manylinux_compatible_none(self, monkeypatch, manylinux_module): 1341 def manylinux_compatible(tag_major, tag_minor, tag_arch): 1342 if tag_major == 2 and tag_minor < 25: 1343 return False 1344 return None 1345 1346 monkeypatch.setattr(tags, "_get_glibc_version", lambda: (2, 30)) 1347 monkeypatch.setattr(distutils.util, "get_platform", lambda: "linux_x86_64") 1348 monkeypatch.setattr( 1349 manylinux_module, 1350 "manylinux_compatible", 1351 manylinux_compatible, 1352 raising=False, 1353 ) 1354 platforms = list(tags._linux_platforms(is_32bit=False)) 1355 expected = [ 1356 "manylinux_2_30_x86_64", 1357 "manylinux_2_29_x86_64", 1358 "manylinux_2_28_x86_64", 1359 "manylinux_2_27_x86_64", 1360 "manylinux_2_26_x86_64", 1361 "manylinux_2_25_x86_64", 1362 "linux_x86_64", 1363 ] 1364 assert platforms == expected 1365