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 |