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