Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a virtualenv-specific configuration file #1364

Merged
merged 2 commits into from
Dec 8, 2013
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ 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 configuration file.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and precedence over a config file specified using PIP_CONFIG_FILE

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was seeing PIP_CONFIG_FILE as just changing the location if "the user configuration file", but I'll see if I can word this more clearly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, it seems to me we should have a real option for --config-file. PIP_CONFIG_FILE is the only odd ball case where we have an environment option without a comand option.


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
Expand Down
12 changes: 9 additions & 3 deletions pip/baseparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be like <venv>/etc/pip.conf? That would stay in compatibility to a potential global pip config.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no env subdirectory on Windows (and no standard sysconfig location like bin_dir or lib_dir) I put it in sys.prefix because that's where the standard Python venv puts its parameter file (although the reason for that file is different).

If it should go in /etc on Unix I could do that - but I wouldn't want to for Windows. Also, should virtualenv create an etc directory? Or should the user be required to create the directory in order to add the per-venv file (and know that they need to do that on Unix but not Windows)?

Personally I think it's more compexity than the feature warrants, but I can do it if the Unix people think it's worth it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I didn't know the core venv does that, interesting. Yeah, for the sake of simplicity I agree sys.prefix is a good choice. Thanks for the clarification, @pfmoore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thedrow I'm going to stick with sys.prefix.. Reasons:

  1. Consistency with core Python venv config location.
  2. Less inconsistency between Unix and Windows (bin vs Scripts is a continual source of irritation, I don't want to do more of the same).
  3. No need for the user to know he has to manually create an etc directory in his venv.

if os.path.exists(venv_config_file):
files.append(venv_config_file)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so previously, pip has never exercised the multi-file support of ConfigParser. not sure how the layering works with that? are you comfortable with that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did check it - it does what I'd expect (later files take precedence over earlier ones where section/key names clash). Yes, I'm comfortable with it.

return files

def check_default(self, option, key, val):
try:
Expand Down
6 changes: 4 additions & 2 deletions pip/locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions tests/functional/test_install_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


14 changes: 14 additions & 0 deletions tests/unit/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -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_general_option_after_subcommand(self, monkeypatch):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about relabel this test name. "test_venv_config_file_found"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, cut and paste typo. Will fix.

# 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