Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/packaging/_musllinux.py: 38%
34 statements
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
« prev ^ index » next coverage.py v7.0.1, created at 2022-12-25 06:11 +0000
1"""PEP 656 support.
3This module implements logic to detect if the currently running Python is
4linked against musl, and what musl version is used.
5"""
7import functools
8import re
9import subprocess
10import sys
11from typing import Iterator, NamedTuple, Optional
13from ._elffile import ELFFile
16class _MuslVersion(NamedTuple):
17 major: int
18 minor: int
21def _parse_musl_version(output: str) -> Optional[_MuslVersion]:
22 lines = [n for n in (n.strip() for n in output.splitlines()) if n]
23 if len(lines) < 2 or lines[0][:4] != "musl":
24 return None
25 m = re.match(r"Version (\d+)\.(\d+)", lines[1])
26 if not m:
27 return None
28 return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
31@functools.lru_cache()
32def _get_musl_version(executable: str) -> Optional[_MuslVersion]:
33 """Detect currently-running musl runtime version.
35 This is done by checking the specified executable's dynamic linking
36 information, and invoking the loader to parse its output for a version
37 string. If the loader is musl, the output would be something like::
39 musl libc (x86_64)
40 Version 1.2.2
41 Dynamic Program Loader
42 """
43 try:
44 with open(executable, "rb") as f:
45 ld = ELFFile(f).interpreter
46 except (OSError, TypeError, ValueError):
47 return None
48 if ld is None or "musl" not in ld:
49 return None
50 proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True)
51 return _parse_musl_version(proc.stderr)
54def platform_tags(arch: str) -> Iterator[str]:
55 """Generate musllinux tags compatible to the current platform.
57 :param arch: Should be the part of platform tag after the ``linux_``
58 prefix, e.g. ``x86_64``. The ``linux_`` prefix is assumed as a
59 prerequisite for the current platform to be musllinux-compatible.
61 :returns: An iterator of compatible musllinux tags.
62 """
63 sys_musl = _get_musl_version(sys.executable)
64 if sys_musl is None: # Python not dynamically linked against musl.
65 return
66 for minor in range(sys_musl.minor, -1, -1):
67 yield f"musllinux_{sys_musl.major}_{minor}_{arch}"
70if __name__ == "__main__": # pragma: no cover
71 import sysconfig
73 plat = sysconfig.get_platform()
74 assert plat.startswith("linux-"), "not linux"
76 print("plat:", plat)
77 print("musl:", _get_musl_version(sys.executable))
78 print("tags:", end=" ")
79 for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])):
80 print(t, end="\n ")