summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorholger krekel <holger@merlinux.eu>2015-12-14 12:01:15 +0100
committerholger krekel <holger@merlinux.eu>2015-12-14 12:01:15 +0100
commit8ddad172739638b930b0752e40f9fba681005aee (patch)
tree83f0ba1f60dcae8848c41507851c810fe0115929
parentbbaf73824cfe0c5268aecc44cd9e61cceac9996d (diff)
downloadtox-8ddad172739638b930b0752e40f9fba681005aee.tar.gz
fix issue294: re-allow cross-section substitution for setenv setting.2.3.1
-rw-r--r--CHANGELOG7
-rw-r--r--setup.py2
-rw-r--r--tests/test_config.py33
-rw-r--r--tox/__init__.py2
-rw-r--r--tox/config.py27
5 files changed, 56 insertions, 15 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 695300e..b41666e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,9 @@
-2.3.0 (unreleased)
+2.3.1
+-----
+
+- fix issue294: re-allow cross-section substitution for setenv.
+
+2.3.0
-----
- DEPRECATE use of "indexservers" in tox.ini. It complicates
diff --git a/setup.py b/setup.py
index a1640f9..cd5d79f 100644
--- a/setup.py
+++ b/setup.py
@@ -48,7 +48,7 @@ def main():
description='virtualenv-based automation of test activities',
long_description=open("README.rst").read(),
url='http://tox.testrun.org/',
- version='2.3.0.dev3',
+ version='2.3.1',
license='http://opensource.org/licenses/MIT',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
author='holger krekel',
diff --git a/tests/test_config.py b/tests/test_config.py
index f727a1f..6176641 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -1672,8 +1672,7 @@ class TestSetenv:
assert envconfig.setenv['VAL'] == envconfig.envdir
assert str(envconfig.envdir) in envconfig.commands[0]
- @pytest.mark.xfail(reason="we don't implement cross-section substitution for setenv")
- def test_setenv_cross_section_subst(self, monkeypatch, newconfig):
+ def test_setenv_cross_section_subst_issue294(self, monkeypatch, newconfig):
"""test that we can do cross-section substitution with setenv"""
monkeypatch.delenv('TEST', raising=False)
config = newconfig("""
@@ -1687,6 +1686,36 @@ class TestSetenv:
envconfig = config.envconfigs["python"]
assert envconfig.setenv["NOT_TEST"] == "defaultvalue"
+ def test_setenv_cross_section_subst_twice(self, monkeypatch, newconfig):
+ """test that we can do cross-section substitution with setenv"""
+ monkeypatch.delenv('TEST', raising=False)
+ config = newconfig("""
+ [section]
+ x = NOT_TEST={env:TEST:defaultvalue}
+ [section1]
+ y = {[section]x}
+
+ [testenv]
+ setenv = {[section1]y}
+ """)
+ envconfig = config.envconfigs["python"]
+ assert envconfig.setenv["NOT_TEST"] == "defaultvalue"
+
+ def test_setenv_cross_section_mixed(self, monkeypatch, newconfig):
+ """test that we can do cross-section substitution with setenv"""
+ monkeypatch.delenv('TEST', raising=False)
+ config = newconfig("""
+ [section]
+ x = NOT_TEST={env:TEST:defaultvalue}
+
+ [testenv]
+ setenv = {[section]x}
+ y = 7
+ """)
+ envconfig = config.envconfigs["python"]
+ assert envconfig.setenv["NOT_TEST"] == "defaultvalue"
+ assert envconfig.setenv["y"] == "7"
+
class TestIndexServer:
def test_indexserver(self, tmpdir, newconfig):
diff --git a/tox/__init__.py b/tox/__init__.py
index 0e69ae9..053386b 100644
--- a/tox/__init__.py
+++ b/tox/__init__.py
@@ -1,5 +1,5 @@
#
-__version__ = '2.3.0.dev3'
+__version__ = '2.3.1'
from .hookspecs import hookspec, hookimpl # noqa
diff --git a/tox/config.py b/tox/config.py
index ad80269..079dff3 100644
--- a/tox/config.py
+++ b/tox/config.py
@@ -889,7 +889,7 @@ class SectionReader:
return self._getdict(value, default=default, sep=sep)
def getdict_setenv(self, name, default=None, sep="\n"):
- value = self.getstring(name, None, replace=False)
+ value = self.getstring(name, None, replace=True, crossonly=True)
definitions = self._getdict(value, default=default, sep=sep)
self._setenv = SetenvDict(definitions, reader=self)
return self._setenv
@@ -931,7 +931,7 @@ class SectionReader:
def getargv(self, name, default=""):
return self.getargvlist(name, default)[0]
- def getstring(self, name, default=None, replace=True):
+ def getstring(self, name, default=None, replace=True, crossonly=False):
x = None
for s in [self.section_name] + self.fallbacksections:
try:
@@ -946,7 +946,7 @@ class SectionReader:
x = self._apply_factors(x)
if replace and x and hasattr(x, 'replace'):
- x = self._replace(x, name=name)
+ x = self._replace(x, name=name, crossonly=crossonly)
# print "getstring", self.section_name, name, "returned", repr(x)
return x
@@ -963,14 +963,14 @@ class SectionReader:
lines = s.strip().splitlines()
return '\n'.join(filter(None, map(factor_line, lines)))
- def _replace(self, value, name=None, section_name=None):
+ def _replace(self, value, name=None, section_name=None, crossonly=False):
if '{' not in value:
return value
section_name = section_name if section_name else self.section_name
self._subststack.append((section_name, name))
try:
- return Replacer(self).do_replace(value)
+ return Replacer(self, crossonly=crossonly).do_replace(value)
finally:
assert self._subststack.pop() == (section_name, name)
@@ -982,22 +982,28 @@ class Replacer:
(?:(?P<sub_type>[^[:{}]+):)? # optional sub_type for special rules
(?P<substitution_value>[^{}]*) # substitution key
[}]
- ''',
- re.VERBOSE)
+ ''', re.VERBOSE)
- def __init__(self, reader):
+ def __init__(self, reader, crossonly=False):
self.reader = reader
+ self.crossonly = crossonly
def do_replace(self, x):
return self.RE_ITEM_REF.sub(self._replace_match, x)
def _replace_match(self, match):
g = match.groupdict()
+ sub_value = g['substitution_value']
+ if self.crossonly:
+ if sub_value.startswith("["):
+ return self._substitute_from_other_section(sub_value)
+ # in crossonly we return all other hits verbatim
+ start, end = match.span()
+ return match.string[start:end]
# special case: opts and packages. Leave {opts} and
# {packages} intact, they are replaced manually in
# _venv.VirtualEnv.run_install_command.
- sub_value = g['substitution_value']
if sub_value in ('opts', 'packages'):
return '{%s}' % sub_value
@@ -1048,7 +1054,8 @@ class Replacer:
raise ValueError('%s already in %s' % (
(section, item), self.reader._subststack))
x = str(cfg[section][item])
- return self.reader._replace(x, name=item, section_name=section)
+ return self.reader._replace(x, name=item, section_name=section,
+ crossonly=self.crossonly)
raise tox.exception.ConfigError(
"substitution key %r not found" % key)