diff --git a/news/9844.bugfix.rst b/news/9844.bugfix.rst new file mode 100644 index 00000000000..c40b286fe6a --- /dev/null +++ b/news/9844.bugfix.rst @@ -0,0 +1,2 @@ +Fix warnings about install scheme selection for Python framework builds +distributed by Apple's Command Line Tools. diff --git a/src/pip/_internal/locations/__init__.py b/src/pip/_internal/locations/__init__.py index 8eedd992661..452c232b6d4 100644 --- a/src/pip/_internal/locations/__init__.py +++ b/src/pip/_internal/locations/__init__.py @@ -11,6 +11,7 @@ USER_CACHE_DIR, get_major_minor_version, get_src_prefix, + is_osx_framework, site_packages, user_site, ) @@ -115,6 +116,19 @@ def get_scheme( if skip_pypy_special_case: continue + # sysconfig's ``osx_framework_user`` does not include ``pythonX.Y`` in + # the ``include`` value, but distutils's ``headers`` does. We'll let + # CPython decide whether this is a bug or feature. See bpo-43948. + skip_osx_framework_user_special_case = ( + user + and is_osx_framework() + and k == "headers" + and old_v.parent == new_v + and old_v.name.startswith("python") + ) + if skip_osx_framework_user_special_case: + continue + warned.append(_warn_if_mismatch(old_v, new_v, key=f"scheme.{k}")) if any(warned): diff --git a/src/pip/_internal/locations/_sysconfig.py b/src/pip/_internal/locations/_sysconfig.py index bccf9853f3a..70a5893b480 100644 --- a/src/pip/_internal/locations/_sysconfig.py +++ b/src/pip/_internal/locations/_sysconfig.py @@ -9,7 +9,7 @@ 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 +from .base import get_major_minor_version, is_osx_framework logger = logging.getLogger(__name__) @@ -30,6 +30,8 @@ def _infer_prefix() -> str: This tries: + * A special ``osx_framework_library`` for Python distributed by Apple's + Command Line Tools, when not running in a virtual environment. * Implementation + OS, used by PyPy on Windows (``pypy_nt``). * Implementation without OS, used by PyPy on POSIX (``pypy``). * OS + "prefix", used by CPython on POSIX (``posix_prefix``). @@ -37,6 +39,9 @@ def _infer_prefix() -> str: If none of the above works, fall back to ``posix_prefix``. """ + os_framework_global = is_osx_framework() and not running_under_virtualenv() + if os_framework_global and "osx_framework_library" in _AVAILABLE_SCHEMES: + return "osx_framework_library" implementation_suffixed = f"{sys.implementation.name}_{os.name}" if implementation_suffixed in _AVAILABLE_SCHEMES: return implementation_suffixed @@ -52,7 +57,10 @@ def _infer_prefix() -> str: def _infer_user() -> str: """Try to find a user scheme for the current platform.""" - suffixed = f"{os.name}_user" + if is_osx_framework() and not running_under_virtualenv(): + suffixed = "osx_framework_user" + else: + suffixed = f"{os.name}_user" if suffixed in _AVAILABLE_SCHEMES: return suffixed if "posix_user" not in _AVAILABLE_SCHEMES: # User scheme unavailable. diff --git a/src/pip/_internal/locations/base.py b/src/pip/_internal/locations/base.py index e9d59701ba4..315527f07b1 100644 --- a/src/pip/_internal/locations/base.py +++ b/src/pip/_internal/locations/base.py @@ -44,3 +44,7 @@ def get_src_prefix() -> str: user_site: typing.Optional[str] = site.getusersitepackages() except AttributeError: user_site = site.USER_SITE + + +def is_osx_framework() -> bool: + return bool(sysconfig.get_config_var("PYTHONFRAMEWORK"))