diff options
author | Alex Grönholm <alex.gronholm@nextday.fi> | 2022-12-10 14:41:36 +0200 |
---|---|---|
committer | Alex Grönholm <alex.gronholm@nextday.fi> | 2022-12-10 14:41:36 +0200 |
commit | 861a3a35437aad0d2358a6a89eebe625e80d496b (patch) | |
tree | 0680862391e1ad9733ba1526a4615778ddc13186 | |
parent | 1b1486da8f84445e42814142bcfd1c1ca432b5d8 (diff) | |
download | wheel-git-861a3a35437aad0d2358a6a89eebe625e80d496b.tar.gz |
Updated vendored packaging lib to v22.0
-rw-r--r-- | docs/news.rst | 4 | ||||
-rw-r--r-- | src/wheel/vendored/packaging/_manylinux.py | 167 | ||||
-rw-r--r-- | src/wheel/vendored/packaging/_musllinux.py | 80 | ||||
-rw-r--r-- | src/wheel/vendored/packaging/tags.py | 71 | ||||
-rw-r--r-- | src/wheel/vendored/vendor.txt | 2 |
5 files changed, 117 insertions, 207 deletions
diff --git a/docs/news.rst b/docs/news.rst index fa5b8f0..ab0f3c9 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -1,6 +1,10 @@ Release Notes ============= +**UNRELEASED** + +- Updated vendored ``packaging`` to 22.0 + **0.38.4 (2022-11-09)** - Fixed ``PKG-INFO`` conversion in ``bdist_wheel`` mangling UTF-8 header values in diff --git a/src/wheel/vendored/packaging/_manylinux.py b/src/wheel/vendored/packaging/_manylinux.py index 4934ba8..2f0cc74 100644 --- a/src/wheel/vendored/packaging/_manylinux.py +++ b/src/wheel/vendored/packaging/_manylinux.py @@ -1,123 +1,58 @@ -from __future__ import annotations - import collections +import contextlib import functools import os import re -import struct import sys import warnings -from typing import IO, Iterator, NamedTuple - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader: - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file: IO[bytes]) -> None: - def unpack(fmt: str) -> int: - try: - data = file.read(struct.calcsize(fmt)) - result: tuple[int, ...] = struct.unpack(fmt, data) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result[0] - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H" - format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I" - format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header() -> _ELFFileHeader | None: +from typing import Dict, Generator, Iterator, NamedTuple, Optional, Tuple + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None -def _is_linux_armhf() -> bool: +def _is_linux_armhf(executable: str) -> bool: # hard-float ABI can be detected from the ELF header of the running # process # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686() -> bool: - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) -def _have_compatible_abi(arch: str) -> bool: +def _have_compatible_abi(executable: str, arch: str) -> bool: if arch == "armv7l": - return _is_linux_armhf() + return _is_linux_armhf(executable) if arch == "i686": - return _is_linux_i686() + return _is_linux_i686(executable) return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} @@ -126,7 +61,7 @@ def _have_compatible_abi(arch: str) -> bool: # For now, guess what the highest minor version might be, assume it will # be 50 for testing. Once this actually happens, update the dictionary # with the actual value. -_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50) +_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) class _GLibCVersion(NamedTuple): @@ -134,7 +69,7 @@ class _GLibCVersion(NamedTuple): minor: int -def _glibc_version_string_confstr() -> str | None: +def _glibc_version_string_confstr() -> Optional[str]: """ Primary implementation of glibc_version_string using os.confstr. """ @@ -143,17 +78,17 @@ def _glibc_version_string_confstr() -> str | None: # platform module. # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr("CS_GNU_LIBC_VERSION") + # Should be a string like "glibc 2.17". + version_string: str = getattr(os, "confstr")("CS_GNU_LIBC_VERSION") assert version_string is not None - _, version = version_string.split() + _, version = version_string.rsplit() except (AssertionError, AttributeError, OSError, ValueError): # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... return None return version -def _glibc_version_string_ctypes() -> str | None: +def _glibc_version_string_ctypes() -> Optional[str]: """ Fallback implementation of glibc_version_string using ctypes. """ @@ -197,12 +132,12 @@ def _glibc_version_string_ctypes() -> str | None: return version_str -def _glibc_version_string() -> str | None: +def _glibc_version_string() -> Optional[str]: """Returns glibc version string, or None if not using glibc.""" return _glibc_version_string_confstr() or _glibc_version_string_ctypes() -def _parse_glibc_version(version_str: str) -> tuple[int, int]: +def _parse_glibc_version(version_str: str) -> Tuple[int, int]: """Parse glibc version. We use a regexp instead of str.split because we want to discard any @@ -213,8 +148,8 @@ def _parse_glibc_version(version_str: str) -> tuple[int, int]: m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str) if not m: warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, + f"Expected glibc version with 2 components major.minor," + f" got: {version_str}", RuntimeWarning, ) return -1, -1 @@ -222,7 +157,7 @@ def _parse_glibc_version(version_str: str) -> tuple[int, int]: @functools.lru_cache() -def _get_glibc_version() -> tuple[int, int]: +def _get_glibc_version() -> Tuple[int, int]: version_str = _glibc_version_string() if version_str is None: return (-1, -1) @@ -267,7 +202,7 @@ _LEGACY_MANYLINUX_MAP = { def platform_tags(linux: str, arch: str) -> Iterator[str]: - if not _have_compatible_abi(arch): + if not _have_compatible_abi(sys.executable, arch): return # Oldest glibc to be supported regardless of architecture is (2, 17). too_old_glibc2 = _GLibCVersion(2, 16) diff --git a/src/wheel/vendored/packaging/_musllinux.py b/src/wheel/vendored/packaging/_musllinux.py index 7946c9b..706ba60 100644 --- a/src/wheel/vendored/packaging/_musllinux.py +++ b/src/wheel/vendored/packaging/_musllinux.py @@ -4,70 +4,13 @@ This module implements logic to detect if the currently running Python is linked against musl, and what musl version is used. """ -from __future__ import annotations - -import contextlib import functools -import operator -import os import re -import struct import subprocess import sys -from typing import IO, Iterator, NamedTuple - - -def _read_unpacked(f: IO[bytes], fmt: str) -> tuple[int, ...]: - return struct.unpack(fmt, f.read(struct.calcsize(fmt))) +from typing import Iterator, NamedTuple, Optional - -def _parse_ld_musl_from_elf(f: IO[bytes]) -> str | None: - """Detect musl libc location by parsing the Python executable. - - Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca - ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html - """ - f.seek(0) - try: - ident = _read_unpacked(f, "16B") - except struct.error: - return None - if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF. - return None - f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version. - - try: - # e_fmt: Format for program header. - # p_fmt: Format for section header. - # p_idx: Indexes to find p_type, p_offset, and p_filesz. - e_fmt, p_fmt, p_idx = { - 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit. - 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit. - }[ident[4]] - except KeyError: - return None - else: - p_get = operator.itemgetter(*p_idx) - - # Find the interpreter section and return its content. - try: - _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt) - except struct.error: - return None - for i in range(e_phnum + 1): - f.seek(e_phoff + e_phentsize * i) - try: - p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt)) - except struct.error: - return None - if p_type != 3: # Not PT_INTERP. - continue - f.seek(p_offset) - interpreter = os.fsdecode(f.read(p_filesz)).strip("\0") - if "musl" not in interpreter: - return None - return interpreter - return None +from ._elffile import ELFFile class _MuslVersion(NamedTuple): @@ -75,7 +18,7 @@ class _MuslVersion(NamedTuple): minor: int -def _parse_musl_version(output: str) -> _MuslVersion | None: +def _parse_musl_version(output: str) -> Optional[_MuslVersion]: lines = [n for n in (n.strip() for n in output.splitlines()) if n] if len(lines) < 2 or lines[0][:4] != "musl": return None @@ -86,7 +29,7 @@ def _parse_musl_version(output: str) -> _MuslVersion | None: @functools.lru_cache() -def _get_musl_version(executable: str) -> _MuslVersion | None: +def _get_musl_version(executable: str) -> Optional[_MuslVersion]: """Detect currently-running musl runtime version. This is done by checking the specified executable's dynamic linking @@ -97,15 +40,14 @@ def _get_musl_version(executable: str) -> _MuslVersion | None: Version 1.2.2 Dynamic Program Loader """ - with contextlib.ExitStack() as stack: - try: - f = stack.enter_context(open(executable, "rb")) - except OSError: - return None - ld = _parse_ld_musl_from_elf(f) - if not ld: + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: return None - proc = subprocess.run([ld], stderr=subprocess.PIPE, text=True) + proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True) return _parse_musl_version(proc.stderr) diff --git a/src/wheel/vendored/packaging/tags.py b/src/wheel/vendored/packaging/tags.py index 4e003a9..a0e1ea2 100644 --- a/src/wheel/vendored/packaging/tags.py +++ b/src/wheel/vendored/packaging/tags.py @@ -2,14 +2,24 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import annotations - import logging import platform +import subprocess import sys import sysconfig from importlib.machinery import EXTENSION_SUFFIXES -from typing import Iterable, Iterator, Sequence, Tuple, cast +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) from . import _manylinux, _musllinux @@ -18,7 +28,7 @@ logger = logging.getLogger(__name__) PythonVersion = Sequence[int] MacVersion = Tuple[int, int] -INTERPRETER_SHORT_NAMES: dict[str, str] = { +INTERPRETER_SHORT_NAMES: Dict[str, str] = { "python": "py", # Generic. "cpython": "cp", "pypy": "pp", @@ -27,7 +37,7 @@ INTERPRETER_SHORT_NAMES: dict[str, str] = { } -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 +_32_BIT_INTERPRETER = sys.maxsize <= 2**32 class Tag: @@ -84,7 +94,7 @@ class Tag: return f"<{self} @ {id(self)}>" -def parse_tag(tag: str) -> frozenset[Tag]: +def parse_tag(tag: str) -> FrozenSet[Tag]: """ Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. @@ -100,7 +110,7 @@ def parse_tag(tag: str) -> frozenset[Tag]: return frozenset(tags) -def _get_config_var(name: str, warn: bool = False) -> int | str | None: +def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: value = sysconfig.get_config_var(name) if value is None and warn: logger.debug( @@ -122,7 +132,7 @@ def _abi3_applies(python_version: PythonVersion) -> bool: return len(python_version) > 1 and tuple(python_version) >= (3, 2) -def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]: +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: py_version = tuple(py_version) # To allow for version comparison. abis = [] version = _version_nodot(py_version[:2]) @@ -159,9 +169,9 @@ def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]: def cpython_tags( - python_version: PythonVersion | None = None, - abis: Iterable[str] | None = None, - platforms: Iterable[str] | None = None, + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, *, warn: bool = False, ) -> Iterator[Tag]: @@ -222,9 +232,9 @@ def _generic_abi() -> Iterator[str]: def generic_tags( - interpreter: str | None = None, - abis: Iterable[str] | None = None, - platforms: Iterable[str] | None = None, + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, *, warn: bool = False, ) -> Iterator[Tag]: @@ -267,9 +277,9 @@ def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: def compatible_tags( - python_version: PythonVersion | None = None, - interpreter: str | None = None, - platforms: Iterable[str] | None = None, + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, ) -> Iterator[Tag]: """ Yields the sequence of tags that are compatible with a specific version of Python. @@ -301,7 +311,7 @@ def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: return "i386" -def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> list[str]: +def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: formats = [cpu_arch] if cpu_arch == "x86_64": if version < (10, 4): @@ -334,7 +344,7 @@ def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> list[str]: def mac_platforms( - version: MacVersion | None = None, arch: str | None = None + version: Optional[MacVersion] = None, arch: Optional[str] = None ) -> Iterator[str]: """ Yields the platform tags for a macOS system. @@ -347,6 +357,22 @@ def mac_platforms( version_str, _, cpu_arch = platform.mac_ver() if version is None: version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + universal_newlines=True, + ).stdout + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) else: version = version if arch is None: @@ -473,6 +499,9 @@ def sys_tags(*, warn: bool = False) -> Iterator[Tag]: yield from generic_tags() if interp_name == "pp": - yield from compatible_tags(interpreter="pp3") + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) else: - yield from compatible_tags() + interp = None + yield from compatible_tags(interpreter=interp) diff --git a/src/wheel/vendored/vendor.txt b/src/wheel/vendored/vendor.txt index df56c23..a75029c 100644 --- a/src/wheel/vendored/vendor.txt +++ b/src/wheel/vendored/vendor.txt @@ -1 +1 @@ -packaging==21.3 +packaging==22.0 |