Skip to content

Commit b26fd5d

Browse files
authored
tox3: Support provision of tox 4 with the min_version option (#2714)
Fixes #2661
1 parent 3b83cd4 commit b26fd5d

File tree

8 files changed

+84
-26
lines changed

8 files changed

+84
-26
lines changed

.github/workflows/check.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: check
22
on:
33
push:
4-
branches: [master, 'test-me-*']
4+
branches: [legacy, 'test-me-*']
55
tags:
66
pull_request:
77
schedule:

docs/changelog/2661.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support provision of tox 4 with the ``min_version`` option - by :user:`hroncok`

docs/config.rst

+6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ Global settings are defined under the ``tox`` section as:
4343
When tox is invoked with the ``--no-provision`` flag,
4444
the provision won't be attempted, tox will fail instead.
4545

46+
.. versionchanged:: 3.27.2
47+
48+
``min_version`` has the same meaning and usage as ``minversion``
49+
to support a best effort provision of tox 4.
50+
51+
4652
.. conf:: requires ^ LIST of PEP-508
4753

4854
.. versionadded:: 3.2.0

src/tox/config/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,10 @@ def run(name, section, subs, config):
13441344
def handle_provision(self, config, reader):
13451345
config.requires = reader.getlist("requires")
13461346
config.minversion = reader.getstring("minversion", None)
1347+
# tox 4 prefers the min_version name of this option.
1348+
# We check is as well in case the config is intended for tox 4.
1349+
# This allows to make a *best effort* attempt to provision tox 4 from tox 3.
1350+
config.minversion = config.minversion or reader.getstring("min_version", None)
13471351
config.provision_tox_env = name = reader.getstring("provision_tox_env", ".tox")
13481352
min_version = "tox >= {}".format(config.minversion or Version(tox.__version__).public)
13491353
deps = self.ensure_requires_satisfied(config, config.requires, min_version)

tests/integration/test_provision_int.py

+29-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def test_provision_missing(initproj, cmd):
2525
"tox.ini": """\
2626
[tox]
2727
skipsdist=True
28-
minversion = 3.7.0
28+
minversion = 3.7.0,<4
2929
requires =
3030
setuptools == 40.6.3
3131
[testenv]
@@ -54,7 +54,7 @@ def test_provision_from_pyvenv(initproj, cmd, monkeypatch):
5454
"tox.ini": """\
5555
[tox]
5656
skipsdist=True
57-
minversion = 3.7.0
57+
minversion = 3.7.0,<4
5858
requires =
5959
setuptools == 40.6.3
6060
[testenv]
@@ -68,6 +68,31 @@ def test_provision_from_pyvenv(initproj, cmd, monkeypatch):
6868
assert ".tox/.tox/bin/python -m virtualenv" in result.out
6969

7070

71+
@pytest.mark.skipif(
72+
"sys.version_info < (3, 7)",
73+
reason="tox 4 only supports Python >= 3.7",
74+
)
75+
def test_provision_tox_4(initproj, cmd, monkeypatch):
76+
initproj(
77+
"pkg123-0.7",
78+
filedefs={
79+
"tox.ini": """\
80+
[tox]
81+
no_package=True
82+
min_version = 4
83+
[testenv]
84+
commands=python -c "import os; print('assert this')"
85+
""",
86+
},
87+
)
88+
result = cmd("-e", "py", "-vvv")
89+
# result.assert_success() has some assumptions about output that tox 4 doesn't follow
90+
assert result.ret == 0, result.output()
91+
assert "assert this" in result.out
92+
# this exact line is only in tox 3:
93+
assert " congratulations :)" not in result.out.splitlines()
94+
95+
7196
@pytest.mark.skipif(INFO.IS_PYPY, reason="TODO: process numbers work differently on pypy")
7297
@pytest.mark.skipif(
7398
"sys.platform == 'win32'",
@@ -83,7 +108,7 @@ def test_provision_interrupt_child(initproj, monkeypatch, capfd, signal_type):
83108
"tox.ini": """
84109
[tox]
85110
skipsdist=True
86-
minversion = 3.7.0
111+
minversion = 3.7.0,<4
87112
requires = setuptools == 40.6.3
88113
tox == 3.7.0
89114
[testenv:b]
@@ -144,7 +169,7 @@ def test_provision_race(initproj, cmd, monkeypatch):
144169
"tox.ini": """\
145170
[tox]
146171
skipsdist=True
147-
minversion = 3.7.0
172+
minversion = 3.7.0,<4
148173
requires =
149174
setuptools == 40.6.3
150175
[testenv]

tests/unit/session/test_parallel.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,7 @@ def invoke_tox_in_thread(thread_name, result_json):
272272
# needs to be process to have it's own stdout
273273
invoke_result[thread_name] = subprocess.check_output(
274274
[sys.executable, "-m", "tox", "-p", "all", "--result-json", str(result_json)],
275-
universal_newlines=True,
276-
)
275+
).decode("utf-8")
277276
except subprocess.CalledProcessError as exception:
278277
invoke_result[thread_name] = exception
279278

tests/unit/session/test_provision.py

+40-17
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,21 @@ def next_tox_major():
2727
return "10.0.0"
2828

2929

30-
def test_provision_min_version_is_requires(newconfig, next_tox_major):
30+
@pytest.fixture(scope="session", params=["minversion", "min_version"])
31+
def minversion_option(request):
32+
"""both possible names for the minversion config option"""
33+
return request.param
34+
35+
36+
def test_provision_min_version_is_requires(newconfig, minversion_option, next_tox_major):
3137
with pytest.raises(MissingRequirement) as context:
3238
newconfig(
3339
[],
3440
"""\
3541
[tox]
36-
minversion = {}
42+
{} = {}
3743
""".format(
44+
minversion_option,
3845
next_tox_major,
3946
),
4047
)
@@ -49,17 +56,20 @@ def test_provision_min_version_is_requires(newconfig, next_tox_major):
4956
assert config.ignore_basepython_conflict is False
5057

5158

52-
def test_provision_config_has_minversion_and_requires(newconfig, next_tox_major):
59+
def test_provision_config_has_minversion_and_requires(
60+
newconfig, minversion_option, next_tox_major
61+
):
5362
with pytest.raises(MissingRequirement) as context:
5463
newconfig(
5564
[],
5665
"""\
5766
[tox]
58-
minversion = {}
67+
{} = {}
5968
requires =
6069
setuptools > 2
6170
pip > 3
6271
""".format(
72+
minversion_option,
6373
next_tox_major,
6474
),
6575
)
@@ -89,17 +99,18 @@ def test_provision_tox_change_name(newconfig):
8999
assert config.provision_tox_env == "magic"
90100

91101

92-
def test_provision_basepython_global_only(newconfig, next_tox_major):
102+
def test_provision_basepython_global_only(newconfig, minversion_option, next_tox_major):
93103
"""we don't want to inherit basepython from global"""
94104
with pytest.raises(MissingRequirement) as context:
95105
newconfig(
96106
[],
97107
"""\
98108
[tox]
99-
minversion = {}
109+
{} = {}
100110
[testenv]
101111
basepython = what
102112
""".format(
113+
minversion_option,
103114
next_tox_major,
104115
),
105116
)
@@ -108,17 +119,18 @@ def test_provision_basepython_global_only(newconfig, next_tox_major):
108119
assert base_python == sys.executable
109120

110121

111-
def test_provision_basepython_local(newconfig, next_tox_major):
122+
def test_provision_basepython_local(newconfig, minversion_option, next_tox_major):
112123
"""however adhere to basepython when explicitly set"""
113124
with pytest.raises(MissingRequirement) as context:
114125
newconfig(
115126
[],
116127
"""\
117128
[tox]
118-
minversion = {}
129+
{} = {}
119130
[testenv:.tox]
120131
basepython = what
121132
""".format(
133+
minversion_option,
122134
next_tox_major,
123135
),
124136
)
@@ -199,14 +211,17 @@ def test_provision_does_not_fail_with_no_provision_no_reason(cmd, initproj, json
199211

200212

201213
@parametrize_json_path
202-
def test_provision_fails_with_no_provision_next_tox(cmd, initproj, next_tox_major, json_path):
214+
def test_provision_fails_with_no_provision_next_tox(
215+
cmd, initproj, minversion_option, next_tox_major, json_path
216+
):
203217
p = initproj(
204218
"test-0.1",
205219
{
206220
"tox.ini": """\
207221
[tox]
208-
minversion = {}
222+
{} = {}
209223
""".format(
224+
minversion_option,
210225
next_tox_major,
211226
)
212227
},
@@ -238,17 +253,21 @@ def test_provision_fails_with_no_provision_missing_requires(cmd, initproj, json_
238253

239254

240255
@parametrize_json_path
241-
def test_provision_does_not_fail_with_satisfied_requires(cmd, initproj, next_tox_major, json_path):
256+
def test_provision_does_not_fail_with_satisfied_requires(
257+
cmd, initproj, minversion_option, json_path
258+
):
242259
p = initproj(
243260
"test-0.1",
244261
{
245262
"tox.ini": """\
246263
[tox]
247-
minversion = 0
264+
{} = 0
248265
requires =
249266
setuptools > 2
250267
pip > 3
251-
"""
268+
""".format(
269+
minversion_option
270+
)
252271
},
253272
)
254273
result = cmd("--no-provision", *([json_path] if json_path else []))
@@ -257,17 +276,20 @@ def test_provision_does_not_fail_with_satisfied_requires(cmd, initproj, next_tox
257276

258277

259278
@parametrize_json_path
260-
def test_provision_fails_with_no_provision_combined(cmd, initproj, next_tox_major, json_path):
279+
def test_provision_fails_with_no_provision_combined(
280+
cmd, initproj, minversion_option, next_tox_major, json_path
281+
):
261282
p = initproj(
262283
"test-0.1",
263284
{
264285
"tox.ini": """\
265286
[tox]
266-
minversion = {}
287+
{} = {}
267288
requires =
268289
setuptools > 2
269290
pip > 3
270291
""".format(
292+
minversion_option,
271293
next_tox_major,
272294
)
273295
},
@@ -389,15 +411,16 @@ def space_path2url(path):
389411
return urljoin("file:", pathname2url(os.path.abspath(at_path)))
390412

391413

392-
def test_provision_does_not_occur_in_devenv(newconfig, next_tox_major):
414+
def test_provision_does_not_occur_in_devenv(newconfig, minversion_option, next_tox_major):
393415
"""Adding --devenv should not change the directory where provisioning occurs"""
394416
with pytest.raises(MissingRequirement) as context:
395417
newconfig(
396418
["--devenv", "my_devenv"],
397419
"""\
398420
[tox]
399-
minversion = {}
421+
{} = {}
400422
""".format(
423+
minversion_option,
401424
next_tox_major,
402425
),
403426
)

tox.ini

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ commands =
5757

5858
[testenv:coverage]
5959
description = [run locally after tests]: combine coverage data and create report;
60-
generates a diff coverage against origin/master (can be changed by setting DIFF_AGAINST env var)
60+
generates a diff coverage against origin/legacy (can be changed by setting DIFF_AGAINST env var)
6161
passenv =
6262
{[testenv]passenv}
6363
DIFF_AGAINST
@@ -73,7 +73,7 @@ commands =
7373
coverage report -m
7474
coverage xml -o {toxworkdir}/coverage.xml
7575
coverage html -d {toxworkdir}/htmlcov
76-
diff-cover --compare-branch {env:DIFF_AGAINST:origin/master} {toxworkdir}/coverage.xml
76+
diff-cover --compare-branch {env:DIFF_AGAINST:origin/legacy} {toxworkdir}/coverage.xml
7777
depends = py27, py35, py36, py37, py38, py39, py310, py311, pypy, pypy3
7878

7979
[testenv:docs]

0 commit comments

Comments
 (0)