diff options
author | Bernát Gábor <bgabor8@bloomberg.net> | 2019-05-15 17:30:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-15 17:30:50 +0100 |
commit | dd6619346306181c7d8313a86afa4ad7be6f2178 (patch) | |
tree | f7a7c5cd971a2fc894aff28ac7b001823f20cd9b | |
parent | bfd22c64acb181955e3e3c126af4aa8da3938828 (diff) | |
download | tox-git-dd6619346306181c7d8313a86afa4ad7be6f2178.tar.gz |
show config now shows all config, filter-able, contains host tox python and package versions (#1298)
and package versions
-rw-r--r-- | docs/changelog/1298.feature.rst | 11 | ||||
-rw-r--r-- | src/tox/config/__init__.py | 26 | ||||
-rw-r--r-- | src/tox/session/commands/show_config.py | 96 | ||||
-rw-r--r-- | tests/unit/config/test_config.py | 49 | ||||
-rw-r--r-- | tests/unit/session/test_show_config.py | 117 | ||||
-rw-r--r-- | tox.ini | 2 |
6 files changed, 215 insertions, 86 deletions
diff --git a/docs/changelog/1298.feature.rst b/docs/changelog/1298.feature.rst new file mode 100644 index 00000000..1375c507 --- /dev/null +++ b/docs/changelog/1298.feature.rst @@ -0,0 +1,11 @@ +``--showconfig`` overhaul: + +- now fully generated via the config parser, so anyone can load it by using the built-in python config parser +- the ``tox`` section contains all configuration data from config +- the ``tox`` section contains a ``host_python`` key detailing the path of the host python +- the ``tox:version`` section contains the versions of all packages tox depends on with their version +- passing ``-l`` now allows only listing default target envs +- allows showing config for a given set of tox environments only via the ``-e`` cli flag or the ``TOXENV`` environment + variable, in this case the ``tox`` and ``tox:version`` section is only shown if at least one verbosity flag is passed + +this should help inspecting the options. diff --git a/src/tox/config/__init__.py b/src/tox/config/__init__.py index 418cca9f..6551e0cd 100644 --- a/src/tox/config/__init__.py +++ b/src/tox/config/__init__.py @@ -392,7 +392,8 @@ def tox_addoption(parser): parser.add_argument( "--showconfig", action="store_true", - help="show configuration information for all environments. ", + help="show live configuration (by default all env, with -l only default targets," + " specific via TOXENV/-e)", ) parser.add_argument( "-l", @@ -1077,7 +1078,8 @@ class ParseIni(object): self.handle_provision(config, reader) self.parse_build_isolation(config, reader) - config.envlist, all_envs, config.envlist_default = self._getenvdata(reader, config) + res = self._getenvdata(reader, config) + config.envlist, all_envs, config.envlist_default, config.envlist_explicit = res # factors used in config or predefined known_factors = self._list_section_factors("testenv") @@ -1268,18 +1270,19 @@ class ParseIni(object): from_config = reader.getstring("envlist", replace=False) env_list = [] + envlist_explicit = False if (from_option and "ALL" in from_option) or ( not from_option and from_environ and "ALL" in from_environ.split(",") ): all_envs = self._getallenvs(reader) else: candidates = ( - os.environ.get(PARALLEL_ENV_VAR_KEY), - from_option, - from_environ, - from_config, + (os.environ.get(PARALLEL_ENV_VAR_KEY), True), + (from_option, True), + (from_environ, True), + (from_config, False), ) - env_str = next((i for i in candidates if i), []) + env_str, envlist_explicit = next(((i, e) for i, e in candidates if i), ([], False)) env_list = _split_env(env_str) all_envs = self._getallenvs(reader, env_list) @@ -1293,7 +1296,7 @@ class ParseIni(object): if config.isolated_build is True and package_env in env_list: msg = "isolated_build_env {} cannot be part of envlist".format(package_env) raise tox.exception.ConfigError(msg) - return env_list, all_envs, _split_env(from_config) + return env_list, all_envs, _split_env(from_config), envlist_explicit def _split_env(env): @@ -1353,21 +1356,22 @@ class DepConfig: self.name = name self.indexserver = indexserver - def __str__(self): + def __repr__(self): if self.indexserver: if self.indexserver.name == "default": return self.name return ":{}:{}".format(self.indexserver.name, self.name) return str(self.name) - __repr__ = __str__ - class IndexServerConfig: def __init__(self, name, url=None): self.name = name self.url = url + def __repr__(self): + return "IndexServerConfig(name={}, url={})".format(self.name, self.url) + is_section_substitution = re.compile(r"{\[[^{}\s]+\]\S+?}").match """Check value matches substitution form of referencing value from other section. diff --git a/src/tox/session/commands/show_config.py b/src/tox/session/commands/show_config.py index d588ac80..f307cc18 100644 --- a/src/tox/session/commands/show_config.py +++ b/src/tox/session/commands/show_config.py @@ -1,31 +1,77 @@ -import subprocess import sys +from collections import OrderedDict -from tox import reporter as report -from tox.version import __version__ +from six import StringIO +from six.moves import configparser + +from tox import reporter + +DO_NOT_SHOW_CONFIG_ATTRIBUTES = ( + "interpreters", + "envconfigs", + "envlist", + "pluginmanager", + "envlist_explicit", +) def show_config(config): - info_versions() - report.keyvalue("config-file:", config.option.configfile) - report.keyvalue("toxinipath: ", config.toxinipath) - report.keyvalue("toxinidir: ", config.toxinidir) - report.keyvalue("toxworkdir: ", config.toxworkdir) - report.keyvalue("setupdir: ", config.setupdir) - report.keyvalue("distshare: ", config.distshare) - report.keyvalue("skipsdist: ", config.skipsdist) - report.line("") - for envconfig in config.envconfigs.values(): - report.line("[testenv:{}]".format(envconfig.envname), bold=True) - for attr in config._parser._testenv_attr: - report.line(" {:<15} = {}".format(attr.name, getattr(envconfig, attr.name))) - - -def info_versions(): - versions = ["tox-{}".format(__version__)] - proc = subprocess.Popen( - (sys.executable, "-m", "virtualenv", "--version"), stdout=subprocess.PIPE + parser = configparser.ConfigParser() + + if not config.envlist_explicit or reporter.verbosity() >= reporter.Verbosity.INFO: + tox_info(config, parser) + version_info(parser) + tox_envs_info(config, parser) + + content = StringIO() + parser.write(content) + value = content.getvalue().rstrip() + reporter.verbosity0(value) + + +def tox_envs_info(config, parser): + if config.envlist_explicit: + env_list = config.envlist + elif config.option.listenvs: + env_list = config.envlist_default + else: + env_list = list(config.envconfigs.keys()) + for name in env_list: + env_config = config.envconfigs[name] + values = OrderedDict( + (attr.name, str(getattr(env_config, attr.name))) + for attr in config._parser._testenv_attr + ) + section = "testenv:{}".format(name) + set_section(parser, section, values) + + +def tox_info(config, parser): + info = OrderedDict( + (i, str(getattr(config, i))) + for i in sorted(dir(config)) + if not i.startswith("_") and i not in DO_NOT_SHOW_CONFIG_ATTRIBUTES ) - out, _ = proc.communicate() - versions.append("virtualenv-{}".format(out.decode("UTF-8").strip())) - report.keyvalue("tool-versions:", " ".join(versions)) + info["host_python"] = sys.executable + set_section(parser, "tox", info) + + +def version_info(parser): + import pkg_resources + + versions = OrderedDict() + visited = set() + to_visit = {"tox"} + while to_visit: + current = to_visit.pop() + visited.add(current) + current_dist = pkg_resources.get_distribution(current) + to_visit.update(i.name for i in current_dist.requires() if i.name not in visited) + versions[current] = current_dist.version + set_section(parser, "tox:versions", versions) + + +def set_section(parser, section, values): + parser.add_section(section) + for key, value in values.items(): + parser.set(section, key, value) diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index c64ec1be..206fde8d 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -2722,12 +2722,6 @@ class TestCmdInvocation: assert "some-repr" in version_info assert "1.0" in version_info - def test_config_specific_ini(self, tmpdir, cmd): - ini = tmpdir.ensure("hello.ini") - result = cmd("-c", ini, "--showconfig") - assert not result.ret - assert result.outlines[1] == "config-file: {}".format(ini) - def test_no_tox_ini(self, cmd, initproj): initproj("noini-0.5") result = cmd() @@ -2736,49 +2730,6 @@ class TestCmdInvocation: assert result.err == msg assert not result.out - def test_override_workdir(self, cmd, initproj): - baddir = "badworkdir-123" - gooddir = "overridden-234" - initproj( - "overrideworkdir-0.5", - filedefs={ - "tox.ini": """ - [tox] - toxworkdir={} - """.format( - baddir - ) - }, - ) - result = cmd("--workdir", gooddir, "--showconfig") - assert not result.ret - assert gooddir in result.out - assert baddir not in result.out - assert py.path.local(gooddir).check() - assert not py.path.local(baddir).check() - - def test_showconfig_with_force_dep_version(self, cmd, initproj): - initproj( - "force_dep_version", - filedefs={ - "tox.ini": """ - [tox] - - [testenv] - deps= - dep1==2.3 - dep2 - """ - }, - ) - result = cmd("--showconfig") - result.assert_success(is_run_test_env=False) - assert any(re.match(r".*deps.*dep1==2.3, dep2.*", l) for l in result.outlines) - # override dep1 specific version, and force version for dep2 - result = cmd("--showconfig", "--force-dep=dep1", "--force-dep=dep2==5.0") - result.assert_success(is_run_test_env=False) - assert any(re.match(r".*deps.*dep1, dep2==5.0.*", l) for l in result.outlines) - @pytest.mark.parametrize( "cli_args,run_envlist", diff --git a/tests/unit/session/test_show_config.py b/tests/unit/session/test_show_config.py new file mode 100644 index 00000000..1a01d1d8 --- /dev/null +++ b/tests/unit/session/test_show_config.py @@ -0,0 +1,117 @@ +import py +import pytest +from six import StringIO +from six.moves import configparser + + +def load_config(args, cmd): + result = cmd(*args) + result.assert_success(is_run_test_env=False) + parser = configparser.ConfigParser() + output = StringIO(result.out) + parser.readfp(output) + return parser + + +def test_showconfig_with_force_dep_version(cmd, initproj): + initproj( + "force_dep_version", + filedefs={ + "tox.ini": """ + [tox] + + [testenv] + deps= + dep1==2.3 + dep2 + """ + }, + ) + parser = load_config(("--showconfig",), cmd) + assert parser.get("testenv:python", "deps") == "[dep1==2.3, dep2]" + + parser = load_config(("--showconfig", "--force-dep=dep1", "--force-dep=dep2==5.0"), cmd) + assert parser.get("testenv:python", "deps") == "[dep1, dep2==5.0]" + + +@pytest.fixture() +def setup_mixed_conf(initproj): + initproj( + "force_dep_version", + filedefs={ + "tox.ini": """ + [tox] + envlist = py37,py27,pypi,docs + + [testenv:notincluded] + changedir = whatever + + [testenv:docs] + changedir = docs + """ + }, + ) + + +@pytest.mark.parametrize( + "args, expected", + [ + ( + ["--showconfig"], + [ + "tox", + "tox:versions", + "testenv:py37", + "testenv:py27", + "testenv:pypi", + "testenv:docs", + "testenv:notincluded", + ], + ), + ( + ["--showconfig", "-l"], + [ + "tox", + "tox:versions", + "testenv:py37", + "testenv:py27", + "testenv:pypi", + "testenv:docs", + ], + ), + (["--showconfig", "-e", "py37,py36"], ["testenv:py37", "testenv:py36"]), + ], + ids=["all", "default_only", "-e"], +) +def test_showconfig(cmd, setup_mixed_conf, args, expected): + parser = load_config(args, cmd) + found_sections = parser.sections() + assert found_sections == expected + + +def test_config_specific_ini(tmpdir, cmd): + ini = tmpdir.ensure("hello.ini") + output = load_config(("-c", ini, "--showconfig"), cmd) + assert output.get("tox", "toxinipath") == ini + + +def test_override_workdir(cmd, initproj): + baddir = "badworkdir-123" + gooddir = "overridden-234" + initproj( + "overrideworkdir-0.5", + filedefs={ + "tox.ini": """ + [tox] + toxworkdir={} + """.format( + baddir + ) + }, + ) + result = cmd("--workdir", gooddir, "--showconfig") + assert not result.ret + assert gooddir in result.out + assert baddir not in result.out + assert py.path.local(gooddir).check() + assert not py.path.local(baddir).check() @@ -20,7 +20,7 @@ setenv = PIP_DISABLE_VERSION_CHECK = 1 COVERAGE_FILE = {env:COVERAGE_FILE:{toxworkdir}/.coverage.{envname}} VIRTUALENV_NO_DOWNLOAD = 1 passenv = http_proxy https_proxy no_proxy SSL_CERT_FILE PYTEST_* -deps = pip == 19.0.3 +deps = pip == 19.1.1 extras = testing commands = pytest \ --cov "{envsitepackagesdir}/tox" \ |