1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00
pip/pip/locations.py
2016-01-20 07:32:19 -05:00

230 lines
8 KiB
Python

"""Locations where we look for configs, install stuff, etc"""
from __future__ import absolute_import
import os
import os.path
import site
import ssl
import sys
from distutils import sysconfig
from distutils.command.install import install, SCHEME_KEYS # noqa
from pip.compat import WINDOWS, expanduser
from pip.utils import appdirs
# if the Python we're running on is new enough to have the needed API then
# we'll ask OpenSSL to give us the path to the default CA Bundle. If this API
# doesn't exist or we cannot resolve the path to an existing file, then we will
# simply set this to None. Setting this to None will have requests fall back
# and use it's default CA Bundle logic.
# Ever since requests 2.9.0, requests has supported a CAPath in addition to a
# CAFile, because some systems (such as Debian) have a broken CAFile currently
# we'll go ahead and support both, prefering CAPath over CAfile.
if getattr(ssl, "get_default_verify_paths", None):
_ssl_paths = ssl.get_default_verify_paths()
# Ok, this is a little hairy because system trust stores are randomly
# broken in different and exciting ways and this should help fix that.
# Ideally we'd just not trust the system store, however that leads end
# users to be confused on systems like Debian that patch python-pip, even
# inside of a virtual environment, to support the system store. This would
# lead to pip trusting the system store when creating the virtual
# environment, but then switching to not doing that when upgraded to a
# version from PyPI. However, we can't *only* rely on the system store
# because not all systems actually have one, so at the end of the day we
# still need to fall back to trusting the bundled copy.
#
# Resolution Method:
#
# 1. We prefer a CAPath, however we will *only* prefer a CAPath if the
# directory exists and it is not empty. This works around systems like
# Homebrew which have an empty CAPath but a populated CAFile.
# 2. Failing that, we prefer a CAFile, however again we will *only* prefer
# it if it exists on disk and if it is not empty. This will work around
# systems that have an empty CAFile sitting around for no good reason.
# 3. Finally, we'll just fall back to letting requests use it's bundled
# CAFile, which can of course be overriden by the end user installing
# certifi.
if _ssl_paths.capath is not None and os.listdir(_ssl_paths.capath):
CA_BUNDLE_PATH = _ssl_paths.capath
elif _ssl_paths.cafile is not None and os.path.getsize(_ssl_paths.cafile):
CA_BUNDLE_PATH = _ssl_paths.cafile
else:
CA_BUNDLE_PATH = None
else:
# If we aren't running on a copy of Python that is new enough to be able
# to query OpenSSL for it's default locations, then we'll only support
# using the built in CA Bundle by default.
CA_BUNDLE_PATH = None
# Application Directories
USER_CACHE_DIR = appdirs.user_cache_dir("pip")
DELETE_MARKER_MESSAGE = '''\
This file is placed here by pip to indicate the source was put
here by pip.
Once this package is successfully installed this source code will be
deleted (unless you remove this file).
'''
PIP_DELETE_MARKER_FILENAME = 'pip-delete-this-directory.txt'
def write_delete_marker_file(directory):
"""
Write the pip delete marker file into this directory.
"""
filepath = os.path.join(directory, PIP_DELETE_MARKER_FILENAME)
with open(filepath, 'w') as marker_fp:
marker_fp.write(DELETE_MARKER_MESSAGE)
def running_under_virtualenv():
"""
Return True if we're running inside a virtualenv, False otherwise.
"""
if hasattr(sys, 'real_prefix'):
return True
elif sys.prefix != getattr(sys, "base_prefix", sys.prefix):
return True
return False
def virtualenv_no_global():
"""
Return True if in a venv and no system site packages.
"""
# this mirrors the logic in virtualenv.py for locating the
# no-global-site-packages.txt file
site_mod_dir = os.path.dirname(os.path.abspath(site.__file__))
no_global_file = os.path.join(site_mod_dir, 'no-global-site-packages.txt')
if running_under_virtualenv() and os.path.isfile(no_global_file):
return True
if running_under_virtualenv():
src_prefix = os.path.join(sys.prefix, 'src')
else:
# FIXME: keep src in cwd for now (it is not a temporary folder)
try:
src_prefix = os.path.join(os.getcwd(), 'src')
except OSError:
# In case the current working directory has been renamed or deleted
sys.exit(
"The folder you are executing pip from can no longer be found."
)
# under Mac OS X + virtualenv sys.prefix is not properly resolved
# it is something like /path/to/python/bin/..
# Note: using realpath due to tmp dirs on OSX being symlinks
src_prefix = os.path.abspath(src_prefix)
# FIXME doesn't account for venv linked to global site-packages
site_packages = sysconfig.get_python_lib()
user_site = site.USER_SITE
user_dir = expanduser('~')
if WINDOWS:
bin_py = os.path.join(sys.prefix, 'Scripts')
bin_user = os.path.join(user_site, 'Scripts')
# buildout uses 'bin' on Windows too?
if not os.path.exists(bin_py):
bin_py = os.path.join(sys.prefix, 'bin')
bin_user = os.path.join(user_site, 'bin')
config_basename = 'pip.ini'
legacy_storage_dir = os.path.join(user_dir, 'pip')
legacy_config_file = os.path.join(
legacy_storage_dir,
config_basename,
)
else:
bin_py = os.path.join(sys.prefix, 'bin')
bin_user = os.path.join(user_site, 'bin')
config_basename = 'pip.conf'
legacy_storage_dir = os.path.join(user_dir, '.pip')
legacy_config_file = os.path.join(
legacy_storage_dir,
config_basename,
)
# Forcing to use /usr/local/bin for standard Mac OS X framework installs
# Also log to ~/Library/Logs/ for use with the Console.app log viewer
if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/':
bin_py = '/usr/local/bin'
site_config_files = [
os.path.join(path, config_basename)
for path in appdirs.site_config_dirs('pip')
]
def distutils_scheme(dist_name, user=False, home=None, root=None,
isolated=False, prefix=None):
"""
Return a distutils install scheme
"""
from distutils.dist import Distribution
scheme = {}
if isolated:
extra_dist_args = {"script_args": ["--no-user-cfg"]}
else:
extra_dist_args = {}
dist_args = {'name': dist_name}
dist_args.update(extra_dist_args)
d = Distribution(dist_args)
d.parse_config_files()
i = d.get_command_obj('install', create=True)
# NOTE: setting user or home has the side-effect of creating the home dir
# or user base for installations during finalize_options()
# ideally, we'd prefer a scheme class that has no side-effects.
assert not (user and prefix), "user={0} prefix={1}".format(user, prefix)
i.user = user or i.user
if user:
i.prefix = ""
i.prefix = prefix or i.prefix
i.home = home or i.home
i.root = root or i.root
i.finalize_options()
for key in SCHEME_KEYS:
scheme[key] = getattr(i, 'install_' + key)
# install_lib specified in setup.cfg should install *everything*
# into there (i.e. it takes precedence over both purelib and
# platlib). Note, i.install_lib is *always* set after
# finalize_options(); we only want to override here if the user
# has explicitly requested it hence going back to the config
if 'install_lib' in d.get_option_dict('install'):
scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib))
if running_under_virtualenv():
scheme['headers'] = os.path.join(
sys.prefix,
'include',
'site',
'python' + sys.version[:3],
dist_name,
)
if root is not None:
path_no_drive = os.path.splitdrive(
os.path.abspath(scheme["headers"]))[1]
scheme["headers"] = os.path.join(
root,
path_no_drive[1:],
)
return scheme