summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernat Gabor <gaborjbernat@gmail.com>2018-09-16 20:29:19 +0100
committerBernát Gábor <gaborjbernat@gmail.com>2018-09-17 01:10:03 +0300
commitad81f9ae84375651ef009a80985241ced60a6ce3 (patch)
tree977fad4c22b069aa452e07988051182c86ddf80e
parente8f877ee2625a4628e5434384cd7d2bc794e10a2 (diff)
downloadtox-git-ad81f9ae84375651ef009a80985241ced60a6ce3.tar.gz
always set PIP_USER and PIP_NO_DEPS to 0 #838
-rw-r--r--changelog/838.feature.rst1
-rwxr-xr-xsrc/tox/venv.py87
-rw-r--r--tests/unit/test_venv.py2
3 files changed, 59 insertions, 31 deletions
diff --git a/changelog/838.feature.rst b/changelog/838.feature.rst
new file mode 100644
index 00000000..b90ff843
--- /dev/null
+++ b/changelog/838.feature.rst
@@ -0,0 +1 @@
+always set ``PIP_USER=0`` (do not install into the user site package, but inside the virtual environment created) and ``PIP_NO_DEPS=0`` (installing without dependencies can cause broken package installations) inside tox - by :user:`gaborbernat`
diff --git a/src/tox/venv.py b/src/tox/venv.py
index a10149a9..0209f915 100755
--- a/src/tox/venv.py
+++ b/src/tox/venv.py
@@ -5,6 +5,7 @@ import pipes
import re
import sys
import warnings
+from itertools import chain
import py
@@ -231,7 +232,7 @@ class VirtualEnv(object):
setup_py = setupdir.join("setup.py")
setup_cfg = setupdir.join("setup.cfg")
args = [self.envconfig.envpython, str(setup_py), "--name"]
- env = self._getenv()
+ env = self._get_os_environ()
output = action.popen(args, cwd=setupdir, redirect=False, returnout=True, env=env)
name = output.strip()
args = [self.envconfig.envpython, "-c", "import sys; print(sys.path)"]
@@ -297,30 +298,26 @@ class VirtualEnv(object):
return options
def run_install_command(self, packages, action, options=()):
- argv = self.envconfig.install_command[:]
- i = argv.index("{packages}")
- argv[i : i + 1] = packages
- if "{opts}" in argv:
- i = argv.index("{opts}")
- argv[i : i + 1] = list(options)
+ def expand(val):
+ # expand an install command
+ if val == "{packages}":
+ for package in packages:
+ yield package
+ elif val == "{opts}":
+ for opt in options:
+ yield opt
+ else:
+ yield val
- for x in ("PIP_RESPECT_VIRTUALENV", "PIP_REQUIRE_VIRTUALENV", "__PYVENV_LAUNCHER__"):
- os.environ.pop(x, None)
+ cmd = list(chain.from_iterable(expand(val) for val in self.envconfig.install_command))
- if "PYTHONPATH" not in self.envconfig.passenv:
- # If PYTHONPATH not explicitly asked for, remove it.
- if "PYTHONPATH" in os.environ:
- self.session.report.warning(
- "Discarding $PYTHONPATH from environment, to override "
- "specify PYTHONPATH in 'passenv' in your configuration."
- )
- os.environ.pop("PYTHONPATH")
+ self.ensure_pip_os_environ_ok()
old_stdout = sys.stdout
sys.stdout = codecs.getwriter("utf8")(sys.stdout)
try:
self._pcall(
- argv,
+ cmd,
cwd=self.envconfig.config.toxinidir,
action=action,
redirect=self.session.report.verbosity < 2,
@@ -328,6 +325,24 @@ class VirtualEnv(object):
finally:
sys.stdout = old_stdout
+ def ensure_pip_os_environ_ok(self):
+ for key in ("PIP_RESPECT_VIRTUALENV", "PIP_REQUIRE_VIRTUALENV", "__PYVENV_LAUNCHER__"):
+ os.environ.pop(key, None)
+ if "PYTHONPATH" not in self.envconfig.passenv:
+ # If PYTHONPATH not explicitly asked for, remove it.
+ if "PYTHONPATH" in os.environ:
+ self.session.report.warning(
+ "Discarding $PYTHONPATH from environment, to override "
+ "specify PYTHONPATH in 'passenv' in your configuration."
+ )
+ os.environ.pop("PYTHONPATH")
+
+ # installing packages at user level may mean we're not installing inside the venv
+ os.environ["PIP_USER"] = "0"
+
+ # installing without dependencies may lead to broken packages
+ os.environ["PIP_NO_DEPS"] = "0"
+
def _install(self, deps, extraopts=None, action=None):
if not deps:
return
@@ -353,13 +368,13 @@ class VirtualEnv(object):
options.extend(extraopts)
self.run_install_command(packages=packages, options=options, action=action)
- def _getenv(self, testcommand=False):
- if testcommand:
+ def _get_os_environ(self, is_test_command=False):
+ if is_test_command:
# for executing tests we construct a clean environment
env = {}
- for envname in self.envconfig.passenv:
- if envname in os.environ:
- env[envname] = os.environ[envname]
+ for env_key in self.envconfig.passenv:
+ if env_key in os.environ:
+ env[env_key] = os.environ[env_key]
else:
# for executing non-test commands we use the full
# invocation environment
@@ -377,7 +392,7 @@ class VirtualEnv(object):
self.session.make_emptydir(self.envconfig.envtmpdir)
self.envconfig.envtmpdir.ensure(dir=1)
cwd = self.envconfig.changedir
- env = self._getenv(testcommand=True)
+ env = self._get_os_environ(is_test_command=True)
# Display PYTHONHASHSEED to assist with reproducibility.
action.setactivity("runtests", "PYTHONHASHSEED={!r}".format(env.get("PYTHONHASHSEED")))
for i, argv in enumerate(self.envconfig.commands):
@@ -405,7 +420,7 @@ class VirtualEnv(object):
action=action,
redirect=redirect,
ignore_ret=ignore_ret,
- testcommand=True,
+ is_test_command=True,
)
except tox.exception.InvocationError as err:
if self.envconfig.ignore_outcome:
@@ -424,18 +439,28 @@ class VirtualEnv(object):
raise
def _pcall(
- self, args, cwd, venv=True, testcommand=False, action=None, redirect=True, ignore_ret=False
+ self,
+ args,
+ cwd,
+ venv=True,
+ is_test_command=False,
+ action=None,
+ redirect=True,
+ ignore_ret=False,
):
+ # construct environment variables
os.environ.pop("VIRTUALENV_PYTHON", None)
+ env = self._get_os_environ(is_test_command=is_test_command)
+ bin_dir = str(self.envconfig.envbindir)
+ env["PATH"] = os.pathsep.join([bin_dir, os.environ["PATH"]])
+ self.session.report.verbosity2("setting PATH={}".format(env["PATH"]))
- cwd.ensure(dir=1)
+ # get command
args[0] = self.getcommandpath(args[0], venv, cwd)
if sys.platform != "win32" and "TOX_LIMITED_SHEBANG" in os.environ:
args = prepend_shebang_interpreter(args)
- env = self._getenv(testcommand=testcommand)
- bindir = str(self.envconfig.envbindir)
- env["PATH"] = p = os.pathsep.join([bindir, os.environ["PATH"]])
- self.session.report.verbosity2("setting PATH={}".format(p))
+
+ cwd.ensure(dir=1) # ensure the cwd exists
return action.popen(args, cwd=cwd, env=env, redirect=redirect, ignore_ret=ignore_ret)
diff --git a/tests/unit/test_venv.py b/tests/unit/test_venv.py
index 54a52511..5b830956 100644
--- a/tests/unit/test_venv.py
+++ b/tests/unit/test_venv.py
@@ -625,6 +625,8 @@ class TestVenvTest:
assert "PIP_RESPECT_VIRTUALENV" not in os.environ
assert "PIP_REQUIRE_VIRTUALENV" not in os.environ
assert "__PYVENV_LAUNCHER__" not in os.environ
+ assert os.environ["PIP_USER"] == "0"
+ assert os.environ["PIP_NO_DEPS"] == "0"
def test_pythonpath_usage(self, newmocksession, monkeypatch):
monkeypatch.setenv("PYTHONPATH", "/my/awesome/library")