From 8a25fa6bf3e625c0b47659e1652ab55a79d4d1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Tue, 1 Sep 2020 16:48:10 +0100 Subject: [PATCH] Support environment files and comments in setenv (#1668) Signed-off-by: Bernat Gabor --- docs/changelog/1667.feature.rst | 1 + docs/config.rst | 8 ++++++ src/tox/config/__init__.py | 18 +++++++++---- tests/unit/config/test_config.py | 44 ++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 docs/changelog/1667.feature.rst diff --git a/docs/changelog/1667.feature.rst b/docs/changelog/1667.feature.rst new file mode 100644 index 000000000..1a3150509 --- /dev/null +++ b/docs/changelog/1667.feature.rst @@ -0,0 +1 @@ +Support for comments within ``setenv`` and environment files via the ``files|`` prefix. - by :user:`gaborbernat` diff --git a/docs/config.rst b/docs/config.rst index d5fdc2b04..d2d89b713 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -404,6 +404,14 @@ Complete list of settings that you can put into ``testenv*`` sections: setenv = PYTHONPATH = {env:PYTHONPATH}{:}{toxinidir} + .. versionadded:: 3.20 + + Support for comments. Lines starting with ``#`` are ignored. + + Support for environment files. Lines starting with the ``file|`` contain path to a environment + file to load. Rules within the environment file are the same as within the ``setenv`` + (same replacement and comment support). + .. conf:: passenv ^ SPACE-SEPARATED-GLOBNAMES .. versionadded:: 2.0 diff --git a/src/tox/config/__init__.py b/src/tox/config/__init__.py index b1032f8bc..197d14c34 100644 --- a/src/tox/config/__init__.py +++ b/src/tox/config/__init__.py @@ -1606,13 +1606,21 @@ def _getdict(self, value, default, sep, replace=True): if value is None or not replace: return default or {} - d = {} + env_values = {} for line in value.split(sep): if line.strip(): - name, rest = line.split("=", 1) - d[name.strip()] = rest.strip() - - return d + if line.startswith("#"): # comment lines are ignored + pass + elif line.startswith("file|"): # file markers contain paths to env files + file_path = line[5:].strip() + if os.path.exists(file_path): + with open(file_path, "rt") as file_handler: + content = file_handler.read() + env_values.update(self._getdict(content, "", sep, replace)) + else: + name, value = line.split("=", 1) + env_values[name.strip()] = value.strip() + return env_values def getfloat(self, name, default=None, replace=True): s = self.getstring(name, default, replace=replace) diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index e89c27a47..7eb2ffbc8 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -6,6 +6,7 @@ import py import pytest from pluggy import PluginManager +from six import PY2 import tox from tox.config import ( @@ -2668,6 +2669,49 @@ def test_setenv_cross_section_mixed(self, monkeypatch, newconfig): assert envconfig.setenv["NOT_TEST"] == "defaultvalue" assert envconfig.setenv["y"] == "7" + def test_setenv_comment(self, newconfig): + """Check that setenv ignores comments.""" + envconfig = newconfig( + """ + [testenv] + setenv = + # MAGIC = yes + """, + ).envconfigs["python"] + assert "MAGIC" not in envconfig.setenv + + @pytest.mark.parametrize( + "content, has_magic", + [ + (None, False), + ("\n", False), + ("#MAGIC = yes", False), + ("MAGIC=yes", True), + ("\nMAGIC = yes", True), + ], + ) + def test_setenv_env_file(self, newconfig, content, has_magic, tmp_path): + """Check that setenv handles env files.""" + env_path = tmp_path / ".env" if content else None + if content: + env_path.write_text(content.decode() if PY2 else content) + env_config = newconfig( + """ + [testenv] + setenv = + ALPHA = 1 + file| {} + """.format( + env_path, + ), + ).envconfigs["python"] + envs = env_config.setenv.definitions + assert envs["ALPHA"] == "1" + if has_magic: + assert envs["MAGIC"] == "yes" + else: + assert "MAGIC" not in envs + class TestIndexServer: def test_indexserver(self, newconfig):