diff --git a/docs/configuration.rst b/docs/configuration.rst index 74e9362bcf6..b1d9faf09ff 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -19,6 +19,13 @@ platforms. You can set a custom path location for the config file using the environment variable ``PIP_CONFIG_FILE``. +In a virtual environment, an additional config file will be read from the base +directory of the virtualenv (``sys.prefix`` as reported by Python). The base +name of the file is the same as the user configuration file (:file:`pip.conf` +on Unix and OSX, :file:`pip.ini` on Windows). Values in the virtualenv-specific +configuration file take precedence over those in the user's configuration file +(whether from the user home or specified via ``PIP_CONFIG_FILE``). + The names of the settings are derived from the long command line option, e.g. if you want to use a different package index (``--index-url``) and set the HTTP timeout (``--default-timeout``) to 60 seconds your config file would diff --git a/pip/baseparser.py b/pip/baseparser.py index ce4016033fc..42aca8f5fd7 100644 --- a/pip/baseparser.py +++ b/pip/baseparser.py @@ -7,7 +7,7 @@ import textwrap from distutils.util import strtobool from pip.backwardcompat import ConfigParser, string_types -from pip.locations import default_config_file +from pip.locations import default_config_file, default_config_basename, running_under_virtualenv from pip.util import get_terminal_size, get_prog @@ -136,8 +136,14 @@ def __init__(self, *args, **kwargs): def get_config_files(self): config_file = os.environ.get('PIP_CONFIG_FILE', False) if config_file and os.path.exists(config_file): - return [config_file] - return [default_config_file] + files = [config_file] + else: + files = [default_config_file] + if running_under_virtualenv(): + venv_config_file = os.path.join(sys.prefix, default_config_basename) + if os.path.exists(venv_config_file): + files.append(venv_config_file) + return files def check_default(self, option, key, val): try: diff --git a/pip/locations.py b/pip/locations.py index 00cf27cb73a..85e250eb342 100644 --- a/pip/locations.py +++ b/pip/locations.py @@ -112,12 +112,14 @@ def _get_build_prefix(): if not os.path.exists(bin_py): bin_py = os.path.join(sys.prefix, 'bin') default_storage_dir = os.path.join(user_dir, 'pip') - default_config_file = os.path.join(default_storage_dir, 'pip.ini') + default_config_basename = 'pip.ini' + default_config_file = os.path.join(default_storage_dir, default_config_basename) default_log_file = os.path.join(default_storage_dir, 'pip.log') else: bin_py = os.path.join(sys.prefix, 'bin') default_storage_dir = os.path.join(user_dir, '.pip') - default_config_file = os.path.join(default_storage_dir, 'pip.conf') + default_config_basename = 'pip.conf' + default_config_file = os.path.join(default_storage_dir, default_config_basename) default_log_file = os.path.join(default_storage_dir, 'pip.log') # Forcing to use /usr/local/bin for standard Mac OS X framework installs diff --git a/tests/functional/test_install_config.py b/tests/functional/test_install_config.py index 7a896d35ce4..ca6c360cb4e 100644 --- a/tests/functional/test_install_config.py +++ b/tests/functional/test_install_config.py @@ -138,3 +138,20 @@ def test_log_file_no_directory(): fp.close() assert os.path.exists(fp.name) os.remove(fp.name) + + +def test_options_from_venv_config(script, virtualenv): + """ + Test if ConfigOptionParser reads a virtualenv-local config file + + """ + from pip.locations import default_config_basename + conf = "[global]\nno-index = true" + ini = virtualenv.location / default_config_basename + with open(ini, 'w') as f: + f.write(conf) + result = script.pip('install', '-vvv', 'INITools', expect_error=True) + assert "Ignoring indexes:" in result.stdout, str(result) + assert "DistributionNotFound: No distributions at all found for INITools" in result.stdout + + diff --git a/tests/unit/test_options.py b/tests/unit/test_options.py index 56be3ae771a..d6f864fb52d 100644 --- a/tests/unit/test_options.py +++ b/tests/unit/test_options.py @@ -226,3 +226,17 @@ def test_cert(self): options1, args1 = main(['--cert', 'path', 'fake']) options2, args2 = main(['fake', '--cert', 'path']) assert options1.cert == options2.cert == 'path' + + +class TestOptionsConfigFiles(object): + + def test_venv_config_file_found(self, monkeypatch): + # We only want a dummy object to call the get_config_files method + monkeypatch.setattr(pip.baseparser.ConfigOptionParser, '__init__', lambda self: None) + + # If we are running in a virtualenv and all files appear to exist, + # we should see two config files. + monkeypatch.setattr(pip.baseparser, 'running_under_virtualenv', lambda: True) + monkeypatch.setattr(os.path, 'exists', lambda filename: True) + cp = pip.baseparser.ConfigOptionParser() + assert len(cp.get_config_files()) == 2