Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

_musllinux.py 2.5KB

1 year ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. """PEP 656 support.
  2. This module implements logic to detect if the currently running Python is
  3. linked against musl, and what musl version is used.
  4. """
  5. import functools
  6. import re
  7. import subprocess
  8. import sys
  9. from typing import Iterator, NamedTuple, Optional
  10. from ._elffile import ELFFile
  11. class _MuslVersion(NamedTuple):
  12. major: int
  13. minor: int
  14. def _parse_musl_version(output: str) -> Optional[_MuslVersion]:
  15. lines = [n for n in (n.strip() for n in output.splitlines()) if n]
  16. if len(lines) < 2 or lines[0][:4] != "musl":
  17. return None
  18. m = re.match(r"Version (\d+)\.(\d+)", lines[1])
  19. if not m:
  20. return None
  21. return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
  22. @functools.lru_cache()
  23. def _get_musl_version(executable: str) -> Optional[_MuslVersion]:
  24. """Detect currently-running musl runtime version.
  25. This is done by checking the specified executable's dynamic linking
  26. information, and invoking the loader to parse its output for a version
  27. string. If the loader is musl, the output would be something like::
  28. musl libc (x86_64)
  29. Version 1.2.2
  30. Dynamic Program Loader
  31. """
  32. try:
  33. with open(executable, "rb") as f:
  34. ld = ELFFile(f).interpreter
  35. except (OSError, TypeError, ValueError):
  36. return None
  37. if ld is None or "musl" not in ld:
  38. return None
  39. proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True)
  40. return _parse_musl_version(proc.stderr)
  41. def platform_tags(arch: str) -> Iterator[str]:
  42. """Generate musllinux tags compatible to the current platform.
  43. :param arch: Should be the part of platform tag after the ``linux_``
  44. prefix, e.g. ``x86_64``. The ``linux_`` prefix is assumed as a
  45. prerequisite for the current platform to be musllinux-compatible.
  46. :returns: An iterator of compatible musllinux tags.
  47. """
  48. sys_musl = _get_musl_version(sys.executable)
  49. if sys_musl is None: # Python not dynamically linked against musl.
  50. return
  51. for minor in range(sys_musl.minor, -1, -1):
  52. yield f"musllinux_{sys_musl.major}_{minor}_{arch}"
  53. if __name__ == "__main__": # pragma: no cover
  54. import sysconfig
  55. plat = sysconfig.get_platform()
  56. assert plat.startswith("linux-"), "not linux"
  57. print("plat:", plat)
  58. print("musl:", _get_musl_version(sys.executable))
  59. print("tags:", end=" ")
  60. for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])):
  61. print(t, end="\n ")