From d01131158621d85f9b49aec2b5f01152d19dab5d Mon Sep 17 00:00:00 2001 From: Bernat Gabor Date: Wed, 15 May 2019 13:50:41 +0100 Subject: [PATCH] show config now shows all config, filter-able, contains host tox python and package versions --- docs/changelog/1298.feature.rst | 9 +++ src/tox/config/__init__.py | 21 ++++-- src/tox/session/commands/show_config.py | 95 ++++++++++++++++++------- tests/unit/config/test_config.py | 22 ------ tests/unit/session/test_show_config.py | 90 +++++++++++++++++++++++ tox.ini | 2 +- 6 files changed, 184 insertions(+), 55 deletions(-) create mode 100644 docs/changelog/1298.feature.rst create mode 100644 tests/unit/session/test_show_config.py diff --git a/docs/changelog/1298.feature.rst b/docs/changelog/1298.feature.rst new file mode 100644 index 0000000000..ce3b6ba759 --- /dev/null +++ b/docs/changelog/1298.feature.rst @@ -0,0 +1,9 @@ +``--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 diff --git a/src/tox/config/__init__.py b/src/tox/config/__init__.py index 418cca9fd9..8d13a3b294 100644 --- a/src/tox/config/__init__.py +++ b/src/tox/config/__init__.py @@ -1077,7 +1077,8 @@ def line_of_default_to_zero(section, name=None): 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 +1269,19 @@ def _getenvdata(self, reader, config): 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 +1295,7 @@ def _getenvdata(self, reader, config): 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): @@ -1368,6 +1370,11 @@ def __init__(self, name, url=None): self.name = name self.url = url + def __str__(self): + return "IndexServerConfig(name={}, url={})".format(self.name, self.url) + + __repr__ = __str__ + 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 d588ac80dc..b45a9f2ebf 100644 --- a/src/tox/session/commands/show_config.py +++ b/src/tox/session/commands/show_config.py @@ -1,31 +1,76 @@ -import subprocess import sys +from collections import OrderedDict -from tox import reporter as report -from tox.version import __version__ +from six import StringIO + +from tox import reporter + +try: + import ConfigParser +except ImportError: + # noinspection PyPep8Naming + import configparser as ConfigParser + +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 + ) + parser["testenv:{}".format(name)] = 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 + 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 + + parser["tox:versions"] = versions diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index c64ec1beaa..76cabe2972 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -2757,28 +2757,6 @@ def test_override_workdir(self, cmd, initproj): 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 0000000000..9cd4930cdc --- /dev/null +++ b/tests/unit/session/test_show_config.py @@ -0,0 +1,90 @@ +import re + +import pytest + +try: + import ConfigParser +except ImportError: + # noinspection PyPep8Naming + import configparser as ConfigParser + + +def test_showconfig_with_force_dep_version(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.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): + result = cmd(*args) + result.assert_success(is_run_test_env=False) + parser = ConfigParser.ConfigParser() + parser.read_string(result.out) + found_sections = parser.sections() + assert found_sections == expected diff --git a/tox.ini b/tox.ini index 02cab4c37a..dc192ae099 100644 --- a/tox.ini +++ b/tox.ini @@ -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" \