Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/packaging/tags.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
| author | shellac |
|---|---|
| date | Mon, 22 Mar 2021 18:12:50 +0000 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:4f3585e2f14b |
|---|---|
| 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 | |
| 5 from __future__ import absolute_import | |
| 6 | |
| 7 import distutils.util | |
| 8 | |
| 9 try: | |
| 10 from importlib.machinery import EXTENSION_SUFFIXES | |
| 11 except ImportError: # pragma: no cover | |
| 12 import imp | |
| 13 | |
| 14 EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] | |
| 15 del imp | |
| 16 import collections | |
| 17 import logging | |
| 18 import os | |
| 19 import platform | |
| 20 import re | |
| 21 import struct | |
| 22 import sys | |
| 23 import sysconfig | |
| 24 import warnings | |
| 25 | |
| 26 from ._typing import TYPE_CHECKING, cast | |
| 27 | |
| 28 if TYPE_CHECKING: # pragma: no cover | |
| 29 from typing import ( | |
| 30 IO, | |
| 31 Dict, | |
| 32 FrozenSet, | |
| 33 Iterable, | |
| 34 Iterator, | |
| 35 List, | |
| 36 Optional, | |
| 37 Sequence, | |
| 38 Tuple, | |
| 39 Union, | |
| 40 ) | |
| 41 | |
| 42 PythonVersion = Sequence[int] | |
| 43 MacVersion = Tuple[int, int] | |
| 44 GlibcVersion = Tuple[int, int] | |
| 45 | |
| 46 | |
| 47 logger = logging.getLogger(__name__) | |
| 48 | |
| 49 INTERPRETER_SHORT_NAMES = { | |
| 50 "python": "py", # Generic. | |
| 51 "cpython": "cp", | |
| 52 "pypy": "pp", | |
| 53 "ironpython": "ip", | |
| 54 "jython": "jy", | |
| 55 } # type: Dict[str, str] | |
| 56 | |
| 57 | |
| 58 _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 | |
| 59 | |
| 60 | |
| 61 _LEGACY_MANYLINUX_MAP = { | |
| 62 # CentOS 7 w/ glibc 2.17 (PEP 599) | |
| 63 (2, 17): "manylinux2014", | |
| 64 # CentOS 6 w/ glibc 2.12 (PEP 571) | |
| 65 (2, 12): "manylinux2010", | |
| 66 # CentOS 5 w/ glibc 2.5 (PEP 513) | |
| 67 (2, 5): "manylinux1", | |
| 68 } | |
| 69 | |
| 70 # If glibc ever changes its major version, we need to know what the last | |
| 71 # minor version was, so we can build the complete list of all versions. | |
| 72 # For now, guess what the highest minor version might be, assume it will | |
| 73 # be 50 for testing. Once this actually happens, update the dictionary | |
| 74 # with the actual value. | |
| 75 _LAST_GLIBC_MINOR = collections.defaultdict(lambda: 50) # type: Dict[int, int] | |
| 76 glibcVersion = collections.namedtuple("Version", ["major", "minor"]) | |
| 77 | |
| 78 | |
| 79 class Tag(object): | |
| 80 """ | |
| 81 A representation of the tag triple for a wheel. | |
| 82 | |
| 83 Instances are considered immutable and thus are hashable. Equality checking | |
| 84 is also supported. | |
| 85 """ | |
| 86 | |
| 87 __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] | |
| 88 | |
| 89 def __init__(self, interpreter, abi, platform): | |
| 90 # type: (str, str, str) -> None | |
| 91 self._interpreter = interpreter.lower() | |
| 92 self._abi = abi.lower() | |
| 93 self._platform = platform.lower() | |
| 94 # The __hash__ of every single element in a Set[Tag] will be evaluated each time | |
| 95 # that a set calls its `.disjoint()` method, which may be called hundreds of | |
| 96 # times when scanning a page of links for packages with tags matching that | |
| 97 # Set[Tag]. Pre-computing the value here produces significant speedups for | |
| 98 # downstream consumers. | |
| 99 self._hash = hash((self._interpreter, self._abi, self._platform)) | |
| 100 | |
| 101 @property | |
| 102 def interpreter(self): | |
| 103 # type: () -> str | |
| 104 return self._interpreter | |
| 105 | |
| 106 @property | |
| 107 def abi(self): | |
| 108 # type: () -> str | |
| 109 return self._abi | |
| 110 | |
| 111 @property | |
| 112 def platform(self): | |
| 113 # type: () -> str | |
| 114 return self._platform | |
| 115 | |
| 116 def __eq__(self, other): | |
| 117 # type: (object) -> bool | |
| 118 if not isinstance(other, Tag): | |
| 119 return NotImplemented | |
| 120 | |
| 121 return ( | |
| 122 (self.platform == other.platform) | |
| 123 and (self.abi == other.abi) | |
| 124 and (self.interpreter == other.interpreter) | |
| 125 ) | |
| 126 | |
| 127 def __hash__(self): | |
| 128 # type: () -> int | |
| 129 return self._hash | |
| 130 | |
| 131 def __str__(self): | |
| 132 # type: () -> str | |
| 133 return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) | |
| 134 | |
| 135 def __repr__(self): | |
| 136 # type: () -> str | |
| 137 return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) | |
| 138 | |
| 139 | |
| 140 def parse_tag(tag): | |
| 141 # type: (str) -> FrozenSet[Tag] | |
| 142 """ | |
| 143 Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. | |
| 144 | |
| 145 Returning a set is required due to the possibility that the tag is a | |
| 146 compressed tag set. | |
| 147 """ | |
| 148 tags = set() | |
| 149 interpreters, abis, platforms = tag.split("-") | |
| 150 for interpreter in interpreters.split("."): | |
| 151 for abi in abis.split("."): | |
| 152 for platform_ in platforms.split("."): | |
| 153 tags.add(Tag(interpreter, abi, platform_)) | |
| 154 return frozenset(tags) | |
| 155 | |
| 156 | |
| 157 def _warn_keyword_parameter(func_name, kwargs): | |
| 158 # type: (str, Dict[str, bool]) -> bool | |
| 159 """ | |
| 160 Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. | |
| 161 """ | |
| 162 if not kwargs: | |
| 163 return False | |
| 164 elif len(kwargs) > 1 or "warn" not in kwargs: | |
| 165 kwargs.pop("warn", None) | |
| 166 arg = next(iter(kwargs.keys())) | |
| 167 raise TypeError( | |
| 168 "{}() got an unexpected keyword argument {!r}".format(func_name, arg) | |
| 169 ) | |
| 170 return kwargs["warn"] | |
| 171 | |
| 172 | |
| 173 def _get_config_var(name, warn=False): | |
| 174 # type: (str, bool) -> Union[int, str, None] | |
| 175 value = sysconfig.get_config_var(name) | |
| 176 if value is None and warn: | |
| 177 logger.debug( | |
| 178 "Config variable '%s' is unset, Python ABI tag may be incorrect", name | |
| 179 ) | |
| 180 return value | |
| 181 | |
| 182 | |
| 183 def _normalize_string(string): | |
| 184 # type: (str) -> str | |
| 185 return string.replace(".", "_").replace("-", "_") | |
| 186 | |
| 187 | |
| 188 def _abi3_applies(python_version): | |
| 189 # type: (PythonVersion) -> bool | |
| 190 """ | |
| 191 Determine if the Python version supports abi3. | |
| 192 | |
| 193 PEP 384 was first implemented in Python 3.2. | |
| 194 """ | |
| 195 return len(python_version) > 1 and tuple(python_version) >= (3, 2) | |
| 196 | |
| 197 | |
| 198 def _cpython_abis(py_version, warn=False): | |
| 199 # type: (PythonVersion, bool) -> List[str] | |
| 200 py_version = tuple(py_version) # To allow for version comparison. | |
| 201 abis = [] | |
| 202 version = _version_nodot(py_version[:2]) | |
| 203 debug = pymalloc = ucs4 = "" | |
| 204 with_debug = _get_config_var("Py_DEBUG", warn) | |
| 205 has_refcount = hasattr(sys, "gettotalrefcount") | |
| 206 # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled | |
| 207 # extension modules is the best option. | |
| 208 # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 | |
| 209 has_ext = "_d.pyd" in EXTENSION_SUFFIXES | |
| 210 if with_debug or (with_debug is None and (has_refcount or has_ext)): | |
| 211 debug = "d" | |
| 212 if py_version < (3, 8): | |
| 213 with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) | |
| 214 if with_pymalloc or with_pymalloc is None: | |
| 215 pymalloc = "m" | |
| 216 if py_version < (3, 3): | |
| 217 unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) | |
| 218 if unicode_size == 4 or ( | |
| 219 unicode_size is None and sys.maxunicode == 0x10FFFF | |
| 220 ): | |
| 221 ucs4 = "u" | |
| 222 elif debug: | |
| 223 # Debug builds can also load "normal" extension modules. | |
| 224 # We can also assume no UCS-4 or pymalloc requirement. | |
| 225 abis.append("cp{version}".format(version=version)) | |
| 226 abis.insert( | |
| 227 0, | |
| 228 "cp{version}{debug}{pymalloc}{ucs4}".format( | |
| 229 version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 | |
| 230 ), | |
| 231 ) | |
| 232 return abis | |
| 233 | |
| 234 | |
| 235 def cpython_tags( | |
| 236 python_version=None, # type: Optional[PythonVersion] | |
| 237 abis=None, # type: Optional[Iterable[str]] | |
| 238 platforms=None, # type: Optional[Iterable[str]] | |
| 239 **kwargs # type: bool | |
| 240 ): | |
| 241 # type: (...) -> Iterator[Tag] | |
| 242 """ | |
| 243 Yields the tags for a CPython interpreter. | |
| 244 | |
| 245 The tags consist of: | |
| 246 - cp<python_version>-<abi>-<platform> | |
| 247 - cp<python_version>-abi3-<platform> | |
| 248 - cp<python_version>-none-<platform> | |
| 249 - cp<less than python_version>-abi3-<platform> # Older Python versions down to 3.2. | |
| 250 | |
| 251 If python_version only specifies a major version then user-provided ABIs and | |
| 252 the 'none' ABItag will be used. | |
| 253 | |
| 254 If 'abi3' or 'none' are specified in 'abis' then they will be yielded at | |
| 255 their normal position and not at the beginning. | |
| 256 """ | |
| 257 warn = _warn_keyword_parameter("cpython_tags", kwargs) | |
| 258 if not python_version: | |
| 259 python_version = sys.version_info[:2] | |
| 260 | |
| 261 interpreter = "cp{}".format(_version_nodot(python_version[:2])) | |
| 262 | |
| 263 if abis is None: | |
| 264 if len(python_version) > 1: | |
| 265 abis = _cpython_abis(python_version, warn) | |
| 266 else: | |
| 267 abis = [] | |
| 268 abis = list(abis) | |
| 269 # 'abi3' and 'none' are explicitly handled later. | |
| 270 for explicit_abi in ("abi3", "none"): | |
| 271 try: | |
| 272 abis.remove(explicit_abi) | |
| 273 except ValueError: | |
| 274 pass | |
| 275 | |
| 276 platforms = list(platforms or _platform_tags()) | |
| 277 for abi in abis: | |
| 278 for platform_ in platforms: | |
| 279 yield Tag(interpreter, abi, platform_) | |
| 280 if _abi3_applies(python_version): | |
| 281 for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): | |
| 282 yield tag | |
| 283 for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): | |
| 284 yield tag | |
| 285 | |
| 286 if _abi3_applies(python_version): | |
| 287 for minor_version in range(python_version[1] - 1, 1, -1): | |
| 288 for platform_ in platforms: | |
| 289 interpreter = "cp{version}".format( | |
| 290 version=_version_nodot((python_version[0], minor_version)) | |
| 291 ) | |
| 292 yield Tag(interpreter, "abi3", platform_) | |
| 293 | |
| 294 | |
| 295 def _generic_abi(): | |
| 296 # type: () -> Iterator[str] | |
| 297 abi = sysconfig.get_config_var("SOABI") | |
| 298 if abi: | |
| 299 yield _normalize_string(abi) | |
| 300 | |
| 301 | |
| 302 def generic_tags( | |
| 303 interpreter=None, # type: Optional[str] | |
| 304 abis=None, # type: Optional[Iterable[str]] | |
| 305 platforms=None, # type: Optional[Iterable[str]] | |
| 306 **kwargs # type: bool | |
| 307 ): | |
| 308 # type: (...) -> Iterator[Tag] | |
| 309 """ | |
| 310 Yields the tags for a generic interpreter. | |
| 311 | |
| 312 The tags consist of: | |
| 313 - <interpreter>-<abi>-<platform> | |
| 314 | |
| 315 The "none" ABI will be added if it was not explicitly provided. | |
| 316 """ | |
| 317 warn = _warn_keyword_parameter("generic_tags", kwargs) | |
| 318 if not interpreter: | |
| 319 interp_name = interpreter_name() | |
| 320 interp_version = interpreter_version(warn=warn) | |
| 321 interpreter = "".join([interp_name, interp_version]) | |
| 322 if abis is None: | |
| 323 abis = _generic_abi() | |
| 324 platforms = list(platforms or _platform_tags()) | |
| 325 abis = list(abis) | |
| 326 if "none" not in abis: | |
| 327 abis.append("none") | |
| 328 for abi in abis: | |
| 329 for platform_ in platforms: | |
| 330 yield Tag(interpreter, abi, platform_) | |
| 331 | |
| 332 | |
| 333 def _py_interpreter_range(py_version): | |
| 334 # type: (PythonVersion) -> Iterator[str] | |
| 335 """ | |
| 336 Yields Python versions in descending order. | |
| 337 | |
| 338 After the latest version, the major-only version will be yielded, and then | |
| 339 all previous versions of that major version. | |
| 340 """ | |
| 341 if len(py_version) > 1: | |
| 342 yield "py{version}".format(version=_version_nodot(py_version[:2])) | |
| 343 yield "py{major}".format(major=py_version[0]) | |
| 344 if len(py_version) > 1: | |
| 345 for minor in range(py_version[1] - 1, -1, -1): | |
| 346 yield "py{version}".format(version=_version_nodot((py_version[0], minor))) | |
| 347 | |
| 348 | |
| 349 def compatible_tags( | |
| 350 python_version=None, # type: Optional[PythonVersion] | |
| 351 interpreter=None, # type: Optional[str] | |
| 352 platforms=None, # type: Optional[Iterable[str]] | |
| 353 ): | |
| 354 # type: (...) -> Iterator[Tag] | |
| 355 """ | |
| 356 Yields the sequence of tags that are compatible with a specific version of Python. | |
| 357 | |
| 358 The tags consist of: | |
| 359 - py*-none-<platform> | |
| 360 - <interpreter>-none-any # ... if `interpreter` is provided. | |
| 361 - py*-none-any | |
| 362 """ | |
| 363 if not python_version: | |
| 364 python_version = sys.version_info[:2] | |
| 365 platforms = list(platforms or _platform_tags()) | |
| 366 for version in _py_interpreter_range(python_version): | |
| 367 for platform_ in platforms: | |
| 368 yield Tag(version, "none", platform_) | |
| 369 if interpreter: | |
| 370 yield Tag(interpreter, "none", "any") | |
| 371 for version in _py_interpreter_range(python_version): | |
| 372 yield Tag(version, "none", "any") | |
| 373 | |
| 374 | |
| 375 def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): | |
| 376 # type: (str, bool) -> str | |
| 377 if not is_32bit: | |
| 378 return arch | |
| 379 | |
| 380 if arch.startswith("ppc"): | |
| 381 return "ppc" | |
| 382 | |
| 383 return "i386" | |
| 384 | |
| 385 | |
| 386 def _mac_binary_formats(version, cpu_arch): | |
| 387 # type: (MacVersion, str) -> List[str] | |
| 388 formats = [cpu_arch] | |
| 389 if cpu_arch == "x86_64": | |
| 390 if version < (10, 4): | |
| 391 return [] | |
| 392 formats.extend(["intel", "fat64", "fat32"]) | |
| 393 | |
| 394 elif cpu_arch == "i386": | |
| 395 if version < (10, 4): | |
| 396 return [] | |
| 397 formats.extend(["intel", "fat32", "fat"]) | |
| 398 | |
| 399 elif cpu_arch == "ppc64": | |
| 400 # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? | |
| 401 if version > (10, 5) or version < (10, 4): | |
| 402 return [] | |
| 403 formats.append("fat64") | |
| 404 | |
| 405 elif cpu_arch == "ppc": | |
| 406 if version > (10, 6): | |
| 407 return [] | |
| 408 formats.extend(["fat32", "fat"]) | |
| 409 | |
| 410 if cpu_arch in {"arm64", "x86_64"}: | |
| 411 formats.append("universal2") | |
| 412 | |
| 413 if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: | |
| 414 formats.append("universal") | |
| 415 | |
| 416 return formats | |
| 417 | |
| 418 | |
| 419 def mac_platforms(version=None, arch=None): | |
| 420 # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] | |
| 421 """ | |
| 422 Yields the platform tags for a macOS system. | |
| 423 | |
| 424 The `version` parameter is a two-item tuple specifying the macOS version to | |
| 425 generate platform tags for. The `arch` parameter is the CPU architecture to | |
| 426 generate platform tags for. Both parameters default to the appropriate value | |
| 427 for the current system. | |
| 428 """ | |
| 429 version_str, _, cpu_arch = platform.mac_ver() # type: ignore | |
| 430 if version is None: | |
| 431 version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) | |
| 432 else: | |
| 433 version = version | |
| 434 if arch is None: | |
| 435 arch = _mac_arch(cpu_arch) | |
| 436 else: | |
| 437 arch = arch | |
| 438 | |
| 439 if (10, 0) <= version and version < (11, 0): | |
| 440 # Prior to Mac OS 11, each yearly release of Mac OS bumped the | |
| 441 # "minor" version number. The major version was always 10. | |
| 442 for minor_version in range(version[1], -1, -1): | |
| 443 compat_version = 10, minor_version | |
| 444 binary_formats = _mac_binary_formats(compat_version, arch) | |
| 445 for binary_format in binary_formats: | |
| 446 yield "macosx_{major}_{minor}_{binary_format}".format( | |
| 447 major=10, minor=minor_version, binary_format=binary_format | |
| 448 ) | |
| 449 | |
| 450 if version >= (11, 0): | |
| 451 # Starting with Mac OS 11, each yearly release bumps the major version | |
| 452 # number. The minor versions are now the midyear updates. | |
| 453 for major_version in range(version[0], 10, -1): | |
| 454 compat_version = major_version, 0 | |
| 455 binary_formats = _mac_binary_formats(compat_version, arch) | |
| 456 for binary_format in binary_formats: | |
| 457 yield "macosx_{major}_{minor}_{binary_format}".format( | |
| 458 major=major_version, minor=0, binary_format=binary_format | |
| 459 ) | |
| 460 | |
| 461 if version >= (11, 0): | |
| 462 # Mac OS 11 on x86_64 is compatible with binaries from previous releases. | |
| 463 # Arm64 support was introduced in 11.0, so no Arm binaries from previous | |
| 464 # releases exist. | |
| 465 # | |
| 466 # However, the "universal2" binary format can have a | |
| 467 # macOS version earlier than 11.0 when the x86_64 part of the binary supports | |
| 468 # that version of macOS. | |
| 469 if arch == "x86_64": | |
| 470 for minor_version in range(16, 3, -1): | |
| 471 compat_version = 10, minor_version | |
| 472 binary_formats = _mac_binary_formats(compat_version, arch) | |
| 473 for binary_format in binary_formats: | |
| 474 yield "macosx_{major}_{minor}_{binary_format}".format( | |
| 475 major=compat_version[0], | |
| 476 minor=compat_version[1], | |
| 477 binary_format=binary_format, | |
| 478 ) | |
| 479 else: | |
| 480 for minor_version in range(16, 3, -1): | |
| 481 compat_version = 10, minor_version | |
| 482 binary_format = "universal2" | |
| 483 yield "macosx_{major}_{minor}_{binary_format}".format( | |
| 484 major=compat_version[0], | |
| 485 minor=compat_version[1], | |
| 486 binary_format=binary_format, | |
| 487 ) | |
| 488 | |
| 489 | |
| 490 # From PEP 513, PEP 600 | |
| 491 def _is_manylinux_compatible(name, arch, glibc_version): | |
| 492 # type: (str, str, GlibcVersion) -> bool | |
| 493 sys_glibc = _get_glibc_version() | |
| 494 if sys_glibc < glibc_version: | |
| 495 return False | |
| 496 # Check for presence of _manylinux module. | |
| 497 try: | |
| 498 import _manylinux # noqa | |
| 499 except ImportError: | |
| 500 pass | |
| 501 else: | |
| 502 if hasattr(_manylinux, "manylinux_compatible"): | |
| 503 result = _manylinux.manylinux_compatible( | |
| 504 glibc_version[0], glibc_version[1], arch | |
| 505 ) | |
| 506 if result is not None: | |
| 507 return bool(result) | |
| 508 else: | |
| 509 if glibc_version == (2, 5): | |
| 510 if hasattr(_manylinux, "manylinux1_compatible"): | |
| 511 return bool(_manylinux.manylinux1_compatible) | |
| 512 if glibc_version == (2, 12): | |
| 513 if hasattr(_manylinux, "manylinux2010_compatible"): | |
| 514 return bool(_manylinux.manylinux2010_compatible) | |
| 515 if glibc_version == (2, 17): | |
| 516 if hasattr(_manylinux, "manylinux2014_compatible"): | |
| 517 return bool(_manylinux.manylinux2014_compatible) | |
| 518 return True | |
| 519 | |
| 520 | |
| 521 def _glibc_version_string(): | |
| 522 # type: () -> Optional[str] | |
| 523 # Returns glibc version string, or None if not using glibc. | |
| 524 return _glibc_version_string_confstr() or _glibc_version_string_ctypes() | |
| 525 | |
| 526 | |
| 527 def _glibc_version_string_confstr(): | |
| 528 # type: () -> Optional[str] | |
| 529 """ | |
| 530 Primary implementation of glibc_version_string using os.confstr. | |
| 531 """ | |
| 532 # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely | |
| 533 # to be broken or missing. This strategy is used in the standard library | |
| 534 # platform module. | |
| 535 # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 | |
| 536 try: | |
| 537 # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". | |
| 538 version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 | |
| 539 "CS_GNU_LIBC_VERSION" | |
| 540 ) | |
| 541 assert version_string is not None | |
| 542 _, version = version_string.split() # type: Tuple[str, str] | |
| 543 except (AssertionError, AttributeError, OSError, ValueError): | |
| 544 # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... | |
| 545 return None | |
| 546 return version | |
| 547 | |
| 548 | |
| 549 def _glibc_version_string_ctypes(): | |
| 550 # type: () -> Optional[str] | |
| 551 """ | |
| 552 Fallback implementation of glibc_version_string using ctypes. | |
| 553 """ | |
| 554 try: | |
| 555 import ctypes | |
| 556 except ImportError: | |
| 557 return None | |
| 558 | |
| 559 # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen | |
| 560 # manpage says, "If filename is NULL, then the returned handle is for the | |
| 561 # main program". This way we can let the linker do the work to figure out | |
| 562 # which libc our process is actually using. | |
| 563 # | |
| 564 # We must also handle the special case where the executable is not a | |
| 565 # dynamically linked executable. This can occur when using musl libc, | |
| 566 # for example. In this situation, dlopen() will error, leading to an | |
| 567 # OSError. Interestingly, at least in the case of musl, there is no | |
| 568 # errno set on the OSError. The single string argument used to construct | |
| 569 # OSError comes from libc itself and is therefore not portable to | |
| 570 # hard code here. In any case, failure to call dlopen() means we | |
| 571 # can proceed, so we bail on our attempt. | |
| 572 try: | |
| 573 # Note: typeshed is wrong here so we are ignoring this line. | |
| 574 process_namespace = ctypes.CDLL(None) # type: ignore | |
| 575 except OSError: | |
| 576 return None | |
| 577 | |
| 578 try: | |
| 579 gnu_get_libc_version = process_namespace.gnu_get_libc_version | |
| 580 except AttributeError: | |
| 581 # Symbol doesn't exist -> therefore, we are not linked to | |
| 582 # glibc. | |
| 583 return None | |
| 584 | |
| 585 # Call gnu_get_libc_version, which returns a string like "2.5" | |
| 586 gnu_get_libc_version.restype = ctypes.c_char_p | |
| 587 version_str = gnu_get_libc_version() # type: str | |
| 588 # py2 / py3 compatibility: | |
| 589 if not isinstance(version_str, str): | |
| 590 version_str = version_str.decode("ascii") | |
| 591 | |
| 592 return version_str | |
| 593 | |
| 594 | |
| 595 def _parse_glibc_version(version_str): | |
| 596 # type: (str) -> Tuple[int, int] | |
| 597 # Parse glibc version. | |
| 598 # | |
| 599 # We use a regexp instead of str.split because we want to discard any | |
| 600 # random junk that might come after the minor version -- this might happen | |
| 601 # in patched/forked versions of glibc (e.g. Linaro's version of glibc | |
| 602 # uses version strings like "2.20-2014.11"). See gh-3588. | |
| 603 m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str) | |
| 604 if not m: | |
| 605 warnings.warn( | |
| 606 "Expected glibc version with 2 components major.minor," | |
| 607 " got: %s" % version_str, | |
| 608 RuntimeWarning, | |
| 609 ) | |
| 610 return -1, -1 | |
| 611 return (int(m.group("major")), int(m.group("minor"))) | |
| 612 | |
| 613 | |
| 614 _glibc_version = [] # type: List[Tuple[int, int]] | |
| 615 | |
| 616 | |
| 617 def _get_glibc_version(): | |
| 618 # type: () -> Tuple[int, int] | |
| 619 if _glibc_version: | |
| 620 return _glibc_version[0] | |
| 621 version_str = _glibc_version_string() | |
| 622 if version_str is None: | |
| 623 _glibc_version.append((-1, -1)) | |
| 624 else: | |
| 625 _glibc_version.append(_parse_glibc_version(version_str)) | |
| 626 return _glibc_version[0] | |
| 627 | |
| 628 | |
| 629 # Python does not provide platform information at sufficient granularity to | |
| 630 # identify the architecture of the running executable in some cases, so we | |
| 631 # determine it dynamically by reading the information from the running | |
| 632 # process. This only applies on Linux, which uses the ELF format. | |
| 633 class _ELFFileHeader(object): | |
| 634 # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header | |
| 635 class _InvalidELFFileHeader(ValueError): | |
| 636 """ | |
| 637 An invalid ELF file header was found. | |
| 638 """ | |
| 639 | |
| 640 ELF_MAGIC_NUMBER = 0x7F454C46 | |
| 641 ELFCLASS32 = 1 | |
| 642 ELFCLASS64 = 2 | |
| 643 ELFDATA2LSB = 1 | |
| 644 ELFDATA2MSB = 2 | |
| 645 EM_386 = 3 | |
| 646 EM_S390 = 22 | |
| 647 EM_ARM = 40 | |
| 648 EM_X86_64 = 62 | |
| 649 EF_ARM_ABIMASK = 0xFF000000 | |
| 650 EF_ARM_ABI_VER5 = 0x05000000 | |
| 651 EF_ARM_ABI_FLOAT_HARD = 0x00000400 | |
| 652 | |
| 653 def __init__(self, file): | |
| 654 # type: (IO[bytes]) -> None | |
| 655 def unpack(fmt): | |
| 656 # type: (str) -> int | |
| 657 try: | |
| 658 (result,) = struct.unpack( | |
| 659 fmt, file.read(struct.calcsize(fmt)) | |
| 660 ) # type: (int, ) | |
| 661 except struct.error: | |
| 662 raise _ELFFileHeader._InvalidELFFileHeader() | |
| 663 return result | |
| 664 | |
| 665 self.e_ident_magic = unpack(">I") | |
| 666 if self.e_ident_magic != self.ELF_MAGIC_NUMBER: | |
| 667 raise _ELFFileHeader._InvalidELFFileHeader() | |
| 668 self.e_ident_class = unpack("B") | |
| 669 if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: | |
| 670 raise _ELFFileHeader._InvalidELFFileHeader() | |
| 671 self.e_ident_data = unpack("B") | |
| 672 if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: | |
| 673 raise _ELFFileHeader._InvalidELFFileHeader() | |
| 674 self.e_ident_version = unpack("B") | |
| 675 self.e_ident_osabi = unpack("B") | |
| 676 self.e_ident_abiversion = unpack("B") | |
| 677 self.e_ident_pad = file.read(7) | |
| 678 format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H" | |
| 679 format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I" | |
| 680 format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q" | |
| 681 format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q | |
| 682 self.e_type = unpack(format_h) | |
| 683 self.e_machine = unpack(format_h) | |
| 684 self.e_version = unpack(format_i) | |
| 685 self.e_entry = unpack(format_p) | |
| 686 self.e_phoff = unpack(format_p) | |
| 687 self.e_shoff = unpack(format_p) | |
| 688 self.e_flags = unpack(format_i) | |
| 689 self.e_ehsize = unpack(format_h) | |
| 690 self.e_phentsize = unpack(format_h) | |
| 691 self.e_phnum = unpack(format_h) | |
| 692 self.e_shentsize = unpack(format_h) | |
| 693 self.e_shnum = unpack(format_h) | |
| 694 self.e_shstrndx = unpack(format_h) | |
| 695 | |
| 696 | |
| 697 def _get_elf_header(): | |
| 698 # type: () -> Optional[_ELFFileHeader] | |
| 699 try: | |
| 700 with open(sys.executable, "rb") as f: | |
| 701 elf_header = _ELFFileHeader(f) | |
| 702 except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): | |
| 703 return None | |
| 704 return elf_header | |
| 705 | |
| 706 | |
| 707 def _is_linux_armhf(): | |
| 708 # type: () -> bool | |
| 709 # hard-float ABI can be detected from the ELF header of the running | |
| 710 # process | |
| 711 # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf | |
| 712 elf_header = _get_elf_header() | |
| 713 if elf_header is None: | |
| 714 return False | |
| 715 result = elf_header.e_ident_class == elf_header.ELFCLASS32 | |
| 716 result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB | |
| 717 result &= elf_header.e_machine == elf_header.EM_ARM | |
| 718 result &= ( | |
| 719 elf_header.e_flags & elf_header.EF_ARM_ABIMASK | |
| 720 ) == elf_header.EF_ARM_ABI_VER5 | |
| 721 result &= ( | |
| 722 elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD | |
| 723 ) == elf_header.EF_ARM_ABI_FLOAT_HARD | |
| 724 return result | |
| 725 | |
| 726 | |
| 727 def _is_linux_i686(): | |
| 728 # type: () -> bool | |
| 729 elf_header = _get_elf_header() | |
| 730 if elf_header is None: | |
| 731 return False | |
| 732 result = elf_header.e_ident_class == elf_header.ELFCLASS32 | |
| 733 result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB | |
| 734 result &= elf_header.e_machine == elf_header.EM_386 | |
| 735 return result | |
| 736 | |
| 737 | |
| 738 def _have_compatible_manylinux_abi(arch): | |
| 739 # type: (str) -> bool | |
| 740 if arch == "armv7l": | |
| 741 return _is_linux_armhf() | |
| 742 if arch == "i686": | |
| 743 return _is_linux_i686() | |
| 744 return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} | |
| 745 | |
| 746 | |
| 747 def _manylinux_tags(linux, arch): | |
| 748 # type: (str, str) -> Iterator[str] | |
| 749 # Oldest glibc to be supported regardless of architecture is (2, 17). | |
| 750 too_old_glibc2 = glibcVersion(2, 16) | |
| 751 if arch in {"x86_64", "i686"}: | |
| 752 # On x86/i686 also oldest glibc to be supported is (2, 5). | |
| 753 too_old_glibc2 = glibcVersion(2, 4) | |
| 754 current_glibc = glibcVersion(*_get_glibc_version()) | |
| 755 glibc_max_list = [current_glibc] | |
| 756 # We can assume compatibility across glibc major versions. | |
| 757 # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 | |
| 758 # | |
| 759 # Build a list of maximum glibc versions so that we can | |
| 760 # output the canonical list of all glibc from current_glibc | |
| 761 # down to too_old_glibc2, including all intermediary versions. | |
| 762 for glibc_major in range(current_glibc.major - 1, 1, -1): | |
| 763 glibc_max_list.append(glibcVersion(glibc_major, _LAST_GLIBC_MINOR[glibc_major])) | |
| 764 for glibc_max in glibc_max_list: | |
| 765 if glibc_max.major == too_old_glibc2.major: | |
| 766 min_minor = too_old_glibc2.minor | |
| 767 else: | |
| 768 # For other glibc major versions oldest supported is (x, 0). | |
| 769 min_minor = -1 | |
| 770 for glibc_minor in range(glibc_max.minor, min_minor, -1): | |
| 771 glibc_version = (glibc_max.major, glibc_minor) | |
| 772 tag = "manylinux_{}_{}".format(*glibc_version) | |
| 773 if _is_manylinux_compatible(tag, arch, glibc_version): | |
| 774 yield linux.replace("linux", tag) | |
| 775 # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. | |
| 776 if glibc_version in _LEGACY_MANYLINUX_MAP: | |
| 777 legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] | |
| 778 if _is_manylinux_compatible(legacy_tag, arch, glibc_version): | |
| 779 yield linux.replace("linux", legacy_tag) | |
| 780 | |
| 781 | |
| 782 def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): | |
| 783 # type: (bool) -> Iterator[str] | |
| 784 linux = _normalize_string(distutils.util.get_platform()) | |
| 785 if is_32bit: | |
| 786 if linux == "linux_x86_64": | |
| 787 linux = "linux_i686" | |
| 788 elif linux == "linux_aarch64": | |
| 789 linux = "linux_armv7l" | |
| 790 _, arch = linux.split("_", 1) | |
| 791 if _have_compatible_manylinux_abi(arch): | |
| 792 for tag in _manylinux_tags(linux, arch): | |
| 793 yield tag | |
| 794 yield linux | |
| 795 | |
| 796 | |
| 797 def _generic_platforms(): | |
| 798 # type: () -> Iterator[str] | |
| 799 yield _normalize_string(distutils.util.get_platform()) | |
| 800 | |
| 801 | |
| 802 def _platform_tags(): | |
| 803 # type: () -> Iterator[str] | |
| 804 """ | |
| 805 Provides the platform tags for this installation. | |
| 806 """ | |
| 807 if platform.system() == "Darwin": | |
| 808 return mac_platforms() | |
| 809 elif platform.system() == "Linux": | |
| 810 return _linux_platforms() | |
| 811 else: | |
| 812 return _generic_platforms() | |
| 813 | |
| 814 | |
| 815 def interpreter_name(): | |
| 816 # type: () -> str | |
| 817 """ | |
| 818 Returns the name of the running interpreter. | |
| 819 """ | |
| 820 try: | |
| 821 name = sys.implementation.name # type: ignore | |
| 822 except AttributeError: # pragma: no cover | |
| 823 # Python 2.7 compatibility. | |
| 824 name = platform.python_implementation().lower() | |
| 825 return INTERPRETER_SHORT_NAMES.get(name) or name | |
| 826 | |
| 827 | |
| 828 def interpreter_version(**kwargs): | |
| 829 # type: (bool) -> str | |
| 830 """ | |
| 831 Returns the version of the running interpreter. | |
| 832 """ | |
| 833 warn = _warn_keyword_parameter("interpreter_version", kwargs) | |
| 834 version = _get_config_var("py_version_nodot", warn=warn) | |
| 835 if version: | |
| 836 version = str(version) | |
| 837 else: | |
| 838 version = _version_nodot(sys.version_info[:2]) | |
| 839 return version | |
| 840 | |
| 841 | |
| 842 def _version_nodot(version): | |
| 843 # type: (PythonVersion) -> str | |
| 844 return "".join(map(str, version)) | |
| 845 | |
| 846 | |
| 847 def sys_tags(**kwargs): | |
| 848 # type: (bool) -> Iterator[Tag] | |
| 849 """ | |
| 850 Returns the sequence of tag triples for the running interpreter. | |
| 851 | |
| 852 The order of the sequence corresponds to priority order for the | |
| 853 interpreter, from most to least important. | |
| 854 """ | |
| 855 warn = _warn_keyword_parameter("sys_tags", kwargs) | |
| 856 | |
| 857 interp_name = interpreter_name() | |
| 858 if interp_name == "cp": | |
| 859 for tag in cpython_tags(warn=warn): | |
| 860 yield tag | |
| 861 else: | |
| 862 for tag in generic_tags(): | |
| 863 yield tag | |
| 864 | |
| 865 for tag in compatible_tags(): | |
| 866 yield tag |
