Skip to content

Commit dd66193

Browse files
authored
show config now shows all config, filter-able, contains host tox python and package versions (#1298)
and package versions
1 parent bfd22c6 commit dd66193

File tree

6 files changed

+215
-86
lines changed

6 files changed

+215
-86
lines changed

docs/changelog/1298.feature.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
``--showconfig`` overhaul:
2+
3+
- now fully generated via the config parser, so anyone can load it by using the built-in python config parser
4+
- the ``tox`` section contains all configuration data from config
5+
- the ``tox`` section contains a ``host_python`` key detailing the path of the host python
6+
- the ``tox:version`` section contains the versions of all packages tox depends on with their version
7+
- passing ``-l`` now allows only listing default target envs
8+
- allows showing config for a given set of tox environments only via the ``-e`` cli flag or the ``TOXENV`` environment
9+
variable, in this case the ``tox`` and ``tox:version`` section is only shown if at least one verbosity flag is passed
10+
11+
this should help inspecting the options.

src/tox/config/__init__.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ def tox_addoption(parser):
392392
parser.add_argument(
393393
"--showconfig",
394394
action="store_true",
395-
help="show configuration information for all environments. ",
395+
help="show live configuration (by default all env, with -l only default targets,"
396+
" specific via TOXENV/-e)",
396397
)
397398
parser.add_argument(
398399
"-l",
@@ -1077,7 +1078,8 @@ def line_of_default_to_zero(section, name=None):
10771078
self.handle_provision(config, reader)
10781079

10791080
self.parse_build_isolation(config, reader)
1080-
config.envlist, all_envs, config.envlist_default = self._getenvdata(reader, config)
1081+
res = self._getenvdata(reader, config)
1082+
config.envlist, all_envs, config.envlist_default, config.envlist_explicit = res
10811083

10821084
# factors used in config or predefined
10831085
known_factors = self._list_section_factors("testenv")
@@ -1268,18 +1270,19 @@ def _getenvdata(self, reader, config):
12681270
from_config = reader.getstring("envlist", replace=False)
12691271

12701272
env_list = []
1273+
envlist_explicit = False
12711274
if (from_option and "ALL" in from_option) or (
12721275
not from_option and from_environ and "ALL" in from_environ.split(",")
12731276
):
12741277
all_envs = self._getallenvs(reader)
12751278
else:
12761279
candidates = (
1277-
os.environ.get(PARALLEL_ENV_VAR_KEY),
1278-
from_option,
1279-
from_environ,
1280-
from_config,
1280+
(os.environ.get(PARALLEL_ENV_VAR_KEY), True),
1281+
(from_option, True),
1282+
(from_environ, True),
1283+
(from_config, False),
12811284
)
1282-
env_str = next((i for i in candidates if i), [])
1285+
env_str, envlist_explicit = next(((i, e) for i, e in candidates if i), ([], False))
12831286
env_list = _split_env(env_str)
12841287
all_envs = self._getallenvs(reader, env_list)
12851288

@@ -1293,7 +1296,7 @@ def _getenvdata(self, reader, config):
12931296
if config.isolated_build is True and package_env in env_list:
12941297
msg = "isolated_build_env {} cannot be part of envlist".format(package_env)
12951298
raise tox.exception.ConfigError(msg)
1296-
return env_list, all_envs, _split_env(from_config)
1299+
return env_list, all_envs, _split_env(from_config), envlist_explicit
12971300

12981301

12991302
def _split_env(env):
@@ -1353,21 +1356,22 @@ def __init__(self, name, indexserver=None):
13531356
self.name = name
13541357
self.indexserver = indexserver
13551358

1356-
def __str__(self):
1359+
def __repr__(self):
13571360
if self.indexserver:
13581361
if self.indexserver.name == "default":
13591362
return self.name
13601363
return ":{}:{}".format(self.indexserver.name, self.name)
13611364
return str(self.name)
13621365

1363-
__repr__ = __str__
1364-
13651366

13661367
class IndexServerConfig:
13671368
def __init__(self, name, url=None):
13681369
self.name = name
13691370
self.url = url
13701371

1372+
def __repr__(self):
1373+
return "IndexServerConfig(name={}, url={})".format(self.name, self.url)
1374+
13711375

13721376
is_section_substitution = re.compile(r"{\[[^{}\s]+\]\S+?}").match
13731377
"""Check value matches substitution form of referencing value from other section.
Lines changed: 71 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,77 @@
1-
import subprocess
21
import sys
2+
from collections import OrderedDict
33

4-
from tox import reporter as report
5-
from tox.version import __version__
4+
from six import StringIO
5+
from six.moves import configparser
6+
7+
from tox import reporter
8+
9+
DO_NOT_SHOW_CONFIG_ATTRIBUTES = (
10+
"interpreters",
11+
"envconfigs",
12+
"envlist",
13+
"pluginmanager",
14+
"envlist_explicit",
15+
)
616

717

818
def show_config(config):
9-
info_versions()
10-
report.keyvalue("config-file:", config.option.configfile)
11-
report.keyvalue("toxinipath: ", config.toxinipath)
12-
report.keyvalue("toxinidir: ", config.toxinidir)
13-
report.keyvalue("toxworkdir: ", config.toxworkdir)
14-
report.keyvalue("setupdir: ", config.setupdir)
15-
report.keyvalue("distshare: ", config.distshare)
16-
report.keyvalue("skipsdist: ", config.skipsdist)
17-
report.line("")
18-
for envconfig in config.envconfigs.values():
19-
report.line("[testenv:{}]".format(envconfig.envname), bold=True)
20-
for attr in config._parser._testenv_attr:
21-
report.line(" {:<15} = {}".format(attr.name, getattr(envconfig, attr.name)))
22-
23-
24-
def info_versions():
25-
versions = ["tox-{}".format(__version__)]
26-
proc = subprocess.Popen(
27-
(sys.executable, "-m", "virtualenv", "--version"), stdout=subprocess.PIPE
19+
parser = configparser.ConfigParser()
20+
21+
if not config.envlist_explicit or reporter.verbosity() >= reporter.Verbosity.INFO:
22+
tox_info(config, parser)
23+
version_info(parser)
24+
tox_envs_info(config, parser)
25+
26+
content = StringIO()
27+
parser.write(content)
28+
value = content.getvalue().rstrip()
29+
reporter.verbosity0(value)
30+
31+
32+
def tox_envs_info(config, parser):
33+
if config.envlist_explicit:
34+
env_list = config.envlist
35+
elif config.option.listenvs:
36+
env_list = config.envlist_default
37+
else:
38+
env_list = list(config.envconfigs.keys())
39+
for name in env_list:
40+
env_config = config.envconfigs[name]
41+
values = OrderedDict(
42+
(attr.name, str(getattr(env_config, attr.name)))
43+
for attr in config._parser._testenv_attr
44+
)
45+
section = "testenv:{}".format(name)
46+
set_section(parser, section, values)
47+
48+
49+
def tox_info(config, parser):
50+
info = OrderedDict(
51+
(i, str(getattr(config, i)))
52+
for i in sorted(dir(config))
53+
if not i.startswith("_") and i not in DO_NOT_SHOW_CONFIG_ATTRIBUTES
2854
)
29-
out, _ = proc.communicate()
30-
versions.append("virtualenv-{}".format(out.decode("UTF-8").strip()))
31-
report.keyvalue("tool-versions:", " ".join(versions))
55+
info["host_python"] = sys.executable
56+
set_section(parser, "tox", info)
57+
58+
59+
def version_info(parser):
60+
import pkg_resources
61+
62+
versions = OrderedDict()
63+
visited = set()
64+
to_visit = {"tox"}
65+
while to_visit:
66+
current = to_visit.pop()
67+
visited.add(current)
68+
current_dist = pkg_resources.get_distribution(current)
69+
to_visit.update(i.name for i in current_dist.requires() if i.name not in visited)
70+
versions[current] = current_dist.version
71+
set_section(parser, "tox:versions", versions)
72+
73+
74+
def set_section(parser, section, values):
75+
parser.add_section(section)
76+
for key, value in values.items():
77+
parser.set(section, key, value)

tests/unit/config/test_config.py

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,12 +2722,6 @@ class MockEggInfo:
27222722
assert "some-repr" in version_info
27232723
assert "1.0" in version_info
27242724

2725-
def test_config_specific_ini(self, tmpdir, cmd):
2726-
ini = tmpdir.ensure("hello.ini")
2727-
result = cmd("-c", ini, "--showconfig")
2728-
assert not result.ret
2729-
assert result.outlines[1] == "config-file: {}".format(ini)
2730-
27312725
def test_no_tox_ini(self, cmd, initproj):
27322726
initproj("noini-0.5")
27332727
result = cmd()
@@ -2736,49 +2730,6 @@ def test_no_tox_ini(self, cmd, initproj):
27362730
assert result.err == msg
27372731
assert not result.out
27382732

2739-
def test_override_workdir(self, cmd, initproj):
2740-
baddir = "badworkdir-123"
2741-
gooddir = "overridden-234"
2742-
initproj(
2743-
"overrideworkdir-0.5",
2744-
filedefs={
2745-
"tox.ini": """
2746-
[tox]
2747-
toxworkdir={}
2748-
""".format(
2749-
baddir
2750-
)
2751-
},
2752-
)
2753-
result = cmd("--workdir", gooddir, "--showconfig")
2754-
assert not result.ret
2755-
assert gooddir in result.out
2756-
assert baddir not in result.out
2757-
assert py.path.local(gooddir).check()
2758-
assert not py.path.local(baddir).check()
2759-
2760-
def test_showconfig_with_force_dep_version(self, cmd, initproj):
2761-
initproj(
2762-
"force_dep_version",
2763-
filedefs={
2764-
"tox.ini": """
2765-
[tox]
2766-
2767-
[testenv]
2768-
deps=
2769-
dep1==2.3
2770-
dep2
2771-
"""
2772-
},
2773-
)
2774-
result = cmd("--showconfig")
2775-
result.assert_success(is_run_test_env=False)
2776-
assert any(re.match(r".*deps.*dep1==2.3, dep2.*", l) for l in result.outlines)
2777-
# override dep1 specific version, and force version for dep2
2778-
result = cmd("--showconfig", "--force-dep=dep1", "--force-dep=dep2==5.0")
2779-
result.assert_success(is_run_test_env=False)
2780-
assert any(re.match(r".*deps.*dep1, dep2==5.0.*", l) for l in result.outlines)
2781-
27822733

27832734
@pytest.mark.parametrize(
27842735
"cli_args,run_envlist",
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import py
2+
import pytest
3+
from six import StringIO
4+
from six.moves import configparser
5+
6+
7+
def load_config(args, cmd):
8+
result = cmd(*args)
9+
result.assert_success(is_run_test_env=False)
10+
parser = configparser.ConfigParser()
11+
output = StringIO(result.out)
12+
parser.readfp(output)
13+
return parser
14+
15+
16+
def test_showconfig_with_force_dep_version(cmd, initproj):
17+
initproj(
18+
"force_dep_version",
19+
filedefs={
20+
"tox.ini": """
21+
[tox]
22+
23+
[testenv]
24+
deps=
25+
dep1==2.3
26+
dep2
27+
"""
28+
},
29+
)
30+
parser = load_config(("--showconfig",), cmd)
31+
assert parser.get("testenv:python", "deps") == "[dep1==2.3, dep2]"
32+
33+
parser = load_config(("--showconfig", "--force-dep=dep1", "--force-dep=dep2==5.0"), cmd)
34+
assert parser.get("testenv:python", "deps") == "[dep1, dep2==5.0]"
35+
36+
37+
@pytest.fixture()
38+
def setup_mixed_conf(initproj):
39+
initproj(
40+
"force_dep_version",
41+
filedefs={
42+
"tox.ini": """
43+
[tox]
44+
envlist = py37,py27,pypi,docs
45+
46+
[testenv:notincluded]
47+
changedir = whatever
48+
49+
[testenv:docs]
50+
changedir = docs
51+
"""
52+
},
53+
)
54+
55+
56+
@pytest.mark.parametrize(
57+
"args, expected",
58+
[
59+
(
60+
["--showconfig"],
61+
[
62+
"tox",
63+
"tox:versions",
64+
"testenv:py37",
65+
"testenv:py27",
66+
"testenv:pypi",
67+
"testenv:docs",
68+
"testenv:notincluded",
69+
],
70+
),
71+
(
72+
["--showconfig", "-l"],
73+
[
74+
"tox",
75+
"tox:versions",
76+
"testenv:py37",
77+
"testenv:py27",
78+
"testenv:pypi",
79+
"testenv:docs",
80+
],
81+
),
82+
(["--showconfig", "-e", "py37,py36"], ["testenv:py37", "testenv:py36"]),
83+
],
84+
ids=["all", "default_only", "-e"],
85+
)
86+
def test_showconfig(cmd, setup_mixed_conf, args, expected):
87+
parser = load_config(args, cmd)
88+
found_sections = parser.sections()
89+
assert found_sections == expected
90+
91+
92+
def test_config_specific_ini(tmpdir, cmd):
93+
ini = tmpdir.ensure("hello.ini")
94+
output = load_config(("-c", ini, "--showconfig"), cmd)
95+
assert output.get("tox", "toxinipath") == ini
96+
97+
98+
def test_override_workdir(cmd, initproj):
99+
baddir = "badworkdir-123"
100+
gooddir = "overridden-234"
101+
initproj(
102+
"overrideworkdir-0.5",
103+
filedefs={
104+
"tox.ini": """
105+
[tox]
106+
toxworkdir={}
107+
""".format(
108+
baddir
109+
)
110+
},
111+
)
112+
result = cmd("--workdir", gooddir, "--showconfig")
113+
assert not result.ret
114+
assert gooddir in result.out
115+
assert baddir not in result.out
116+
assert py.path.local(gooddir).check()
117+
assert not py.path.local(baddir).check()

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ setenv = PIP_DISABLE_VERSION_CHECK = 1
2020
COVERAGE_FILE = {env:COVERAGE_FILE:{toxworkdir}/.coverage.{envname}}
2121
VIRTUALENV_NO_DOWNLOAD = 1
2222
passenv = http_proxy https_proxy no_proxy SSL_CERT_FILE PYTEST_*
23-
deps = pip == 19.0.3
23+
deps = pip == 19.1.1
2424
extras = testing
2525
commands = pytest \
2626
--cov "{envsitepackagesdir}/tox" \

0 commit comments

Comments
 (0)