Repository: tox-dev/tox
Pull request #597 (2017-09-01 22:35:21)

Don't attempt to parse dicts if not replacing
When reading in the config and processing dict content we were splitting
on = assuming we would always have key = value pairs. However in the
case where we don't want to replace because the testenv will not be used
this can result in ValueErrors as substitution variables are not string
key = value pairs.

Avoid all this by only attempting to process dict key = values if we are
replacing in the first place. Otherwise the content isn't needed and we
just return the default value or empty dict.

This should fix https://github.com/tox-dev/tox/issues/595

Thanks for contributing a PR!

Here's a quick checklist of what we'd like you to think off:

- [X] Make sure to include one or more tests for your change;
- [NA] if an enhancement PR please create docs and at best an example
- [X] Add yourself to `CONTRIBUTORS`;
- [X] make a descriptive Pull Request text (it will be used for changelog)




Patch:
diff --git a/tox/config.py b/tox/config.py
index a15b69bb..1fba5425 100755
--- a/tox/config.py
+++ b/tox/config.py
@@ -957,16 +957,17 @@ class SectionReader:
 
     def getdict(self, name, default=None, sep="\n", replace=True):
         value = self.getstring(name, None, replace=replace)
-        return self._getdict(value, default=default, sep=sep)
+        return self._getdict(value, default=default, sep=sep, replace=replace)
 
     def getdict_setenv(self, name, default=None, sep="\n", replace=True):
         value = self.getstring(name, None, replace=replace, crossonly=True)
-        definitions = self._getdict(value, default=default, sep=sep)
+        definitions = self._getdict(value, default=default, sep=sep,
+                                    replace=replace)
         self._setenv = SetenvDict(definitions, reader=self)
         return self._setenv
 
-    def _getdict(self, value, default, sep):
-        if value is None:
+    def _getdict(self, value, default, sep, replace=True):
+        if value is None or not replace:
             return default or {}
 
         d = {}
@@ -979,7 +980,7 @@ class SectionReader:
 
     def getbool(self, name, default=None, replace=True):
         s = self.getstring(name, default, replace=replace)
-        if not s:
+        if not s or not replace:
             s = default
         if s is None:
             raise KeyError("no config value [%s] %s found" % (


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
index 6a458dee..c363f925 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -345,15 +345,16 @@ class TestIniParserAgainstCommandsKey:
         assert envconfig.commands == [["echo", "bar"]]
 
     def test_reproduce_issue595(self, newconfig):
-        with pytest.raises(ValueError):
-            newconfig("""
-                [tox]
-                envlist = spam
-                [testenv]
-                setenv = DONTCARE = 0
-                [testenv:eggs]
-                setenv = {[testenv]setenv}
-            """)
+        config = newconfig("""
+            [tox]
+            envlist = spam
+            [testenv]
+            setenv = DONTCARE = 0
+            [testenv:eggs]
+            setenv = {[testenv]setenv}
+            sitepackages = {[testenv]sitepackages}
+        """)
+        assert config.envlist == ['spam']
 
 
 class TestIniParser: