__init__.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import logging
  2. import pathlib
  3. import sys
  4. import sysconfig
  5. from typing import List, Optional
  6. from pip._internal.models.scheme import SCHEME_KEYS, Scheme
  7. from . import _distutils, _sysconfig
  8. from .base import (
  9. USER_CACHE_DIR,
  10. get_major_minor_version,
  11. get_src_prefix,
  12. site_packages,
  13. user_site,
  14. )
  15. __all__ = [
  16. "USER_CACHE_DIR",
  17. "get_bin_prefix",
  18. "get_bin_user",
  19. "get_major_minor_version",
  20. "get_platlib",
  21. "get_prefixed_libs",
  22. "get_purelib",
  23. "get_scheme",
  24. "get_src_prefix",
  25. "site_packages",
  26. "user_site",
  27. ]
  28. logger = logging.getLogger(__name__)
  29. def _default_base(*, user: bool) -> str:
  30. if user:
  31. base = sysconfig.get_config_var("userbase")
  32. else:
  33. base = sysconfig.get_config_var("base")
  34. assert base is not None
  35. return base
  36. def _warn_if_mismatch(old: pathlib.Path, new: pathlib.Path, *, key: str) -> bool:
  37. if old == new:
  38. return False
  39. issue_url = "https://github.com/pypa/pip/issues/9617"
  40. message = (
  41. "Value for %s does not match. Please report this to <%s>"
  42. "\ndistutils: %s"
  43. "\nsysconfig: %s"
  44. )
  45. logger.debug(message, key, issue_url, old, new)
  46. return True
  47. def _log_context(
  48. *,
  49. user: bool = False,
  50. home: Optional[str] = None,
  51. root: Optional[str] = None,
  52. prefix: Optional[str] = None,
  53. ) -> None:
  54. message = (
  55. "Additional context:" "\nuser = %r" "\nhome = %r" "\nroot = %r" "\nprefix = %r"
  56. )
  57. logger.debug(message, user, home, root, prefix)
  58. def get_scheme(
  59. dist_name, # type: str
  60. user=False, # type: bool
  61. home=None, # type: Optional[str]
  62. root=None, # type: Optional[str]
  63. isolated=False, # type: bool
  64. prefix=None, # type: Optional[str]
  65. ):
  66. # type: (...) -> Scheme
  67. old = _distutils.get_scheme(
  68. dist_name,
  69. user=user,
  70. home=home,
  71. root=root,
  72. isolated=isolated,
  73. prefix=prefix,
  74. )
  75. new = _sysconfig.get_scheme(
  76. dist_name,
  77. user=user,
  78. home=home,
  79. root=root,
  80. isolated=isolated,
  81. prefix=prefix,
  82. )
  83. base = prefix or home or _default_base(user=user)
  84. warned = []
  85. for k in SCHEME_KEYS:
  86. # Extra join because distutils can return relative paths.
  87. old_v = pathlib.Path(base, getattr(old, k))
  88. new_v = pathlib.Path(getattr(new, k))
  89. # distutils incorrectly put PyPy packages under ``site-packages/python``
  90. # in the ``posix_home`` scheme, but PyPy devs said they expect the
  91. # directory name to be ``pypy`` instead. So we treat this as a bug fix
  92. # and not warn about it. See bpo-43307 and python/cpython#24628.
  93. skip_pypy_special_case = (
  94. sys.implementation.name == "pypy"
  95. and home is not None
  96. and k in ("platlib", "purelib")
  97. and old_v.parent == new_v.parent
  98. and old_v.name == "python"
  99. and new_v.name == "pypy"
  100. )
  101. if skip_pypy_special_case:
  102. continue
  103. warned.append(_warn_if_mismatch(old_v, new_v, key=f"scheme.{k}"))
  104. if any(warned):
  105. _log_context(user=user, home=home, root=root, prefix=prefix)
  106. return old
  107. def get_bin_prefix():
  108. # type: () -> str
  109. old = _distutils.get_bin_prefix()
  110. new = _sysconfig.get_bin_prefix()
  111. if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="bin_prefix"):
  112. _log_context()
  113. return old
  114. def get_bin_user():
  115. # type: () -> str
  116. return _sysconfig.get_scheme("", user=True).scripts
  117. def get_purelib():
  118. # type: () -> str
  119. """Return the default pure-Python lib location."""
  120. old = _distutils.get_purelib()
  121. new = _sysconfig.get_purelib()
  122. if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="purelib"):
  123. _log_context()
  124. return old
  125. def get_platlib():
  126. # type: () -> str
  127. """Return the default platform-shared lib location."""
  128. old = _distutils.get_platlib()
  129. new = _sysconfig.get_platlib()
  130. if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="platlib"):
  131. _log_context()
  132. return old
  133. def get_prefixed_libs(prefix):
  134. # type: (str) -> List[str]
  135. """Return the lib locations under ``prefix``."""
  136. old_pure, old_plat = _distutils.get_prefixed_libs(prefix)
  137. new_pure, new_plat = _sysconfig.get_prefixed_libs(prefix)
  138. warned = [
  139. _warn_if_mismatch(
  140. pathlib.Path(old_pure),
  141. pathlib.Path(new_pure),
  142. key="prefixed-purelib",
  143. ),
  144. _warn_if_mismatch(
  145. pathlib.Path(old_plat),
  146. pathlib.Path(new_plat),
  147. key="prefixed-platlib",
  148. ),
  149. ]
  150. if any(warned):
  151. _log_context(prefix=prefix)
  152. if old_pure == old_plat:
  153. return [old_pure]
  154. return [old_pure, old_plat]