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

pexrc support #128

Merged
merged 5 commits into from Oct 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
@@ -1,6 +1,7 @@
*~
*.pyc
*.egg-info
/venv
/build/*
/dist/*
/docs/_build
Expand Down
3 changes: 2 additions & 1 deletion docs/buildingpex.rst
Expand Up @@ -322,7 +322,8 @@ Tailoring PEX execution at build time

There are a few options that can tailor how PEX environments are invoked. These can be found
by running ``pex --help``. Every flag mentioned here has a corresponding environment variable
that can be used to override the runtime behavior.
that can be used to override the runtime behavior which can be set directly in your environment,
or sourced from a ``.pexrc`` file (checking for ``~/.pexrc`` first, then for a relative ``.pexrc``).


``--zip-safe``/``--not-zip-safe``
Expand Down
33 changes: 31 additions & 2 deletions pex/variables.py
Expand Up @@ -5,6 +5,7 @@
# checkstyle: noqa

import os
import sys
from contextlib import contextmanager

from .common import die
Expand Down Expand Up @@ -32,9 +33,13 @@ def iter_help(cls):
variable_type, variable_text = cls.process_pydoc(getattr(value, '__doc__'))
yield variable_name, variable_type, variable_text

def __init__(self, environ=None, use_defaults=True):
self._environ = environ.copy() if environ is not None else os.environ
def __init__(self, environ=None, rc='~/.pexrc', use_defaults=True):
self._use_defaults = use_defaults
self._environ = environ.copy() if environ else os.environ
if not self.PEX_IGNORE_RCFILES:
rc_values = self._from_rc(rc).copy()
rc_values.update(self._environ)
self._environ = rc_values

def copy(self):
return self._environ.copy()
Expand All @@ -45,6 +50,22 @@ def delete(self, variable):
def set(self, variable, value):
self._environ[variable] = str(value)

def _from_rc(self, rc):
ret_vars = {}
for filename in [rc, os.path.join(os.path.dirname(sys.argv[0]), '.pexrc')]:
try:
with open(os.path.expanduser(filename)) as fh:
rc_items = map(self._get_kv, fh)
ret_vars.update(dict(filter(None, rc_items)))
except IOError:
continue
return ret_vars

def _get_kv(self, variable):
kv = variable.strip().split('=')
if len(list(filter(None, kv))) == 2:
return kv

def _defaulted(self, default):
return default if self._use_defaults else None

Expand Down Expand Up @@ -271,6 +292,14 @@ def PEX_HTTP_RETRIES(self):
"""
return self._get_int('PEX_HTTP_RETRIES', default=5)

@property
def PEX_IGNORE_RCFILES(self):
"""Boolean

Explicitly disable the reading/parsing of pexrc files (~/.pexrc). Default: false.
"""
return self._get_bool('PEX_IGNORE_RCFILES', default=False)


# Global singleton environment
ENV = Variables()
34 changes: 34 additions & 0 deletions tests/test_variables.py
@@ -1,6 +1,8 @@
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import tempfile

import pytest

from pex.variables import Variables
Expand Down Expand Up @@ -74,6 +76,38 @@ def test_pex_vars_set():
assert v._get_int('HELLO') is None


def test_pex_get_kv():
v = Variables(environ={})
assert v._get_kv('HELLO') is None
assert v._get_kv('=42') is None
assert v._get_kv('TOO=MANY=COOKS') is None
assert v._get_kv('THIS=WORKS') == ['THIS', 'WORKS']


def test_pex_from_rc():
with tempfile.NamedTemporaryFile(mode='w') as pexrc:
pexrc.write('HELLO=42')
pexrc.flush()
v = Variables(rc=pexrc.name)
assert v._get_int('HELLO') == 42


def test_pexrc_precedence():
with tempfile.NamedTemporaryFile(mode='w') as pexrc:
pexrc.write('HELLO=FORTYTWO')
pexrc.flush()
v = Variables(environ={'HELLO': 42}, rc=pexrc.name)
assert v._get_int('HELLO') == 42


def test_rc_ignore():
with tempfile.NamedTemporaryFile(mode='w') as pexrc:
pexrc.write('HELLO=FORTYTWO')
pexrc.flush()
v = Variables(environ={'PEX_IGNORE_RC_FILES': True, 'HELLO': 42}, rc=pexrc.name)
assert v._get_int('HELLO') == 42
Copy link
Member

Choose a reason for hiding this comment

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

This does not show ignore worked since 42 also wins by precedence if not ignored.

Fixing this test and optionally adding 2 more tests, one for .pexrc and one for ~/.pexrc .pexrc precedence would be great if you're around and have time to do this today. If not I'll merge this in at 1pm Mountain and follow up with these before release later in the afternoon.

All the rest lgtm.

Copy link
Member

Choose a reason for hiding this comment

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

Actually - this test is doubly broken atm. The IGNORE env var name is typoed and the value is invalid - must be a string. Its an accident ints work as environ= values, the intent is int([str]) in the parse routine and it happens that int(int) works just fine.

So this works:

$ git diff -U1
diff --git a/tests/test_variables.py b/tests/test_variables.py
index 124ec6d..447efb5 100644
--- a/tests/test_variables.py
+++ b/tests/test_variables.py
@@ -106,4 +106,4 @@ def test_rc_ignore():
     pexrc.flush()
-    v = Variables(environ={'PEX_IGNORE_RC_FILES': True, 'HELLO': 42}, rc=pexrc.name)
-    assert v._get_int('HELLO') == 42
+    v = Variables(environ={'PEX_IGNORE_RCFILES': 'True'}, rc=pexrc.name)
+    assert 'HELLO' not in v._environ

Since the fixed up test works - ie the prod code works - I'll merge this in and follow up with a fix to make the test valid.



def test_pex_vars_defaults_stripped():
v = Variables(environ={})
stripped = v.strip_defaults()
Expand Down