Skip to content

Commit 310c9ad

Browse files
authored
Merge pull request #10217 from uranusjr/locations-linux-system
2 parents 06f8cfb + 92921f9 commit 310c9ad

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

src/pip/_internal/locations/__init__.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
import pathlib
55
import sys
66
import sysconfig
7-
from typing import List, Optional
7+
from typing import Dict, Iterator, List, Optional, Tuple
88

99
from pip._internal.models.scheme import SCHEME_KEYS, Scheme
10+
from pip._internal.utils.compat import WINDOWS
1011

1112
from . import _distutils, _sysconfig
1213
from .base import (
@@ -41,6 +42,53 @@
4142
_MISMATCH_LEVEL = logging.WARNING
4243

4344

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+
53+
@functools.lru_cache(maxsize=None)
54+
def _looks_like_red_hat_patched() -> bool:
55+
"""Red Hat patches platlib in unix_prefix and unix_home, but not purelib.
56+
57+
This is the only way I can see to tell a Red Hat-patched Python.
58+
"""
59+
from distutils.command.install import INSTALL_SCHEMES # type: ignore
60+
61+
return all(
62+
k in INSTALL_SCHEMES
63+
and _looks_like_red_hat_patched_platlib_purelib(INSTALL_SCHEMES[k])
64+
for k in ("unix_prefix", "unix_home")
65+
)
66+
67+
68+
@functools.lru_cache(maxsize=None)
69+
def _looks_like_debian_patched() -> bool:
70+
"""Debian adds two additional schemes."""
71+
from distutils.command.install import INSTALL_SCHEMES # type: ignore
72+
73+
return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES
74+
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+
4492
def _default_base(*, user: bool) -> str:
4593
if user:
4694
base = sysconfig.get_config_var("userbase")
@@ -143,6 +191,36 @@ def get_scheme(
143191
if skip_osx_framework_user_special_case:
144192
continue
145193

194+
# On Red Hat and derived Linux distributions, distutils is patched to
195+
# use "lib64" instead of "lib" for platlib.
196+
if k == "platlib" and _looks_like_red_hat_patched():
197+
continue
198+
199+
# Both Debian and Red Hat patch Python to place the system site under
200+
# /usr/local instead of /usr. Debian also places lib in dist-packages
201+
# instead of site-packages, but the /usr/local check should cover it.
202+
skip_linux_system_special_case = (
203+
not (user or home or prefix)
204+
and old_v.parts[1:3] == ("usr", "local")
205+
and len(new_v.parts) > 1
206+
and new_v.parts[1] == "usr"
207+
and (len(new_v.parts) < 3 or new_v.parts[2] != "local")
208+
and (_looks_like_red_hat_patched() or _looks_like_debian_patched())
209+
)
210+
if skip_linux_system_special_case:
211+
continue
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+
146224
warned.append(_warn_if_mismatch(old_v, new_v, key=f"scheme.{k}"))
147225

148226
if any(warned):

src/pip/_internal/locations/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import functools
12
import os
23
import site
34
import sys
@@ -46,5 +47,6 @@ def get_src_prefix() -> str:
4647
user_site = site.USER_SITE
4748

4849

50+
@functools.lru_cache(maxsize=None)
4951
def is_osx_framework() -> bool:
5052
return bool(sysconfig.get_config_var("PYTHONFRAMEWORK"))

0 commit comments

Comments
 (0)