locations.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. """Locations where we look for configs, install stuff, etc"""
  2. from __future__ import absolute_import
  3. import getpass
  4. import os
  5. import os.path
  6. import site
  7. import sys
  8. import tempfile
  9. from distutils import sysconfig
  10. from distutils.command.install import install, SCHEME_KEYS
  11. from pip.compat import get_path_uid, WINDOWS
  12. from pip.utils import appdirs
  13. from pip import exceptions
  14. # Hack for flake8
  15. install
  16. # CA Bundle Locations
  17. CA_BUNDLE_PATHS = [
  18. # Debian/Ubuntu/Gentoo etc.
  19. "/etc/ssl/certs/ca-certificates.crt",
  20. # Fedora/RHEL
  21. "/etc/pki/tls/certs/ca-bundle.crt",
  22. # OpenSUSE
  23. "/etc/ssl/ca-bundle.pem",
  24. # OpenBSD
  25. "/etc/ssl/cert.pem",
  26. # FreeBSD/DragonFly
  27. "/usr/local/share/certs/ca-root-nss.crt",
  28. # Homebrew on OSX
  29. "/usr/local/etc/openssl/cert.pem",
  30. ]
  31. # Attempt to locate a CA Bundle that we can pass into requests, we have a list
  32. # of possible ones from various systems. If we cannot find one then we'll set
  33. # this to None so that we default to whatever requests is setup to handle.
  34. #
  35. # Note to Downstream: If you wish to disable this autodetection and simply use
  36. # whatever requests does (likely you've already patched
  37. # requests.certs.where()) then simply edit this line so
  38. # that it reads ``CA_BUNDLE_PATH = None``.
  39. CA_BUNDLE_PATH = next((x for x in CA_BUNDLE_PATHS if os.path.exists(x)), None)
  40. # Application Directories
  41. USER_CACHE_DIR = appdirs.user_cache_dir("pip")
  42. DELETE_MARKER_MESSAGE = '''\
  43. This file is placed here by pip to indicate the source was put
  44. here by pip.
  45. Once this package is successfully installed this source code will be
  46. deleted (unless you remove this file).
  47. '''
  48. PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt'
  49. def write_delete_marker_file(directory):
  50. """
  51. Write the pip delete marker file into this directory.
  52. """
  53. filepath = os.path.join(directory, PIP_DELETE_MARKER_FILENAME)
  54. with open(filepath, 'w') as marker_fp:
  55. marker_fp.write(DELETE_MARKER_MESSAGE)
  56. def running_under_virtualenv():
  57. """
  58. Return True if we're running inside a virtualenv, False otherwise.
  59. """
  60. if hasattr(sys, 'real_prefix'):
  61. return True
  62. elif sys.prefix != getattr(sys, "base_prefix", sys.prefix):
  63. return True
  64. return False
  65. def virtualenv_no_global():
  66. """
  67. Return True if in a venv and no system site packages.
  68. """
  69. # this mirrors the logic in virtualenv.py for locating the
  70. # no-global-site-packages.txt file
  71. site_mod_dir = os.path.dirname(os.path.abspath(site.__file__))
  72. no_global_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt')
  73. if running_under_virtualenv() and os.path.isfile(no_global_file):
  74. return True
  75. def __get_username():
  76. """ Returns the effective username of the current process. """
  77. if WINDOWS:
  78. return getpass.getuser()
  79. import pwd
  80. return pwd.getpwuid(os.geteuid()).pw_name
  81. def _get_build_prefix():
  82. """ Returns a safe build_prefix """
  83. path = os.path.join(
  84. tempfile.gettempdir(),
  85. 'pip_build_%s' % __get_username().replace(' ', '_')
  86. )
  87. if WINDOWS:
  88. """ on windows(tested on 7) temp dirs are isolated """
  89. return path
  90. try:
  91. os.mkdir(path)
  92. write_delete_marker_file(path)
  93. except OSError:
  94. file_uid = None
  95. try:
  96. # raises OSError for symlinks
  97. # https://github.com/pypa/pip/pull/935#discussion_r5307003
  98. file_uid = get_path_uid(path)
  99. except OSError:
  100. file_uid = None
  101. if file_uid != os.geteuid():
  102. msg = (
  103. "The temporary folder for building (%s) is either not owned by"
  104. " you, or is a symlink." % path
  105. )
  106. print(msg)
  107. print(
  108. "pip will not work until the temporary folder is either "
  109. "deleted or is a real directory owned by your user account."
  110. )
  111. raise exceptions.InstallationError(msg)
  112. return path
  113. if running_under_virtualenv():
  114. build_prefix = os.path.join(sys.prefix, 'build')
  115. src_prefix = os.path.join(sys.prefix, 'src')
  116. else:
  117. # Note: intentionally NOT using mkdtemp
  118. # See https://github.com/pypa/pip/issues/906 for plan to move to mkdtemp
  119. build_prefix = _get_build_prefix()
  120. # FIXME: keep src in cwd for now (it is not a temporary folder)
  121. try:
  122. src_prefix = os.path.join(os.getcwd(), 'src')
  123. except OSError:
  124. # In case the current working directory has been renamed or deleted
  125. sys.exit(
  126. "The folder you are executing pip from can no longer be found."
  127. )
  128. # under Mac OS X + virtualenv sys.prefix is not properly resolved
  129. # it is something like /path/to/python/bin/..
  130. # Note: using realpath due to tmp dirs on OSX being symlinks
  131. build_prefix = os.path.abspath(os.path.realpath(build_prefix))
  132. src_prefix = os.path.abspath(src_prefix)
  133. # FIXME doesn't account for venv linked to global site-packages
  134. site_packages = sysconfig.get_python_lib()
  135. user_site = site.USER_SITE
  136. user_dir = os.path.expanduser('~')
  137. if WINDOWS:
  138. bin_py = os.path.join(sys.prefix, 'Scripts')
  139. bin_user = os.path.join(user_site, 'Scripts')
  140. # buildout uses 'bin' on Windows too?
  141. if not os.path.exists(bin_py):
  142. bin_py = os.path.join(sys.prefix, 'bin')
  143. bin_user = os.path.join(user_site, 'bin')
  144. config_basename = 'pip.ini'
  145. legacy_storage_dir = os.path.join(user_dir, 'pip')
  146. legacy_config_file = os.path.join(
  147. legacy_storage_dir,
  148. config_basename,
  149. )
  150. else:
  151. bin_py = os.path.join(sys.prefix, 'bin')
  152. bin_user = os.path.join(user_site, 'bin')
  153. config_basename = 'pip.conf'
  154. legacy_storage_dir = os.path.join(user_dir, '.pip')
  155. legacy_config_file = os.path.join(
  156. legacy_storage_dir,
  157. config_basename,
  158. )
  159. # Forcing to use /usr/local/bin for standard Mac OS X framework installs
  160. # Also log to ~/Library/Logs/ for use with the Console.app log viewer
  161. if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/':
  162. bin_py = '/usr/local/bin'
  163. site_config_files = [
  164. os.path.join(path, config_basename)
  165. for path in appdirs.site_config_dirs('pip')
  166. ]
  167. def distutils_scheme(dist_name, user=False, home=None, root=None,
  168. isolated=False):
  169. """
  170. Return a distutils install scheme
  171. """
  172. from distutils.dist import Distribution
  173. scheme = {}
  174. if isolated:
  175. extra_dist_args = {"script_args": ["--no-user-cfg"]}
  176. else:
  177. extra_dist_args = {}
  178. dist_args = {'name': dist_name}
  179. dist_args.update(extra_dist_args)
  180. d = Distribution(dist_args)
  181. d.parse_config_files()
  182. i = d.get_command_obj('install', create=True)
  183. # NOTE: setting user or home has the side-effect of creating the home dir
  184. # or user base for installations during finalize_options()
  185. # ideally, we'd prefer a scheme class that has no side-effects.
  186. i.user = user or i.user
  187. i.home = home or i.home
  188. i.root = root or i.root
  189. i.finalize_options()
  190. for key in SCHEME_KEYS:
  191. scheme[key] = getattr(i, 'install_' + key)
  192. if i.install_lib is not None:
  193. # install_lib takes precedence over purelib and platlib
  194. scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib))
  195. if running_under_virtualenv():
  196. scheme['headers'] = os.path.join(
  197. sys.prefix,
  198. 'include',
  199. 'site',
  200. 'python' + sys.version[:3],
  201. dist_name,
  202. )
  203. if root is not None:
  204. scheme["headers"] = os.path.join(
  205. root,
  206. os.path.abspath(scheme["headers"])[1:],
  207. )
  208. return scheme