3 new commits in tox:

https://bitbucket.org/hpk42/tox/commits/f6fc0af4ceb5/
Changeset:   f6fc0af4ceb5
Branch:      issue285
User:        hpk42
Date:        2015-11-13 09:48:43+00:00
Summary:     add various xfailing test cases for env substitution in setenv
Affected #:  1 file

diff -r f6cca79ba7f6522893ab720e1a5d09ab38fd3543 -r 
f6fc0af4ceb5e0b313008ffe7f1a523ffe8d7fc1 tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -1598,6 +1598,47 @@
         self._check_hashseed(envconfigs["hash2"], '123456789')
 
 
+class TestSetenv:
+    @pytest.mark.xfail(reason="fix pending")
+    def test_setenv_uses_os_environ(self, tmpdir, newconfig, monkeypatch):
+        monkeypatch.setenv("X", "1")
+        config = newconfig("""
+            [testenv:env1]
+            setenv =
+                X = {env:X}
+        """)
+        assert config.envconfigs["env1"].setenv["X"] == "1"
+
+    @pytest.mark.xfail(reason="fix pending")
+    def test_setenv_default_os_environ(self, tmpdir, newconfig, monkeypatch):
+        monkeypatch.delenv("X", raising=False)
+        config = newconfig("""
+            [testenv:env1]
+            setenv =
+                X = {env:X:2}
+        """)
+        assert config.envconfigs["env1"].setenv["X"] == "2"
+
+    @pytest.mark.xfail(reason="fix pending")
+    def test_setenv_uses_other_setenv(self, tmpdir, newconfig):
+        config = newconfig("""
+            [testenv:env1]
+            setenv =
+                Y = 5
+                X = {env:Y}
+        """)
+        assert config.envconfigs["env1"].setenv["X"] == "5"
+
+    @pytest.mark.xfail(reason="fix pending")
+    def test_setenv_recursive_direct(self, tmpdir, newconfig):
+        config = newconfig("""
+            [testenv:env1]
+            setenv =
+                X = {env:X:3}
+        """)
+        assert config.envconfigs["env1"].setenv["X"] == "3"
+
+
 class TestIndexServer:
     def test_indexserver(self, tmpdir, newconfig):
         config = newconfig("""


https://bitbucket.org/hpk42/tox/commits/fd4af25ed954/
Changeset:   fd4af25ed954
Branch:      issue285
User:        hpk42
Date:        2015-11-13 09:48:45+00:00
Summary:     put replacing/substitution into own class
Affected #:  1 file

diff -r f6fc0af4ceb5e0b313008ffe7f1a523ffe8d7fc1 -r 
fd4af25ed9541539b969146305375064320666cd tox/config.py
--- a/tox/config.py
+++ b/tox/config.py
@@ -799,16 +799,6 @@
 is_section_substitution = re.compile("{\[[^{}\s]+\]\S+?}").match
 
 
-RE_ITEM_REF = re.compile(
-    r'''
-    (?<!\\)[{]
-    (?:(?P<sub_type>[^[:{}]+):)?    # optional sub_type for special rules
-    (?P<substitution_value>[^{}]*)  # substitution key
-    [}]
-    ''',
-    re.VERBOSE)
-
-
 class SectionReader:
     def __init__(self, section_name, cfgparser, fallbacksections=None, 
factors=()):
         self.section_name = section_name
@@ -888,11 +878,7 @@
             x = self._apply_factors(x)
 
         if replace and x and hasattr(x, 'replace'):
-            self._subststack.append((self.section_name, name))
-            try:
-                x = self._replace(x)
-            finally:
-                assert self._subststack.pop() == (self.section_name, name)
+            x = self._replace(x, name=name)
         # print "getstring", self.section_name, name, "returned", repr(x)
         return x
 
@@ -909,8 +895,61 @@
         lines = s.strip().splitlines()
         return '\n'.join(filter(None, map(factor_line, lines)))
 
+    def _replace(self, value, name=None, section_name=None):
+        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)
+        finally:
+            assert self._subststack.pop() == (section_name, name)
+
+
+class Replacer:
+    RE_ITEM_REF = re.compile(
+        r'''
+        (?<!\\)[{]
+        (?:(?P<sub_type>[^[:{}]+):)?    # optional sub_type for special rules
+        (?P<substitution_value>[^{}]*)  # substitution key
+        [}]
+        ''',
+        re.VERBOSE)
+
+
+    def __init__(self, reader, opt_replace_env=True):
+        self.reader = reader
+        self.opt_replace_env = opt_replace_env
+
+    def do_replace(self, x):
+        return self.RE_ITEM_REF.sub(self._replace_match, x)
+
+    def _replace_match(self, match):
+        g = match.groupdict()
+
+        # 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
+
+        try:
+            sub_type = g['sub_type']
+        except KeyError:
+            raise tox.exception.ConfigError(
+                "Malformed substitution; no substitution type provided")
+
+        if sub_type == "env":
+            assert self.opt_replace_env
+            return self._replace_env(match)
+        if sub_type != None:
+            raise tox.exception.ConfigError("No support for the %s 
substitution type" % sub_type)
+        return self._replace_substitution(match)
+
     def _replace_env(self, match):
-        env_list = self.getdict('setenv')
+        env_list = self.reader.getdict('setenv')
         match_value = match.group('substitution_value')
         if not match_value:
             raise tox.exception.ConfigError(
@@ -938,60 +977,26 @@
         if key.startswith("[") and "]" in key:
             i = key.find("]")
             section, item = key[1:i], key[i + 1:]
-            if section in self._cfg and item in self._cfg[section]:
-                if (section, item) in self._subststack:
+            cfg = self.reader._cfg
+            if section in cfg and item in cfg[section]:
+                if (section, item) in self.reader._subststack:
                     raise ValueError('%s already in %s' % (
-                        (section, item), self._subststack))
-                x = str(self._cfg[section][item])
-                self._subststack.append((section, item))
-                try:
-                    return self._replace(x)
-                finally:
-                    self._subststack.pop()
+                        (section, item), self.reader._subststack))
+                x = str(cfg[section][item])
+                return self.reader._replace(x, name=item, section_name=section)
 
         raise tox.exception.ConfigError(
             "substitution key %r not found" % key)
 
     def _replace_substitution(self, match):
         sub_key = match.group('substitution_value')
-        val = self._subs.get(sub_key, None)
+        val = self.reader._subs.get(sub_key, None)
         if val is None:
             val = self._substitute_from_other_section(sub_key)
         if py.builtin.callable(val):
             val = val()
         return str(val)
 
-    def _replace_match(self, match):
-        g = match.groupdict()
-
-        # 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
-
-        handlers = {
-            'env': self._replace_env,
-            None: self._replace_substitution,
-        }
-        try:
-            sub_type = g['sub_type']
-        except KeyError:
-            raise tox.exception.ConfigError(
-                "Malformed substitution; no substitution type provided")
-
-        try:
-            handler = handlers[sub_type]
-        except KeyError:
-            raise tox.exception.ConfigError("No support for the %s 
substitution type" % sub_type)
-
-        return handler(match)
-
-    def _replace(self, x):
-        if '{' in x:
-            return RE_ITEM_REF.sub(self._replace_match, x)
-        return x
 
 
 class _ArgvlistReader:


https://bitbucket.org/hpk42/tox/commits/a6d3e1956d85/
Changeset:   a6d3e1956d85
Branch:      issue285
User:        hpk42
Date:        2015-11-13 09:49:05+00:00
Summary:     address issue285 but not fully resolve it: setenv processing now 
works,
making all config tests pass except for one substitution issue which
requires yet some more logic, probably.
Affected #:  2 files

diff -r fd4af25ed9541539b969146305375064320666cd -r 
a6d3e1956d8516889b1ee3275257e397fe1dfd51 tests/test_config.py
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -283,18 +283,9 @@
            commands =
              ls {env:TEST}
         """)
-        reader = SectionReader("testenv:py27", config._cfg)
-        x = reader.getargvlist("commands")
-        assert x == [
-            "ls testvalue".split()
-        ]
-        assert x != [
-            "ls {env:TEST}".split()
-        ]
-        y = reader.getargvlist("setenv")
-        assert y == [
-            "TEST=testvalue".split()
-        ]
+        envconfig = config.envconfigs["py27"]
+        assert envconfig.commands == [["ls", "testvalue"]]
+        assert envconfig.setenv["TEST"] == "testvalue"
 
 
 class TestIniParser:
@@ -1599,7 +1590,19 @@
 
 
 class TestSetenv:
-    @pytest.mark.xfail(reason="fix pending")
+    def test_getdict_lazy(self, tmpdir, newconfig):
+        config = newconfig("""
+            [testenv:X]
+            key0 =
+                key1 = {env:X}
+                key2 = {env:X:1}
+        """)
+        envconfig = config.envconfigs["X"]
+        val = envconfig._reader.getdict_lazy("key0")
+        assert val == {"key1": "{env:X}",
+                       "key2": "{env:X:1}"}
+
+
     def test_setenv_uses_os_environ(self, tmpdir, newconfig, monkeypatch):
         monkeypatch.setenv("X", "1")
         config = newconfig("""
@@ -1609,7 +1612,6 @@
         """)
         assert config.envconfigs["env1"].setenv["X"] == "1"
 
-    @pytest.mark.xfail(reason="fix pending")
     def test_setenv_default_os_environ(self, tmpdir, newconfig, monkeypatch):
         monkeypatch.delenv("X", raising=False)
         config = newconfig("""
@@ -1619,7 +1621,6 @@
         """)
         assert config.envconfigs["env1"].setenv["X"] == "2"
 
-    @pytest.mark.xfail(reason="fix pending")
     def test_setenv_uses_other_setenv(self, tmpdir, newconfig):
         config = newconfig("""
             [testenv:env1]
@@ -1629,7 +1630,6 @@
         """)
         assert config.envconfigs["env1"].setenv["X"] == "5"
 
-    @pytest.mark.xfail(reason="fix pending")
     def test_setenv_recursive_direct(self, tmpdir, newconfig):
         config = newconfig("""
             [testenv:env1]

diff -r fd4af25ed9541539b969146305375064320666cd -r 
a6d3e1956d8516889b1ee3275257e397fe1dfd51 tox/config.py
--- a/tox/config.py
+++ b/tox/config.py
@@ -323,11 +323,39 @@
     parser.add_argument("args", nargs="*",
                         help="additional arguments available to command 
positional substitution")
 
-    # add various core venv interpreter attributes
     parser.add_testenv_attribute(
         name="envdir", type="path", default="{toxworkdir}/{envname}",
         help="venv directory")
 
+    # add various core venv interpreter attributes
+    def setenv(testenv_config, value):
+        setenv = value
+        reader = testenv_config._reader
+
+        # we need to resolve environment variable substitution
+
+        replacing = []  # for detecting direct recursion
+        def setenv_reader(name):
+            if name in setenv and name not in replacing:
+                return setenv[name]
+            return os.environ.get(name)
+        reader.set_envreader(setenv_reader)
+
+        for name, value in setenv.items():
+            replacing.append(name)
+            setenv[name] = reader._replace(value)
+            replacing.pop()
+
+        config = testenv_config.config
+        if "PYTHONHASHSEED" not in setenv and config.hashseed is not None:
+            setenv['PYTHONHASHSEED'] = config.hashseed
+        return setenv
+
+    parser.add_testenv_attribute(
+        name="setenv", type="dict_lazy", postprocess=setenv,
+        help="list of X=Y lines with environment variable settings")
+
+
     def basepython_default(testenv_config, value):
         if value is None:
             for f in testenv_config.factors:
@@ -385,17 +413,6 @@
         name="recreate", type="bool", default=False, postprocess=recreate,
         help="always recreate this test environment.")
 
-    def setenv(testenv_config, value):
-        setenv = value
-        config = testenv_config.config
-        if "PYTHONHASHSEED" not in setenv and config.hashseed is not None:
-            setenv['PYTHONHASHSEED'] = config.hashseed
-        return setenv
-
-    parser.add_testenv_attribute(
-        name="setenv", type="dict", postprocess=setenv,
-        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(
@@ -518,21 +535,15 @@
     @property
     def envbindir(self):
         """ path to directory where scripts/binaries reside. """
-        if (sys.platform == "win32"
-                and "jython" not in self.basepython
-                and "pypy" not in self.basepython):
+        if sys.platform == "win32":
             return self.envdir.join("Scripts")
         else:
             return self.envdir.join("bin")
 
     @property
     def envpython(self):
-        """ path to python/jython executable. """
-        if "jython" in str(self.basepython):
-            name = "jython"
-        else:
-            name = "python"
-        return self.envbindir.join(name)
+        """ path to python executable. """
+        return self.envbindir.join(self.basepython)
 
     # no @property to avoid early calling (see callable(subst[key]) checks)
     def envsitepackagesdir(self):
@@ -699,7 +710,7 @@
 
         for env_attr in config._testenv_attr:
             atype = env_attr.type
-            if atype in ("bool", "path", "string", "dict", "argv", "argvlist"):
+            if atype in ("bool", "path", "string", "dict", "dict_lazy", 
"argv", "argvlist"):
                 meth = getattr(reader, "get" + atype)
                 res = meth(env_attr.name, env_attr.default)
             elif atype == "space-separated-list":
@@ -807,6 +818,13 @@
         self.factors = factors
         self._subs = {}
         self._subststack = []
+        self._envreader = os.environ.get
+
+    def set_envreader(self, envreader):
+        self._envreader = envreader
+
+    def get_environ_value(self, name):
+        return self._envreader(name)
 
     def addsubstitutions(self, _posargs=None, **kw):
         self._subs.update(kw)
@@ -826,17 +844,24 @@
         return [x.strip() for x in s.split(sep) if x.strip()]
 
     def getdict(self, name, default=None, sep="\n"):
-        s = self.getstring(name, None)
-        if s is None:
+        value = self.getstring(name, None)
+        return self._getdict(value, default=default, sep=sep)
+
+    def getdict_lazy(self, name, default=None, sep="\n"):
+        value = self.getstring(name, None, replace="noenv")
+        return self._getdict(value, default=default, sep=sep)
+
+    def _getdict(self, value, default, sep):
+        if value is None:
             return default or {}
 
-        value = {}
-        for line in s.split(sep):
+        d = {}
+        for line in value.split(sep):
             if line.strip():
                 name, rest = line.split('=', 1)
-                value[name.strip()] = rest.strip()
+                d[name.strip()] = rest.strip()
 
-        return value
+        return d
 
     def getbool(self, name, default=None):
         s = self.getstring(name, default)
@@ -878,7 +903,7 @@
             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, opt_replace_env=(replace!="noenv"))
         # print "getstring", self.section_name, name, "returned", repr(x)
         return x
 
@@ -895,14 +920,14 @@
         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, 
opt_replace_env=True):
         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, 
opt_replace_env=opt_replace_env).do_replace(value)
         finally:
             assert self._subststack.pop() == (section_name, name)
 
@@ -918,7 +943,7 @@
         re.VERBOSE)
 
 
-    def __init__(self, reader, opt_replace_env=True):
+    def __init__(self, reader, opt_replace_env):
         self.reader = reader
         self.opt_replace_env = opt_replace_env
 
@@ -942,14 +967,14 @@
                 "Malformed substitution; no substitution type provided")
 
         if sub_type == "env":
-            assert self.opt_replace_env
-            return self._replace_env(match)
+            if self.opt_replace_env:
+                return self._replace_env(match)
+            return "{env:%s}" %(g["substitution_value"])
         if sub_type != None:
             raise tox.exception.ConfigError("No support for the %s 
substitution type" % sub_type)
         return self._replace_substitution(match)
 
     def _replace_env(self, match):
-        env_list = self.reader.getdict('setenv')
         match_value = match.group('substitution_value')
         if not match_value:
             raise tox.exception.ConfigError(
@@ -963,15 +988,14 @@
         else:
             envkey = match_value
 
-        if envkey not in os.environ and default is None:
-            if envkey not in env_list and default is None:
+        envvalue = self.reader.get_environ_value(envkey)
+        if envvalue is None:
+            if default is None:
                 raise tox.exception.ConfigError(
                     "substitution env:%r: unknown environment variable %r" %
                     (envkey, envkey))
-        if envkey in os.environ:
-            return os.environ.get(envkey, default)
-        else:
-            return env_list.get(envkey, default)
+            return default
+        return envvalue
 
     def _substitute_from_other_section(self, key):
         if key.startswith("[") and "]" in key:
@@ -983,7 +1007,8 @@
                     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,
+                                            
opt_replace_env=self.opt_replace_env)
 
         raise tox.exception.ConfigError(
             "substitution key %r not found" % key)

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

Reply via email to