|
4 | 4 | import pathlib
|
5 | 5 | import sys
|
6 | 6 | import sysconfig
|
7 |
| -from typing import List, Optional |
| 7 | +from typing import Dict, Iterator, List, Optional, Tuple |
8 | 8 |
|
9 | 9 | from pip._internal.models.scheme import SCHEME_KEYS, Scheme
|
| 10 | +from pip._internal.utils.compat import WINDOWS |
10 | 11 |
|
11 | 12 | from . import _distutils, _sysconfig
|
12 | 13 | from .base import (
|
|
41 | 42 | _MISMATCH_LEVEL = logging.WARNING
|
42 | 43 |
|
43 | 44 |
|
| 45 | +def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool: |
| 46 | + platlib = scheme["platlib"] |
| 47 | + if "/lib64/" not in platlib: |
| 48 | + return False |
| 49 | + unpatched = platlib.replace("/lib64/", "/lib/") |
| 50 | + return unpatched.replace("$platbase/", "$base/") == scheme["purelib"] |
| 51 | + |
| 52 | + |
44 | 53 | @functools.lru_cache(maxsize=None)
|
45 | 54 | def _looks_like_red_hat_patched() -> bool:
|
46 | 55 | """Red Hat patches platlib in unix_prefix and unix_home, but not purelib.
|
47 | 56 |
|
48 | 57 | This is the only way I can see to tell a Red Hat-patched Python.
|
49 | 58 | """
|
50 |
| - from distutils.command.install import INSTALL_SCHEMES as SCHEMES |
| 59 | + from distutils.command.install import INSTALL_SCHEMES # type: ignore |
51 | 60 |
|
52 |
| - return ( |
53 |
| - k in SCHEMES |
54 |
| - and "lib64" in SCHEMES[k]["platlib"] |
55 |
| - and SCHEMES[k]["platlib"].replace("lib64", "lib") == SCHEMES[k]["purelib"] |
| 61 | + return all( |
| 62 | + k in INSTALL_SCHEMES |
| 63 | + and _looks_like_red_hat_patched_platlib_purelib(INSTALL_SCHEMES[k]) |
56 | 64 | for k in ("unix_prefix", "unix_home")
|
57 | 65 | )
|
58 | 66 |
|
59 | 67 |
|
60 | 68 | @functools.lru_cache(maxsize=None)
|
61 | 69 | def _looks_like_debian_patched() -> bool:
|
62 | 70 | """Debian adds two additional schemes."""
|
63 |
| - from distutils.command.install import INSTALL_SCHEMES |
| 71 | + from distutils.command.install import INSTALL_SCHEMES # type: ignore |
64 | 72 |
|
65 | 73 | return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES
|
66 | 74 |
|
67 | 75 |
|
| 76 | +def _fix_abiflags(parts: Tuple[str]) -> Iterator[str]: |
| 77 | + ldversion = sysconfig.get_config_var("LDVERSION") |
| 78 | + abiflags: str = getattr(sys, "abiflags", None) |
| 79 | + |
| 80 | + # LDVERSION does not end with sys.abiflags. Just return the path unchanged. |
| 81 | + if not ldversion or not abiflags or not ldversion.endswith(abiflags): |
| 82 | + yield from parts |
| 83 | + return |
| 84 | + |
| 85 | + # Strip sys.abiflags from LDVERSION-based path components. |
| 86 | + for part in parts: |
| 87 | + if part.endswith(ldversion): |
| 88 | + part = part[: (0 - len(abiflags))] |
| 89 | + yield part |
| 90 | + |
| 91 | + |
68 | 92 | def _default_base(*, user: bool) -> str:
|
69 | 93 | if user:
|
70 | 94 | base = sysconfig.get_config_var("userbase")
|
@@ -177,14 +201,26 @@ def get_scheme(
|
177 | 201 | # instead of site-packages, but the /usr/local check should cover it.
|
178 | 202 | skip_linux_system_special_case = (
|
179 | 203 | not (user or home or prefix)
|
180 |
| - and old_v.parts[1:3] == ("user", "local") |
| 204 | + and old_v.parts[1:3] == ("usr", "local") |
| 205 | + and len(new_v.parts) > 1 |
181 | 206 | and new_v.parts[1] == "usr"
|
182 |
| - and new_v.parts[2] != "local" |
| 207 | + and (len(new_v.parts) < 3 or new_v.parts[2] != "local") |
183 | 208 | and (_looks_like_red_hat_patched() or _looks_like_debian_patched())
|
184 | 209 | )
|
185 | 210 | if skip_linux_system_special_case:
|
186 | 211 | continue
|
187 | 212 |
|
| 213 | + # On Python 3.7 and earlier, sysconfig does not include sys.abiflags in |
| 214 | + # the "pythonX.Y" part of the path, but distutils does. |
| 215 | + skip_sysconfig_abiflag_bug = ( |
| 216 | + sys.version_info < (3, 8) |
| 217 | + and not WINDOWS |
| 218 | + and k in ("headers", "platlib", "purelib") |
| 219 | + and tuple(_fix_abiflags(old_v.parts)) == new_v.parts |
| 220 | + ) |
| 221 | + if skip_sysconfig_abiflag_bug: |
| 222 | + continue |
| 223 | + |
188 | 224 | warned.append(_warn_if_mismatch(old_v, new_v, key=f"scheme.{k}"))
|
189 | 225 |
|
190 | 226 | if any(warned):
|
|
0 commit comments