From e7acbf7b5e4bfe155c203f7fc5dd01b86d60545c Mon Sep 17 00:00:00 2001 From: Tyler Smart Date: Thu, 10 Aug 2023 23:48:12 -0600 Subject: [PATCH 1/5] Disallow environments which are not explicitly specified in the config file Special exception is made for 'py*' which allows implicit creation of environments. --- src/tox/session/env_select.py | 8 +++++ tests/plugin/test_plugin.py | 11 ++++++- tests/session/cmd/test_devenv.py | 2 +- tests/session/cmd/test_parallel.py | 2 +- tests/session/cmd/test_sequential.py | 4 +-- tests/session/test_env_select.py | 32 +++++++++++++++++++ .../package/test_package_pyproject.py | 2 +- 7 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/tox/session/env_select.py b/src/tox/session/env_select.py index 127606b04..88973c9e8 100644 --- a/src/tox/session/env_select.py +++ b/src/tox/session/env_select.py @@ -152,6 +152,14 @@ def _collect_names(self) -> Iterator[tuple[Iterable[str], bool]]: elif self._cli_envs.is_all: everything_active = True else: + if cli_envs_not_in_config := (set(self._cli_envs) - set(self._state.conf)): + # allow cli_envs matching ".pkg" and starting with "py" to be implicitly created. + cli_envs_not_in_config = [ + env for env in cli_envs_not_in_config if not env.startswith("py") and env not in (".pkg",) + ] + if cli_envs_not_in_config: + msg = f"provided environments not found in configuration file: {cli_envs_not_in_config}" + raise HandledError(msg) yield self._cli_envs, True yield self._state.conf, everything_active label_envs = dict.fromkeys(chain.from_iterable(self._state.conf.core["labels"].values())) diff --git a/tests/plugin/test_plugin.py b/tests/plugin/test_plugin.py index 6c03ff5ca..32a8e9fdc 100644 --- a/tests/plugin/test_plugin.py +++ b/tests/plugin/test_plugin.py @@ -76,7 +76,16 @@ def tox_env_teardown(tox_env: ToxEnv) -> None: plugins = tuple(v for v in locals().values() if callable(v) and hasattr(v, "tox_impl")) assert len(plugins) == 8 register_inline_plugin(mocker, *plugins) - project = tox_project({"tox.ini": "[testenv]\npackage=skip\ncommands=python -c 'print(1)'"}) + + tox_ini = """ + [tox] + env_list=a,b + [testenv] + package=skip + commands=python -c 'print(1)' + env_list=a,b + """ + project = tox_project({"tox.ini": tox_ini}) result = project.run("r", "-e", "a,b") result.assert_success() cmd = "print(1)" if sys.platform == "win32" else "'print(1)'" diff --git a/tests/session/cmd/test_devenv.py b/tests/session/cmd/test_devenv.py index daef60000..4337596ff 100644 --- a/tests/session/cmd/test_devenv.py +++ b/tests/session/cmd/test_devenv.py @@ -9,7 +9,7 @@ def test_devenv_fail_multiple_target(tox_project: ToxProjectCreator) -> None: - outcome = tox_project({"tox.ini": ""}).run("d", "-e", "a,b") + outcome = tox_project({"tox.ini": "[tox]\nenv_list=a,b"}).run("d", "-e", "a,b") outcome.assert_failed() msg = "ROOT: HandledError| exactly one target environment allowed in devenv mode but found a, b\n" outcome.assert_out_err(msg, "") diff --git a/tests/session/cmd/test_parallel.py b/tests/session/cmd/test_parallel.py index 0dd3321f9..8ab93a787 100644 --- a/tests/session/cmd/test_parallel.py +++ b/tests/session/cmd/test_parallel.py @@ -141,7 +141,7 @@ def test_keyboard_interrupt(tox_project: ToxProjectCreator, demo_pkg_inline: Pat ) cmd = ["-c", str(proj.path / "tox.ini"), "p", "-p", "1", "-e", f"py,py{sys.version_info[0]},dep"] process = Popen([sys.executable, "-m", "tox", *cmd], stdout=PIPE, stderr=PIPE, universal_newlines=True) - while not marker.exists(): + while not marker.exists() and (process.poll() is None): sleep(0.05) process.send_signal(SIGINT) out, err = process.communicate() diff --git a/tests/session/cmd/test_sequential.py b/tests/session/cmd/test_sequential.py index 570864828..0e1875d07 100644 --- a/tests/session/cmd/test_sequential.py +++ b/tests/session/cmd/test_sequential.py @@ -394,7 +394,7 @@ def test_platform_matches_run_env(tox_project: ToxProjectCreator) -> None: def test_platform_does_not_match_package_env(tox_project: ToxProjectCreator, demo_pkg_inline: Path) -> None: toml = (demo_pkg_inline / "pyproject.toml").read_text() build = (demo_pkg_inline / "build.py").read_text() - ini = "[testenv]\npackage=wheel\n[testenv:.pkg]\nplatform=wrong_platform" + ini = "[tox]\nenv_list=a,b\n[testenv]\npackage=wheel\n[testenv:.pkg]\nplatform=wrong_platform" proj = tox_project({"tox.ini": ini, "pyproject.toml": toml, "build.py": build}) result = proj.run("r", "-e", "a,b") result.assert_failed() # tox run fails as all envs are skipped @@ -430,7 +430,7 @@ def test_sequential_help(tox_project: ToxProjectCreator) -> None: def test_sequential_clears_pkg_at_most_once(tox_project: ToxProjectCreator, demo_pkg_inline: Path) -> None: - project = tox_project({"tox.ini": ""}) + project = tox_project({"tox.ini": "[tox]\nenv_list=a,b"}) result = project.run("r", "--root", str(demo_pkg_inline), "-e", "a,b", "-r") result.assert_success() diff --git a/tests/session/test_env_select.py b/tests/session/test_env_select.py index 2833e230c..13de0ea66 100644 --- a/tests/session/test_env_select.py +++ b/tests/session/test_env_select.py @@ -132,3 +132,35 @@ def test_env_select_lazily_looks_at_envs() -> None: # late-assigning env should be reflected in env_selector state.conf.options.env = CliEnv("py") assert set(env_selector.iter()) == {"py"} + + +def test_cli_env_can_be_specified_in_default(tox_project: ToxProjectCreator) -> None: + proj = tox_project({"tox.ini": "[tox]\nenv_list=exists"}) + outcome = proj.run("r", "-e", "exists") + outcome.assert_success() + assert "exists" in outcome.out + assert not outcome.err + + +def test_cli_env_can_be_specified_in_additional_environments(tox_project: ToxProjectCreator) -> None: + proj = tox_project({"tox.ini": "[testenv:exists]"}) + outcome = proj.run("r", "-e", "exists") + outcome.assert_success() + assert "exists" in outcome.out + assert not outcome.err + + +def test_cli_env_not_in_tox_config_fails(tox_project: ToxProjectCreator) -> None: + proj = tox_project({"tox.ini": ""}) + outcome = proj.run("r", "-e", "does_not_exist") + outcome.assert_failed(code=-2) + assert "provided environments not found in configuration file: ['does_not_exist']" in outcome.out, outcome.out + + +@pytest.mark.parametrize("env_name", ["py", "py310", ".pkg"]) +def test_allowed_implicit_cli_envs(env_name: str, tox_project: ToxProjectCreator) -> None: + proj = tox_project({"tox.ini": ""}) + outcome = proj.run("r", "-e", env_name) + outcome.assert_success() + assert env_name in outcome.out + assert not outcome.err diff --git a/tests/tox_env/python/virtual_env/package/test_package_pyproject.py b/tests/tox_env/python/virtual_env/package/test_package_pyproject.py index 4e3b44559..26e74426c 100644 --- a/tests/tox_env/python/virtual_env/package/test_package_pyproject.py +++ b/tests/tox_env/python/virtual_env/package/test_package_pyproject.py @@ -228,7 +228,7 @@ def test_pyproject_deps_static_with_dynamic( # noqa: PLR0913 def test_pyproject_no_build_editable_fallback(tox_project: ToxProjectCreator, demo_pkg_inline: Path) -> None: - proj = tox_project({"tox.ini": ""}, base=demo_pkg_inline) + proj = tox_project({"tox.ini": "[tox]\nenv_list=a,b"}, base=demo_pkg_inline) execute_calls = proj.patch_execute(lambda r: 0 if "install" in r.run_id else None) result = proj.run("r", "-e", "a,b", "--notest", "--develop") result.assert_success() From 8c2af1de62631db9eba324b5cbd97f52053a494d Mon Sep 17 00:00:00 2001 From: Tyler Smart Date: Sun, 13 Aug 2023 15:56:21 -0600 Subject: [PATCH 2/5] Fix py37 tests --- src/tox/session/env_select.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tox/session/env_select.py b/src/tox/session/env_select.py index 88973c9e8..eba5df523 100644 --- a/src/tox/session/env_select.py +++ b/src/tox/session/env_select.py @@ -152,7 +152,8 @@ def _collect_names(self) -> Iterator[tuple[Iterable[str], bool]]: elif self._cli_envs.is_all: everything_active = True else: - if cli_envs_not_in_config := (set(self._cli_envs) - set(self._state.conf)): + cli_envs_not_in_config = set(self._cli_envs) - set(self._state.conf) + if cli_envs_not_in_config: # allow cli_envs matching ".pkg" and starting with "py" to be implicitly created. cli_envs_not_in_config = [ env for env in cli_envs_not_in_config if not env.startswith("py") and env not in (".pkg",) From 7aa17bb2aaab777b653850dc805fed5eca793c70 Mon Sep 17 00:00:00 2001 From: Tyler Smart Date: Sun, 13 Aug 2023 16:08:18 -0600 Subject: [PATCH 3/5] Fix mypy complaint --- src/tox/session/env_select.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tox/session/env_select.py b/src/tox/session/env_select.py index eba5df523..359f61b9c 100644 --- a/src/tox/session/env_select.py +++ b/src/tox/session/env_select.py @@ -155,11 +155,11 @@ def _collect_names(self) -> Iterator[tuple[Iterable[str], bool]]: cli_envs_not_in_config = set(self._cli_envs) - set(self._state.conf) if cli_envs_not_in_config: # allow cli_envs matching ".pkg" and starting with "py" to be implicitly created. - cli_envs_not_in_config = [ + disallowed_cli_envs = [ env for env in cli_envs_not_in_config if not env.startswith("py") and env not in (".pkg",) ] - if cli_envs_not_in_config: - msg = f"provided environments not found in configuration file: {cli_envs_not_in_config}" + if disallowed_cli_envs: + msg = f"provided environments not found in configuration file: {disallowed_cli_envs}" raise HandledError(msg) yield self._cli_envs, True yield self._state.conf, everything_active From d4ca1c3b841fe15a95add0b2eaf74b0cd0990385 Mon Sep 17 00:00:00 2001 From: Tyler Smart Date: Sun, 13 Aug 2023 16:23:14 -0600 Subject: [PATCH 4/5] Add changelog entry --- docs/changelog/2858.feature.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 docs/changelog/2858.feature.rst diff --git a/docs/changelog/2858.feature.rst b/docs/changelog/2858.feature.rst new file mode 100644 index 000000000..653da2a4d --- /dev/null +++ b/docs/changelog/2858.feature.rst @@ -0,0 +1,18 @@ +Disallow command line environments which are not explicitly specified in the config file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, any environment would be implicilty created even if no such environment +was specified in the configuration file. + +For example, given this config: + +.. code-block:: ini + [testenv:unit] + deps = pytest + commands = pytest + +Running `tox -e unit` would run our tests but running ``tox -e unt`` or ``tox -e unti`` +would ultimately succeed without running any tests. + +A special exception is made for environments starting in `py*`. In the above example +running `tox -e py310` would still function as intended. From d6f67a6569235fb0cfe4aab1f00ed8327d7b07d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Mon, 14 Aug 2023 09:27:12 -0700 Subject: [PATCH 5/5] PR Feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bernát Gábor --- docs/changelog/2858.feature.rst | 19 +------------------ docs/user_guide.rst | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/changelog/2858.feature.rst b/docs/changelog/2858.feature.rst index 653da2a4d..2381c5625 100644 --- a/docs/changelog/2858.feature.rst +++ b/docs/changelog/2858.feature.rst @@ -1,18 +1 @@ -Disallow command line environments which are not explicitly specified in the config file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Previously, any environment would be implicilty created even if no such environment -was specified in the configuration file. - -For example, given this config: - -.. code-block:: ini - [testenv:unit] - deps = pytest - commands = pytest - -Running `tox -e unit` would run our tests but running ``tox -e unt`` or ``tox -e unti`` -would ultimately succeed without running any tests. - -A special exception is made for environments starting in `py*`. In the above example -running `tox -e py310` would still function as intended. +Disallow command line environments which are not explicitly specified in the config file - by :user:`tjsmart`. diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 52d61b7f3..d08be62a8 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -541,3 +541,19 @@ create your virtual env for the developers. py310-lint -> [no description] py311-black -> [no description] py311-lint -> [no description] + +Disallow command line environments which are not explicitly specified in the config file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, any environment would be implicitly created even if no such environment was specified in the configuration +file.For example, given this config: + +.. code-block:: ini + + [testenv:unit] + deps = pytest + commands = pytest + +Running ``tox -e unit`` would run our tests but running ``tox -e unt`` or ``tox -e unti`` would ultimately succeed +without running any tests. A special exception is made for environments starting in ``py*``. In the above example +running ``tox -e py310`` would still function as intended.