Change venv
This commit is contained in:
@@ -4,14 +4,14 @@ import os
|
||||
import pathlib
|
||||
import sys
|
||||
import sysconfig
|
||||
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
||||
from typing import Any, Dict, Generator, Optional, Tuple
|
||||
|
||||
from pip._internal.models.scheme import SCHEME_KEYS, Scheme
|
||||
from pip._internal.utils.compat import WINDOWS
|
||||
from pip._internal.utils.deprecation import deprecated
|
||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
|
||||
from . import _distutils, _sysconfig
|
||||
from . import _sysconfig
|
||||
from .base import (
|
||||
USER_CACHE_DIR,
|
||||
get_major_minor_version,
|
||||
@@ -27,7 +27,6 @@ __all__ = [
|
||||
"get_bin_user",
|
||||
"get_major_minor_version",
|
||||
"get_platlib",
|
||||
"get_prefixed_libs",
|
||||
"get_purelib",
|
||||
"get_scheme",
|
||||
"get_src_prefix",
|
||||
@@ -38,20 +37,48 @@ __all__ = [
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if os.environ.get("_PIP_LOCATIONS_NO_WARN_ON_MISMATCH"):
|
||||
_MISMATCH_LEVEL = logging.DEBUG
|
||||
else:
|
||||
_MISMATCH_LEVEL = logging.WARNING
|
||||
|
||||
_PLATLIBDIR: str = getattr(sys, "platlibdir", "lib")
|
||||
|
||||
_USE_SYSCONFIG_DEFAULT = sys.version_info >= (3, 10)
|
||||
|
||||
|
||||
def _should_use_sysconfig() -> bool:
|
||||
"""This function determines the value of _USE_SYSCONFIG.
|
||||
|
||||
By default, pip uses sysconfig on Python 3.10+.
|
||||
But Python distributors can override this decision by setting:
|
||||
sysconfig._PIP_USE_SYSCONFIG = True / False
|
||||
Rationale in https://github.com/pypa/pip/issues/10647
|
||||
|
||||
This is a function for testability, but should be constant during any one
|
||||
run.
|
||||
"""
|
||||
return bool(getattr(sysconfig, "_PIP_USE_SYSCONFIG", _USE_SYSCONFIG_DEFAULT))
|
||||
|
||||
|
||||
_USE_SYSCONFIG = _should_use_sysconfig()
|
||||
|
||||
if not _USE_SYSCONFIG:
|
||||
# Import distutils lazily to avoid deprecation warnings,
|
||||
# but import it soon enough that it is in memory and available during
|
||||
# a pip reinstall.
|
||||
from . import _distutils
|
||||
|
||||
# Be noisy about incompatibilities if this platforms "should" be using
|
||||
# sysconfig, but is explicitly opting out and using distutils instead.
|
||||
if _USE_SYSCONFIG_DEFAULT and not _USE_SYSCONFIG:
|
||||
_MISMATCH_LEVEL = logging.WARNING
|
||||
else:
|
||||
_MISMATCH_LEVEL = logging.DEBUG
|
||||
|
||||
|
||||
def _looks_like_bpo_44860() -> bool:
|
||||
"""The resolution to bpo-44860 will change this incorrect platlib.
|
||||
|
||||
See <https://bugs.python.org/issue44860>.
|
||||
"""
|
||||
from distutils.command.install import INSTALL_SCHEMES # type: ignore
|
||||
from distutils.command.install import INSTALL_SCHEMES
|
||||
|
||||
try:
|
||||
unix_user_platlib = INSTALL_SCHEMES["unix_user"]["platlib"]
|
||||
@@ -62,6 +89,8 @@ def _looks_like_bpo_44860() -> bool:
|
||||
|
||||
def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool:
|
||||
platlib = scheme["platlib"]
|
||||
if "/$platlibdir/" in platlib:
|
||||
platlib = platlib.replace("/$platlibdir/", f"/{_PLATLIBDIR}/")
|
||||
if "/lib64/" not in platlib:
|
||||
return False
|
||||
unpatched = platlib.replace("/lib64/", "/lib/")
|
||||
@@ -74,7 +103,7 @@ def _looks_like_red_hat_lib() -> bool:
|
||||
|
||||
This is the only way I can see to tell a Red Hat-patched Python.
|
||||
"""
|
||||
from distutils.command.install import INSTALL_SCHEMES # type: ignore
|
||||
from distutils.command.install import INSTALL_SCHEMES
|
||||
|
||||
return all(
|
||||
k in INSTALL_SCHEMES
|
||||
@@ -86,7 +115,7 @@ def _looks_like_red_hat_lib() -> bool:
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def _looks_like_debian_scheme() -> bool:
|
||||
"""Debian adds two additional schemes."""
|
||||
from distutils.command.install import INSTALL_SCHEMES # type: ignore
|
||||
from distutils.command.install import INSTALL_SCHEMES
|
||||
|
||||
return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES
|
||||
|
||||
@@ -111,6 +140,22 @@ def _looks_like_red_hat_scheme() -> bool:
|
||||
)
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def _looks_like_slackware_scheme() -> bool:
|
||||
"""Slackware patches sysconfig but fails to patch distutils and site.
|
||||
|
||||
Slackware changes sysconfig's user scheme to use ``"lib64"`` for the lib
|
||||
path, but does not do the same to the site module.
|
||||
"""
|
||||
if user_site is None: # User-site not available.
|
||||
return False
|
||||
try:
|
||||
paths = sysconfig.get_paths(scheme="posix_user", expand=False)
|
||||
except KeyError: # User-site not available.
|
||||
return False
|
||||
return "/lib64/" in paths["purelib"] and "/lib64/" not in user_site
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def _looks_like_msys2_mingw_scheme() -> bool:
|
||||
"""MSYS2 patches distutils and sysconfig to use a UNIX-like scheme.
|
||||
@@ -129,9 +174,9 @@ def _looks_like_msys2_mingw_scheme() -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _fix_abiflags(parts: Tuple[str]) -> Iterator[str]:
|
||||
def _fix_abiflags(parts: Tuple[str]) -> Generator[str, None, None]:
|
||||
ldversion = sysconfig.get_config_var("LDVERSION")
|
||||
abiflags: str = getattr(sys, "abiflags", None)
|
||||
abiflags = getattr(sys, "abiflags", None)
|
||||
|
||||
# LDVERSION does not end with sys.abiflags. Just return the path unchanged.
|
||||
if not ldversion or not abiflags or not ldversion.endswith(abiflags):
|
||||
@@ -190,7 +235,7 @@ def get_scheme(
|
||||
isolated: bool = False,
|
||||
prefix: Optional[str] = None,
|
||||
) -> Scheme:
|
||||
old = _distutils.get_scheme(
|
||||
new = _sysconfig.get_scheme(
|
||||
dist_name,
|
||||
user=user,
|
||||
home=home,
|
||||
@@ -198,7 +243,10 @@ def get_scheme(
|
||||
isolated=isolated,
|
||||
prefix=prefix,
|
||||
)
|
||||
new = _sysconfig.get_scheme(
|
||||
if _USE_SYSCONFIG:
|
||||
return new
|
||||
|
||||
old = _distutils.get_scheme(
|
||||
dist_name,
|
||||
user=user,
|
||||
home=home,
|
||||
@@ -263,6 +311,17 @@ def get_scheme(
|
||||
if skip_bpo_44860:
|
||||
continue
|
||||
|
||||
# Slackware incorrectly patches posix_user to use lib64 instead of lib,
|
||||
# but not usersite to match the location.
|
||||
skip_slackware_user_scheme = (
|
||||
user
|
||||
and k in ("platlib", "purelib")
|
||||
and not WINDOWS
|
||||
and _looks_like_slackware_scheme()
|
||||
)
|
||||
if skip_slackware_user_scheme:
|
||||
continue
|
||||
|
||||
# Both Debian and Red Hat patch Python to place the system site under
|
||||
# /usr/local instead of /usr. Debian also places lib in dist-packages
|
||||
# instead of site-packages, but the /usr/local check should cover it.
|
||||
@@ -296,6 +355,18 @@ def get_scheme(
|
||||
if skip_msys2_mingw_bug:
|
||||
continue
|
||||
|
||||
# CPython's POSIX install script invokes pip (via ensurepip) against the
|
||||
# interpreter located in the source tree, not the install site. This
|
||||
# triggers special logic in sysconfig that's not present in distutils.
|
||||
# https://github.com/python/cpython/blob/8c21941ddaf/Lib/sysconfig.py#L178-L194
|
||||
skip_cpython_build = (
|
||||
sysconfig.is_python_build(check_home=True)
|
||||
and not WINDOWS
|
||||
and k in ("headers", "include", "platinclude")
|
||||
)
|
||||
if skip_cpython_build:
|
||||
continue
|
||||
|
||||
warning_contexts.append((old_v, new_v, f"scheme.{k}"))
|
||||
|
||||
if not warning_contexts:
|
||||
@@ -315,10 +386,12 @@ def get_scheme(
|
||||
)
|
||||
if any(default_old[k] != getattr(old, k) for k in SCHEME_KEYS):
|
||||
deprecated(
|
||||
"Configuring installation scheme with distutils config files "
|
||||
"is deprecated and will no longer work in the near future. If you "
|
||||
"are using a Homebrew or Linuxbrew Python, please see discussion "
|
||||
"at https://github.com/Homebrew/homebrew-core/issues/76621",
|
||||
reason=(
|
||||
"Configuring installation scheme with distutils config files "
|
||||
"is deprecated and will no longer work in the near future. If you "
|
||||
"are using a Homebrew or Linuxbrew Python, please see discussion "
|
||||
"at https://github.com/Homebrew/homebrew-core/issues/76621"
|
||||
),
|
||||
replacement=None,
|
||||
gone_in=None,
|
||||
)
|
||||
@@ -333,8 +406,11 @@ def get_scheme(
|
||||
|
||||
|
||||
def get_bin_prefix() -> str:
|
||||
old = _distutils.get_bin_prefix()
|
||||
new = _sysconfig.get_bin_prefix()
|
||||
if _USE_SYSCONFIG:
|
||||
return new
|
||||
|
||||
old = _distutils.get_bin_prefix()
|
||||
if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="bin_prefix"):
|
||||
_log_context()
|
||||
return old
|
||||
@@ -363,8 +439,11 @@ def _looks_like_deb_system_dist_packages(value: str) -> bool:
|
||||
|
||||
def get_purelib() -> str:
|
||||
"""Return the default pure-Python lib location."""
|
||||
old = _distutils.get_purelib()
|
||||
new = _sysconfig.get_purelib()
|
||||
if _USE_SYSCONFIG:
|
||||
return new
|
||||
|
||||
old = _distutils.get_purelib()
|
||||
if _looks_like_deb_system_dist_packages(old):
|
||||
return old
|
||||
if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="purelib"):
|
||||
@@ -374,35 +453,15 @@ def get_purelib() -> str:
|
||||
|
||||
def get_platlib() -> str:
|
||||
"""Return the default platform-shared lib location."""
|
||||
old = _distutils.get_platlib()
|
||||
new = _sysconfig.get_platlib()
|
||||
if _USE_SYSCONFIG:
|
||||
return new
|
||||
|
||||
from . import _distutils
|
||||
|
||||
old = _distutils.get_platlib()
|
||||
if _looks_like_deb_system_dist_packages(old):
|
||||
return old
|
||||
if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="platlib"):
|
||||
_log_context()
|
||||
return old
|
||||
|
||||
|
||||
def get_prefixed_libs(prefix: str) -> List[str]:
|
||||
"""Return the lib locations under ``prefix``."""
|
||||
old_pure, old_plat = _distutils.get_prefixed_libs(prefix)
|
||||
new_pure, new_plat = _sysconfig.get_prefixed_libs(prefix)
|
||||
|
||||
warned = [
|
||||
_warn_if_mismatch(
|
||||
pathlib.Path(old_pure),
|
||||
pathlib.Path(new_pure),
|
||||
key="prefixed-purelib",
|
||||
),
|
||||
_warn_if_mismatch(
|
||||
pathlib.Path(old_plat),
|
||||
pathlib.Path(new_plat),
|
||||
key="prefixed-platlib",
|
||||
),
|
||||
]
|
||||
if any(warned):
|
||||
_log_context(prefix=prefix)
|
||||
|
||||
if old_pure == old_plat:
|
||||
return [old_pure]
|
||||
return [old_pure, old_plat]
|
||||
|
@@ -3,6 +3,17 @@
|
||||
# The following comment should be removed at some point in the future.
|
||||
# mypy: strict-optional=False
|
||||
|
||||
# If pip's going to use distutils, it should not be using the copy that setuptools
|
||||
# might have injected into the environment. This is done by removing the injected
|
||||
# shim, if it's injected.
|
||||
#
|
||||
# See https://github.com/pypa/pip/issues/8761 for the original discussion and
|
||||
# rationale for why this is done within pip.
|
||||
try:
|
||||
__import__("_distutils_hack").remove_shim()
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
@@ -10,7 +21,7 @@ from distutils.cmd import Command as DistutilsCommand
|
||||
from distutils.command.install import SCHEME_KEYS
|
||||
from distutils.command.install import install as distutils_install_command
|
||||
from distutils.sysconfig import get_python_lib
|
||||
from typing import Dict, List, Optional, Tuple, Union, cast
|
||||
from typing import Dict, List, Optional, Union, cast
|
||||
|
||||
from pip._internal.models.scheme import Scheme
|
||||
from pip._internal.utils.compat import WINDOWS
|
||||
@@ -24,10 +35,10 @@ logger = logging.getLogger(__name__)
|
||||
def distutils_scheme(
|
||||
dist_name: str,
|
||||
user: bool = False,
|
||||
home: str = None,
|
||||
root: str = None,
|
||||
home: Optional[str] = None,
|
||||
root: Optional[str] = None,
|
||||
isolated: bool = False,
|
||||
prefix: str = None,
|
||||
prefix: Optional[str] = None,
|
||||
*,
|
||||
ignore_config_files: bool = False,
|
||||
) -> Dict[str, str]:
|
||||
@@ -84,7 +95,7 @@ def distutils_scheme(
|
||||
if home:
|
||||
prefix = home
|
||||
elif user:
|
||||
prefix = i.install_userbase # type: ignore
|
||||
prefix = i.install_userbase
|
||||
else:
|
||||
prefix = i.prefix
|
||||
scheme["headers"] = os.path.join(
|
||||
@@ -160,10 +171,3 @@ def get_purelib() -> str:
|
||||
|
||||
def get_platlib() -> str:
|
||||
return get_python_lib(plat_specific=True)
|
||||
|
||||
|
||||
def get_prefixed_libs(prefix: str) -> Tuple[str, str]:
|
||||
return (
|
||||
get_python_lib(plat_specific=False, prefix=prefix),
|
||||
get_python_lib(plat_specific=True, prefix=prefix),
|
||||
)
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import distutils.util # FIXME: For change_root.
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
@@ -9,7 +8,7 @@ from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationI
|
||||
from pip._internal.models.scheme import SCHEME_KEYS, Scheme
|
||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
|
||||
from .base import get_major_minor_version, is_osx_framework
|
||||
from .base import change_root, get_major_minor_version, is_osx_framework
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -194,7 +193,7 @@ def get_scheme(
|
||||
)
|
||||
if root is not None:
|
||||
for key in SCHEME_KEYS:
|
||||
value = distutils.util.change_root(root, getattr(scheme, key))
|
||||
value = change_root(root, getattr(scheme, key))
|
||||
setattr(scheme, key, value)
|
||||
return scheme
|
||||
|
||||
@@ -212,8 +211,3 @@ def get_purelib() -> str:
|
||||
|
||||
def get_platlib() -> str:
|
||||
return sysconfig.get_paths()["platlib"]
|
||||
|
||||
|
||||
def get_prefixed_libs(prefix: str) -> typing.Tuple[str, str]:
|
||||
paths = sysconfig.get_paths(vars={"base": prefix, "platbase": prefix})
|
||||
return (paths["purelib"], paths["platlib"])
|
||||
|
@@ -5,6 +5,7 @@ import sys
|
||||
import sysconfig
|
||||
import typing
|
||||
|
||||
from pip._internal.exceptions import InstallationError
|
||||
from pip._internal.utils import appdirs
|
||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
|
||||
@@ -12,7 +13,7 @@ from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
USER_CACHE_DIR = appdirs.user_cache_dir("pip")
|
||||
|
||||
# FIXME doesn't account for venv linked to global site-packages
|
||||
site_packages: typing.Optional[str] = sysconfig.get_path("purelib")
|
||||
site_packages: str = sysconfig.get_path("purelib")
|
||||
|
||||
|
||||
def get_major_minor_version() -> str:
|
||||
@@ -23,6 +24,34 @@ def get_major_minor_version() -> str:
|
||||
return "{}.{}".format(*sys.version_info)
|
||||
|
||||
|
||||
def change_root(new_root: str, pathname: str) -> str:
|
||||
"""Return 'pathname' with 'new_root' prepended.
|
||||
|
||||
If 'pathname' is relative, this is equivalent to os.path.join(new_root, pathname).
|
||||
Otherwise, it requires making 'pathname' relative and then joining the
|
||||
two, which is tricky on DOS/Windows and Mac OS.
|
||||
|
||||
This is borrowed from Python's standard library's distutils module.
|
||||
"""
|
||||
if os.name == "posix":
|
||||
if not os.path.isabs(pathname):
|
||||
return os.path.join(new_root, pathname)
|
||||
else:
|
||||
return os.path.join(new_root, pathname[1:])
|
||||
|
||||
elif os.name == "nt":
|
||||
(drive, path) = os.path.splitdrive(pathname)
|
||||
if path[0] == "\\":
|
||||
path = path[1:]
|
||||
return os.path.join(new_root, path)
|
||||
|
||||
else:
|
||||
raise InstallationError(
|
||||
f"Unknown platform: {os.name}\n"
|
||||
"Can not change root path prefix on unknown platform."
|
||||
)
|
||||
|
||||
|
||||
def get_src_prefix() -> str:
|
||||
if running_under_virtualenv():
|
||||
src_prefix = os.path.join(sys.prefix, "src")
|
||||
|
Reference in New Issue
Block a user