From b26fd5d14dfbc54541c8612c8e936cc79f1f5e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 14 Dec 2022 23:42:49 +0100 Subject: [PATCH] tox3: Support provision of tox 4 with the min_version option (#2714) Fixes https://github.com/tox-dev/tox/issues/2661 --- .github/workflows/check.yml | 2 +- docs/changelog/2661.feature.rst | 1 + docs/config.rst | 6 +++ src/tox/config/__init__.py | 4 ++ tests/integration/test_provision_int.py | 33 ++++++++++++-- tests/unit/session/test_parallel.py | 3 +- tests/unit/session/test_provision.py | 57 +++++++++++++++++-------- tox.ini | 4 +- 8 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 docs/changelog/2661.feature.rst diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a52a041cc..b848af033 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,7 +1,7 @@ name: check on: push: - branches: [master, 'test-me-*'] + branches: [legacy, 'test-me-*'] tags: pull_request: schedule: diff --git a/docs/changelog/2661.feature.rst b/docs/changelog/2661.feature.rst new file mode 100644 index 000000000..15e287204 --- /dev/null +++ b/docs/changelog/2661.feature.rst @@ -0,0 +1 @@ +Support provision of tox 4 with the ``min_version`` option - by :user:`hroncok` diff --git a/docs/config.rst b/docs/config.rst index d3b28ff72..d71048cbb 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -43,6 +43,12 @@ Global settings are defined under the ``tox`` section as: When tox is invoked with the ``--no-provision`` flag, the provision won't be attempted, tox will fail instead. + .. versionchanged:: 3.27.2 + + ``min_version`` has the same meaning and usage as ``minversion`` + to support a best effort provision of tox 4. + + .. conf:: requires ^ LIST of PEP-508 .. versionadded:: 3.2.0 diff --git a/src/tox/config/__init__.py b/src/tox/config/__init__.py index 75b9ab92f..fdf6e62c1 100644 --- a/src/tox/config/__init__.py +++ b/src/tox/config/__init__.py @@ -1344,6 +1344,10 @@ def run(name, section, subs, config): def handle_provision(self, config, reader): config.requires = reader.getlist("requires") config.minversion = reader.getstring("minversion", None) + # tox 4 prefers the min_version name of this option. + # We check is as well in case the config is intended for tox 4. + # This allows to make a *best effort* attempt to provision tox 4 from tox 3. + config.minversion = config.minversion or reader.getstring("min_version", None) config.provision_tox_env = name = reader.getstring("provision_tox_env", ".tox") min_version = "tox >= {}".format(config.minversion or Version(tox.__version__).public) deps = self.ensure_requires_satisfied(config, config.requires, min_version) diff --git a/tests/integration/test_provision_int.py b/tests/integration/test_provision_int.py index f1763e494..ec6829785 100644 --- a/tests/integration/test_provision_int.py +++ b/tests/integration/test_provision_int.py @@ -25,7 +25,7 @@ def test_provision_missing(initproj, cmd): "tox.ini": """\ [tox] skipsdist=True - minversion = 3.7.0 + minversion = 3.7.0,<4 requires = setuptools == 40.6.3 [testenv] @@ -54,7 +54,7 @@ def test_provision_from_pyvenv(initproj, cmd, monkeypatch): "tox.ini": """\ [tox] skipsdist=True - minversion = 3.7.0 + minversion = 3.7.0,<4 requires = setuptools == 40.6.3 [testenv] @@ -68,6 +68,31 @@ def test_provision_from_pyvenv(initproj, cmd, monkeypatch): assert ".tox/.tox/bin/python -m virtualenv" in result.out +@pytest.mark.skipif( + "sys.version_info < (3, 7)", + reason="tox 4 only supports Python >= 3.7", +) +def test_provision_tox_4(initproj, cmd, monkeypatch): + initproj( + "pkg123-0.7", + filedefs={ + "tox.ini": """\ + [tox] + no_package=True + min_version = 4 + [testenv] + commands=python -c "import os; print('assert this')" + """, + }, + ) + result = cmd("-e", "py", "-vvv") + # result.assert_success() has some assumptions about output that tox 4 doesn't follow + assert result.ret == 0, result.output() + assert "assert this" in result.out + # this exact line is only in tox 3: + assert " congratulations :)" not in result.out.splitlines() + + @pytest.mark.skipif(INFO.IS_PYPY, reason="TODO: process numbers work differently on pypy") @pytest.mark.skipif( "sys.platform == 'win32'", @@ -83,7 +108,7 @@ def test_provision_interrupt_child(initproj, monkeypatch, capfd, signal_type): "tox.ini": """ [tox] skipsdist=True - minversion = 3.7.0 + minversion = 3.7.0,<4 requires = setuptools == 40.6.3 tox == 3.7.0 [testenv:b] @@ -144,7 +169,7 @@ def test_provision_race(initproj, cmd, monkeypatch): "tox.ini": """\ [tox] skipsdist=True - minversion = 3.7.0 + minversion = 3.7.0,<4 requires = setuptools == 40.6.3 [testenv] diff --git a/tests/unit/session/test_parallel.py b/tests/unit/session/test_parallel.py index e08ef1f0c..3fa2331ba 100644 --- a/tests/unit/session/test_parallel.py +++ b/tests/unit/session/test_parallel.py @@ -272,8 +272,7 @@ def invoke_tox_in_thread(thread_name, result_json): # needs to be process to have it's own stdout invoke_result[thread_name] = subprocess.check_output( [sys.executable, "-m", "tox", "-p", "all", "--result-json", str(result_json)], - universal_newlines=True, - ) + ).decode("utf-8") except subprocess.CalledProcessError as exception: invoke_result[thread_name] = exception diff --git a/tests/unit/session/test_provision.py b/tests/unit/session/test_provision.py index 24f7b3d80..0ebaf8af9 100644 --- a/tests/unit/session/test_provision.py +++ b/tests/unit/session/test_provision.py @@ -27,14 +27,21 @@ def next_tox_major(): return "10.0.0" -def test_provision_min_version_is_requires(newconfig, next_tox_major): +@pytest.fixture(scope="session", params=["minversion", "min_version"]) +def minversion_option(request): + """both possible names for the minversion config option""" + return request.param + + +def test_provision_min_version_is_requires(newconfig, minversion_option, next_tox_major): with pytest.raises(MissingRequirement) as context: newconfig( [], """\ [tox] - minversion = {} + {} = {} """.format( + minversion_option, next_tox_major, ), ) @@ -49,17 +56,20 @@ def test_provision_min_version_is_requires(newconfig, next_tox_major): assert config.ignore_basepython_conflict is False -def test_provision_config_has_minversion_and_requires(newconfig, next_tox_major): +def test_provision_config_has_minversion_and_requires( + newconfig, minversion_option, next_tox_major +): with pytest.raises(MissingRequirement) as context: newconfig( [], """\ [tox] - minversion = {} + {} = {} requires = setuptools > 2 pip > 3 """.format( + minversion_option, next_tox_major, ), ) @@ -89,17 +99,18 @@ def test_provision_tox_change_name(newconfig): assert config.provision_tox_env == "magic" -def test_provision_basepython_global_only(newconfig, next_tox_major): +def test_provision_basepython_global_only(newconfig, minversion_option, next_tox_major): """we don't want to inherit basepython from global""" with pytest.raises(MissingRequirement) as context: newconfig( [], """\ [tox] - minversion = {} + {} = {} [testenv] basepython = what """.format( + minversion_option, next_tox_major, ), ) @@ -108,17 +119,18 @@ def test_provision_basepython_global_only(newconfig, next_tox_major): assert base_python == sys.executable -def test_provision_basepython_local(newconfig, next_tox_major): +def test_provision_basepython_local(newconfig, minversion_option, next_tox_major): """however adhere to basepython when explicitly set""" with pytest.raises(MissingRequirement) as context: newconfig( [], """\ [tox] - minversion = {} + {} = {} [testenv:.tox] basepython = what """.format( + minversion_option, next_tox_major, ), ) @@ -199,14 +211,17 @@ def test_provision_does_not_fail_with_no_provision_no_reason(cmd, initproj, json @parametrize_json_path -def test_provision_fails_with_no_provision_next_tox(cmd, initproj, next_tox_major, json_path): +def test_provision_fails_with_no_provision_next_tox( + cmd, initproj, minversion_option, next_tox_major, json_path +): p = initproj( "test-0.1", { "tox.ini": """\ [tox] - minversion = {} + {} = {} """.format( + minversion_option, next_tox_major, ) }, @@ -238,17 +253,21 @@ def test_provision_fails_with_no_provision_missing_requires(cmd, initproj, json_ @parametrize_json_path -def test_provision_does_not_fail_with_satisfied_requires(cmd, initproj, next_tox_major, json_path): +def test_provision_does_not_fail_with_satisfied_requires( + cmd, initproj, minversion_option, json_path +): p = initproj( "test-0.1", { "tox.ini": """\ [tox] - minversion = 0 + {} = 0 requires = setuptools > 2 pip > 3 - """ + """.format( + minversion_option + ) }, ) 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 @parametrize_json_path -def test_provision_fails_with_no_provision_combined(cmd, initproj, next_tox_major, json_path): +def test_provision_fails_with_no_provision_combined( + cmd, initproj, minversion_option, next_tox_major, json_path +): p = initproj( "test-0.1", { "tox.ini": """\ [tox] - minversion = {} + {} = {} requires = setuptools > 2 pip > 3 """.format( + minversion_option, next_tox_major, ) }, @@ -389,15 +411,16 @@ def space_path2url(path): return urljoin("file:", pathname2url(os.path.abspath(at_path))) -def test_provision_does_not_occur_in_devenv(newconfig, next_tox_major): +def test_provision_does_not_occur_in_devenv(newconfig, minversion_option, next_tox_major): """Adding --devenv should not change the directory where provisioning occurs""" with pytest.raises(MissingRequirement) as context: newconfig( ["--devenv", "my_devenv"], """\ [tox] - minversion = {} + {} = {} """.format( + minversion_option, next_tox_major, ), ) diff --git a/tox.ini b/tox.ini index c2557528b..aad28783c 100644 --- a/tox.ini +++ b/tox.ini @@ -57,7 +57,7 @@ commands = [testenv:coverage] description = [run locally after tests]: combine coverage data and create report; - generates a diff coverage against origin/master (can be changed by setting DIFF_AGAINST env var) + generates a diff coverage against origin/legacy (can be changed by setting DIFF_AGAINST env var) passenv = {[testenv]passenv} DIFF_AGAINST @@ -73,7 +73,7 @@ commands = coverage report -m coverage xml -o {toxworkdir}/coverage.xml coverage html -d {toxworkdir}/htmlcov - diff-cover --compare-branch {env:DIFF_AGAINST:origin/master} {toxworkdir}/coverage.xml + diff-cover --compare-branch {env:DIFF_AGAINST:origin/legacy} {toxworkdir}/coverage.xml depends = py27, py35, py36, py37, py38, py39, py310, py311, pypy, pypy3 [testenv:docs]