4 new commits in tox: https://bitbucket.org/hpk42/tox/commits/438a7c640081/ Changeset: 438a7c640081 Branch: passenv_multiline User: mazzucco Date: 2015-06-18 09:50:48+00:00 Summary: hpk42/tox/issue/259/passenv-statement-should-accept-multi-line Affected #: 2 files
diff -r 6d4378ce90621105cda12fb2f9dfc67dfbefc7ca -r 438a7c640081598e9d97d100fcbf5122c327e04f tests/test_config.py --- a/tests/test_config.py +++ b/tests/test_config.py @@ -696,14 +696,44 @@ assert envconfig.setenv['ANOTHER_VAL'] == 'else' @pytest.mark.parametrize("plat", ["win32", "linux2"]) - def test_passenv(self, tmpdir, newconfig, monkeypatch, plat): + def test_passenv_as_multiline_list(self, tmpdir, newconfig, monkeypatch, plat): monkeypatch.setattr(sys, "platform", plat) monkeypatch.setenv("A123A", "a") monkeypatch.setenv("A123B", "b") monkeypatch.setenv("BX23", "0") config = newconfig(""" [testenv] - passenv = A123* B?23 + passenv = + A123* + # isolated comment + B?23 + """) + assert len(config.envconfigs) == 1 + envconfig = config.envconfigs['python'] + if plat == "win32": + assert "PATHEXT" in envconfig.passenv + assert "SYSTEMDRIVE" in envconfig.passenv + assert "SYSTEMROOT" in envconfig.passenv + assert "TEMP" in envconfig.passenv + assert "TMP" in envconfig.passenv + else: + assert "TMPDIR" in envconfig.passenv + assert "PATH" in envconfig.passenv + assert "PIP_INDEX_URL" in envconfig.passenv + assert "LANG" in envconfig.passenv + assert "A123A" in envconfig.passenv + assert "A123B" in envconfig.passenv + + @pytest.mark.parametrize("plat", ["win32", "linux2"]) + def test_passenv_as_space_separated_list(self, tmpdir, newconfig, monkeypatch, plat): + monkeypatch.setattr(sys, "platform", plat) + monkeypatch.setenv("A123A", "a") + monkeypatch.setenv("A123B", "b") + monkeypatch.setenv("BX23", "0") + config = newconfig(""" + [testenv] + passenv = + A123* B?23 """) assert len(config.envconfigs) == 1 envconfig = config.envconfigs['python'] diff -r 6d4378ce90621105cda12fb2f9dfc67dfbefc7ca -r 438a7c640081598e9d97d100fcbf5122c327e04f tox/config.py --- a/tox/config.py +++ b/tox/config.py @@ -387,6 +387,11 @@ help="list of X=Y lines with environment variable settings") def passenv(testenv_config, value): + if len(value) == 1 and "\n" in value[0]: + # If we have a list of 1 element that contains new lines, + # passenv has been specified as a multi line list. + value = value[0].split("\n") + passenv = set(["PATH", "PIP_INDEX_URL", "LANG"]) # we ensure that tmp directory settings are passed on https://bitbucket.org/hpk42/tox/commits/84af17ba7280/ Changeset: 84af17ba7280 Branch: passenv_multiline User: stefano-m Date: 2015-06-18 20:15:04+00:00 Summary: merge with default Affected #: 7 files diff -r 438a7c640081598e9d97d100fcbf5122c327e04f -r 84af17ba72801f740b12663e6f4cc56cbbb404eb CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,14 @@ +2.1.0 +---------- + +- fix issue258, fix issue248, fix issue253: for non-test commands + (installation, venv creation) we pass in the full invocation environment. + +- remove experimental --set-home option which was hardly used and + hackily implemented (if people want home-directory isolation we should + figure out a better way to do it, possibly through a plugin) + + 2.0.2 ---------- diff -r 438a7c640081598e9d97d100fcbf5122c327e04f -r 84af17ba72801f740b12663e6f4cc56cbbb404eb doc/config.txt --- a/doc/config.txt +++ b/doc/config.txt @@ -195,14 +195,16 @@ A list of wildcard environment variable names which shall be copied from the tox invocation environment to the test - environment. If a specified environment variable doesn't exist in the tox - invocation environment it is ignored. You can use ``*`` and ``?`` to - match multiple environment variables with one name. + environment when executing test commands. If a specified environment + variable doesn't exist in the tox invocation environment it is ignored. + You can use ``*`` and ``?`` to match multiple environment variables with + one name. - Note that the ``PATH`` and ``PIP_INDEX_URL`` variables are unconditionally - passed down and on Windows ``SYSTEMROOT``, ``PATHEXT``, ``TEMP`` and ``TMP`` - will be passed down as well whereas on unix ``TMPDIR`` will be passed down. - You can override these variables with the ``setenv`` option. + Note that the ``PATH``, ``LANG`` and ``PIP_INDEX_URL`` variables are + unconditionally passed down and on Windows ``SYSTEMROOT``, ``PATHEXT``, + ``TEMP`` and ``TMP`` will be passed down as well whereas on unix + ``TMPDIR`` will be passed down. You can override these variables + with the ``setenv`` option. .. confval:: recreate=True|False(default) diff -r 438a7c640081598e9d97d100fcbf5122c327e04f -r 84af17ba72801f740b12663e6f4cc56cbbb404eb setup.py --- a/setup.py +++ b/setup.py @@ -48,7 +48,7 @@ description='virtualenv-based automation of test activities', long_description=open("README.rst").read(), url='http://tox.testrun.org/', - version='2.0.2', + version='2.1.0.dev1', license='http://opensource.org/licenses/MIT', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='holger krekel', diff -r 438a7c640081598e9d97d100fcbf5122c327e04f -r 84af17ba72801f740b12663e6f4cc56cbbb404eb tests/test_venv.py --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -510,6 +510,7 @@ def test_env_variables_added_to_pcall(tmpdir, mocksession, newconfig, monkeypatch): pkg = tmpdir.ensure("package.tar.gz") monkeypatch.setenv("X123", "123") + monkeypatch.setenv("YY", "456") config = newconfig([], """ [testenv:python] commands=python -V @@ -533,9 +534,12 @@ assert env['ENV_VAR'] == 'value' assert env['VIRTUAL_ENV'] == str(venv.path) assert env['X123'] == "123" + # all env variables are passed for installation + assert l[0].env["YY"] == "456" + assert "YY" not in l[1].env assert set(["ENV_VAR", "VIRTUAL_ENV", "PYTHONHASHSEED", "X123", "PATH"])\ - .issubset(env) + .issubset(l[1].env) # for e in os.environ: # assert e in env @@ -614,49 +618,3 @@ x4 = venv.getcommandpath("x", cwd=tmpdir) assert x4.endswith(os.sep + 'x') mocksession.report.expect("warning", "*test command found but not*") - - -def test_sethome_only_on_option(newmocksession, monkeypatch): - mocksession = newmocksession([], "") - venv = mocksession.getenv('python') - action = mocksession.newaction(venv, "qwe", []) - monkeypatch.setattr(tox.venv, "hack_home_env", None) - venv._install(["x"], action=action) - - -def test_sethome_works_on_option(newmocksession, monkeypatch): - mocksession = newmocksession(["--set-home", "-i ALL=http://qwe"], "") - venv = mocksession.getenv('python') - action = mocksession.newaction(venv, "qwe", []) - venv._install(["x"], action=action) - _, mocked = mocksession.report.getnext("logpopen") - p = mocked.env["HOME"] - pydist = py.path.local(p).join(".pydistutils.cfg") - assert "http://qwe" in pydist.read() - - -def test_hack_home_env(tmpdir): - from tox.venv import hack_home_env - env = hack_home_env(tmpdir, "http://index") - assert env["HOME"] == str(tmpdir) - assert env["PIP_INDEX_URL"] == "http://index" - assert "index_url = http://index" in \ - tmpdir.join(".pydistutils.cfg").read() - tmpdir.remove() - env = hack_home_env(tmpdir, None) - assert env["HOME"] == str(tmpdir) - assert not tmpdir.join(".pydistutils.cfg").check() - assert "PIP_INDEX_URL" not in env - - -def test_hack_home_env_passthrough(tmpdir, monkeypatch): - from tox.venv import hack_home_env - env = hack_home_env(tmpdir, "http://index") - monkeypatch.setattr(os, "environ", env) - - tmpdir = tmpdir.mkdir("tmpdir2") - env2 = hack_home_env(tmpdir) - assert env2["HOME"] == str(tmpdir) - assert env2["PIP_INDEX_URL"] == "http://index" - assert "index_url = http://index" in \ - tmpdir.join(".pydistutils.cfg").read() diff -r 438a7c640081598e9d97d100fcbf5122c327e04f -r 84af17ba72801f740b12663e6f4cc56cbbb404eb tox/__init__.py --- a/tox/__init__.py +++ b/tox/__init__.py @@ -1,5 +1,5 @@ # -__version__ = '2.0.2' +__version__ = '2.1.0.dev1' from .hookspecs import hookspec, hookimpl # noqa diff -r 438a7c640081598e9d97d100fcbf5122c327e04f -r 84af17ba72801f740b12663e6f4cc56cbbb404eb tox/config.py --- a/tox/config.py +++ b/tox/config.py @@ -283,10 +283,6 @@ parser.add_argument("--develop", action="store_true", dest="develop", help="install package in the venv using 'setup.py develop' via " "'pip -e .'") - parser.add_argument("--set-home", action="store_true", dest="sethome", - help="(experimental) force creating a new $HOME for each test " - "environment and create .pydistutils.cfg|pip.conf files " - "if index servers are specified with tox. ") parser.add_argument('-i', action="append", dest="indexurl", metavar="URL", help="set indexserver url (if URL is of form name=url set the " @@ -299,11 +295,9 @@ dest="recreate", help="force recreation of virtual environments") parser.add_argument("--result-json", action="store", - dest="resultjson", metavar="PATH", - help="write a json file with detailed information about " - "all commands and results involved. This will turn off " - "pass-through output from running test commands which is " - "instead captured into the json result file.") + dest="resultjson", metavar="PATH", + help="write a json file with detailed information about " + "all commands and results involved.") # We choose 1 to 4294967295 because it is the range of PYTHONHASHSEED. parser.add_argument("--hashseed", action="store", @@ -414,8 +408,11 @@ parser.add_testenv_attribute( name="passenv", type="space-separated-list", postprocess=passenv, - help="environment variables names which shall be passed " - "from tox invocation to test environment when executing commands.") + help="environment variables needed during executing test commands " + "(taken from invocation environment). Note that tox always " + "passes through some basic environment variables which are " + "needed for basic functioning of the Python system. " + "See --showconfig for the eventual passenv setting.") parser.add_testenv_attribute( name="whitelist_externals", type="line-list", diff -r 438a7c640081598e9d97d100fcbf5122c327e04f -r 84af17ba72801f740b12663e6f4cc56cbbb404eb tox/venv.py --- a/tox/venv.py +++ b/tox/venv.py @@ -259,9 +259,7 @@ l.append("--pre") return l - def run_install_command(self, packages, options=(), - indexserver=None, action=None, - extraenv=None): + def run_install_command(self, packages, options=(), action=None): argv = self.envconfig.install_command[:] # use pip-script on win32 to avoid the executable locking i = argv.index('{packages}') @@ -269,18 +267,14 @@ if '{opts}' in argv: i = argv.index('{opts}') argv[i:i + 1] = list(options) + for x in ('PIP_RESPECT_VIRTUALENV', 'PIP_REQUIRE_VIRTUALENV', '__PYVENV_LAUNCHER__'): - try: - del os.environ[x] - except KeyError: - pass + os.environ.pop(x, None) + old_stdout = sys.stdout sys.stdout = codecs.getwriter('utf8')(sys.stdout) - if extraenv is None: - extraenv = {} - self._pcall(argv, cwd=self.envconfig.config.toxinidir, - extraenv=extraenv, action=action) + self._pcall(argv, cwd=self.envconfig.config.toxinidir, action=action) sys.stdout = old_stdout def _install(self, deps, extraopts=None, action=None): @@ -302,31 +296,29 @@ assert ixserver.url is None or isinstance(ixserver.url, str) for ixserver in l: - if self.envconfig.config.option.sethome: - extraenv = hack_home_env( - homedir=self.envconfig.envtmpdir.join("pseudo-home"), - index_url=ixserver.url) - else: - extraenv = {} - packages = d[ixserver] options = self._installopts(ixserver.url) if extraopts: options.extend(extraopts) self.run_install_command(packages=packages, options=options, - action=action, extraenv=extraenv) + action=action) - def _getenv(self, extraenv={}): - env = {} - for envname in self.envconfig.passenv: - if envname in os.environ: - env[envname] = os.environ[envname] + def _getenv(self, testcommand=False): + if testcommand: + # 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] + else: + # for executing non-test commands we use the full + # invocation environment + env = os.environ.copy() + # in any case we honor per-testenv setenv configuration env.update(self.envconfig.setenv) env['VIRTUAL_ENV'] = str(self.path) - - env.update(extraenv) return env def test(self, redirect=False): @@ -335,7 +327,7 @@ self.status = 0 self.session.make_emptydir(self.envconfig.envtmpdir) cwd = self.envconfig.changedir - env = self._getenv() + env = self._getenv(testcommand=True) # Display PYTHONHASHSEED to assist with reproducibility. action.setactivity("runtests", "PYTHONHASHSEED=%r" % env.get('PYTHONHASHSEED')) for i, argv in enumerate(self.envconfig.commands): @@ -357,7 +349,7 @@ try: self._pcall(argv, cwd=cwd, action=action, redirect=redirect, - ignore_ret=ignore_ret) + ignore_ret=ignore_ret, testcommand=True) except tox.exception.InvocationError as err: self.session.report.error(str(err)) self.status = "commands failed" @@ -368,20 +360,18 @@ self.session.report.error(self.status) raise - def _pcall(self, args, venv=True, cwd=None, extraenv={}, + def _pcall(self, args, cwd, venv=True, testcommand=False, action=None, redirect=True, ignore_ret=False): for name in ("VIRTUALENV_PYTHON", "PYTHONDONTWRITEBYTECODE"): - try: - del os.environ[name] - except KeyError: - pass - assert cwd + os.environ.pop(name, None) + cwd.ensure(dir=1) old = self.patchPATH() try: args[0] = self.getcommandpath(args[0], venv, cwd) - env = self._getenv(extraenv) - return action.popen(args, cwd=cwd, env=env, redirect=redirect, ignore_ret=ignore_ret) + env = self._getenv(testcommand=testcommand) + return action.popen(args, cwd=cwd, env=env, + redirect=redirect, ignore_ret=ignore_ret) finally: os.environ['PATH'] = old @@ -398,25 +388,3 @@ if not path.check(file=1): return "0" * 32 return path.computehash() - - -def hack_home_env(homedir, index_url=None): - # XXX HACK (this could also live with tox itself, consider) - # if tox uses pip on a package that requires setup_requires - # the index url set with pip is usually not recognized - # because it is setuptools executing very early. - # We therefore run the tox command in an artifical home - # directory and set .pydistutils.cfg and pip.conf files - # accordingly. - if not homedir.check(): - homedir.ensure(dir=1) - d = dict(HOME=str(homedir)) - if not index_url: - index_url = os.environ.get("TOX_INDEX_URL") - if index_url: - homedir.join(".pydistutils.cfg").write( - "[easy_install]\n" - "index_url = %s\n" % index_url) - d["PIP_INDEX_URL"] = index_url - d["TOX_INDEX_URL"] = index_url - return d https://bitbucket.org/hpk42/tox/commits/32306cad6374/ Changeset: 32306cad6374 Branch: passenv_multiline User: stefano-m Date: 2015-06-18 21:56:38+00:00 Summary: make passenv attribute type line-list Affected #: 2 files diff -r 84af17ba72801f740b12663e6f4cc56cbbb404eb -r 32306cad63746de7c48760d0021df8371b4269be tests/test_config.py --- a/tests/test_config.py +++ b/tests/test_config.py @@ -733,6 +733,7 @@ config = newconfig(""" [testenv] passenv = + # comment A123* B?23 """) assert len(config.envconfigs) == 1 @@ -754,20 +755,39 @@ def test_passenv_with_factor(self, tmpdir, newconfig, monkeypatch): monkeypatch.setenv("A123A", "a") monkeypatch.setenv("A123B", "b") + monkeypatch.setenv("A123C", "c") + monkeypatch.setenv("A123D", "d") monkeypatch.setenv("BX23", "0") + monkeypatch.setenv("CCA43", "3") + monkeypatch.setenv("CB21", "4") config = newconfig(""" [tox] envlist = {x1,x2} [testenv] passenv = - x1: A123A - x2: A123B + x1: A123A CC* + x1: CB21 + # passed to both environments + A123C + x2: A123B A123D """) assert len(config.envconfigs) == 2 + assert "A123A" in config.envconfigs["x1"].passenv + assert "A123C" in config.envconfigs["x1"].passenv + assert "CCA43" in config.envconfigs["x1"].passenv + assert "CB21" in config.envconfigs["x1"].passenv assert "A123B" not in config.envconfigs["x1"].passenv + assert "A123D" not in config.envconfigs["x1"].passenv + assert "BX23" not in config.envconfigs["x1"].passenv + assert "A123B" in config.envconfigs["x2"].passenv + assert "A123D" in config.envconfigs["x2"].passenv assert "A123A" not in config.envconfigs["x2"].passenv + assert "A123C" in config.envconfigs["x2"].passenv + assert "CCA43" not in config.envconfigs["x2"].passenv + assert "CB21" not in config.envconfigs["x2"].passenv + assert "BX23" not in config.envconfigs["x2"].passenv def test_changedir_override(self, tmpdir, newconfig): config = newconfig(""" diff -r 84af17ba72801f740b12663e6f4cc56cbbb404eb -r 32306cad63746de7c48760d0021df8371b4269be tox/config.py --- a/tox/config.py +++ b/tox/config.py @@ -381,10 +381,10 @@ help="list of X=Y lines with environment variable settings") def passenv(testenv_config, value): - if len(value) == 1 and "\n" in value[0]: - # If we have a list of 1 element that contains new lines, - # passenv has been specified as a multi line list. - value = value[0].split("\n") + # Flatten the list to deal with space-separated values. + value = list( + itertools.chain.from_iterable( + [x.split(' ') for x in value])) passenv = set(["PATH", "PIP_INDEX_URL", "LANG"]) @@ -407,7 +407,7 @@ return passenv parser.add_testenv_attribute( - name="passenv", type="space-separated-list", postprocess=passenv, + name="passenv", type="line-list", postprocess=passenv, help="environment variables needed during executing test commands " "(taken from invocation environment). Note that tox always " "passes through some basic environment variables which are " https://bitbucket.org/hpk42/tox/commits/37c6dac2b848/ Changeset: 37c6dac2b848 User: hpk42 Date: 2015-06-19 09:10:16+00:00 Summary: Merged in stefano-m/tox/passenv_multiline (pull request #166) Issue #259 passenv statement should accept multi-line list Affected #: 2 files diff -r 673d3f1f8d095cd8e1733506ffdc2f162b93e59b -r 37c6dac2b8484bb084dd6fff81fde108e5b96a4c tests/test_config.py --- a/tests/test_config.py +++ b/tests/test_config.py @@ -696,14 +696,45 @@ assert envconfig.setenv['ANOTHER_VAL'] == 'else' @pytest.mark.parametrize("plat", ["win32", "linux2"]) - def test_passenv(self, tmpdir, newconfig, monkeypatch, plat): + def test_passenv_as_multiline_list(self, tmpdir, newconfig, monkeypatch, plat): monkeypatch.setattr(sys, "platform", plat) monkeypatch.setenv("A123A", "a") monkeypatch.setenv("A123B", "b") monkeypatch.setenv("BX23", "0") config = newconfig(""" [testenv] - passenv = A123* B?23 + passenv = + A123* + # isolated comment + B?23 + """) + assert len(config.envconfigs) == 1 + envconfig = config.envconfigs['python'] + if plat == "win32": + assert "PATHEXT" in envconfig.passenv + assert "SYSTEMDRIVE" in envconfig.passenv + assert "SYSTEMROOT" in envconfig.passenv + assert "TEMP" in envconfig.passenv + assert "TMP" in envconfig.passenv + else: + assert "TMPDIR" in envconfig.passenv + assert "PATH" in envconfig.passenv + assert "PIP_INDEX_URL" in envconfig.passenv + assert "LANG" in envconfig.passenv + assert "A123A" in envconfig.passenv + assert "A123B" in envconfig.passenv + + @pytest.mark.parametrize("plat", ["win32", "linux2"]) + def test_passenv_as_space_separated_list(self, tmpdir, newconfig, monkeypatch, plat): + monkeypatch.setattr(sys, "platform", plat) + monkeypatch.setenv("A123A", "a") + monkeypatch.setenv("A123B", "b") + monkeypatch.setenv("BX23", "0") + config = newconfig(""" + [testenv] + passenv = + # comment + A123* B?23 """) assert len(config.envconfigs) == 1 envconfig = config.envconfigs['python'] @@ -724,20 +755,39 @@ def test_passenv_with_factor(self, tmpdir, newconfig, monkeypatch): monkeypatch.setenv("A123A", "a") monkeypatch.setenv("A123B", "b") + monkeypatch.setenv("A123C", "c") + monkeypatch.setenv("A123D", "d") monkeypatch.setenv("BX23", "0") + monkeypatch.setenv("CCA43", "3") + monkeypatch.setenv("CB21", "4") config = newconfig(""" [tox] envlist = {x1,x2} [testenv] passenv = - x1: A123A - x2: A123B + x1: A123A CC* + x1: CB21 + # passed to both environments + A123C + x2: A123B A123D """) assert len(config.envconfigs) == 2 + assert "A123A" in config.envconfigs["x1"].passenv + assert "A123C" in config.envconfigs["x1"].passenv + assert "CCA43" in config.envconfigs["x1"].passenv + assert "CB21" in config.envconfigs["x1"].passenv assert "A123B" not in config.envconfigs["x1"].passenv + assert "A123D" not in config.envconfigs["x1"].passenv + assert "BX23" not in config.envconfigs["x1"].passenv + assert "A123B" in config.envconfigs["x2"].passenv + assert "A123D" in config.envconfigs["x2"].passenv assert "A123A" not in config.envconfigs["x2"].passenv + assert "A123C" in config.envconfigs["x2"].passenv + assert "CCA43" not in config.envconfigs["x2"].passenv + assert "CB21" not in config.envconfigs["x2"].passenv + assert "BX23" not in config.envconfigs["x2"].passenv def test_changedir_override(self, tmpdir, newconfig): config = newconfig(""" diff -r 673d3f1f8d095cd8e1733506ffdc2f162b93e59b -r 37c6dac2b8484bb084dd6fff81fde108e5b96a4c tox/config.py --- a/tox/config.py +++ b/tox/config.py @@ -381,6 +381,11 @@ help="list of X=Y lines with environment variable settings") def passenv(testenv_config, value): + # Flatten the list to deal with space-separated values. + value = list( + itertools.chain.from_iterable( + [x.split(' ') for x in value])) + passenv = set(["PATH", "PIP_INDEX_URL", "LANG"]) # we ensure that tmp directory settings are passed on @@ -402,7 +407,7 @@ return passenv parser.add_testenv_attribute( - name="passenv", type="space-separated-list", postprocess=passenv, + name="passenv", type="line-list", postprocess=passenv, help="environment variables needed during executing test commands " "(taken from invocation environment). Note that tox always " "passes through some basic environment variables which are " Repository URL: https://bitbucket.org/hpk42/tox/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. _______________________________________________ pytest-commit mailing list pytest-commit@python.org https://mail.python.org/mailman/listinfo/pytest-commit