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