Skip to content

Commit 2124458

Browse files
tjsmartgaborbernat
andauthored
Disallow command line environments which are not explicitly specified in the config file (#3089)
Co-authored-by: Bernát Gábor <[email protected]>
1 parent f516cb5 commit 2124458

File tree

9 files changed

+73
-6
lines changed

9 files changed

+73
-6
lines changed

docs/changelog/2858.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Disallow command line environments which are not explicitly specified in the config file - by :user:`tjsmart`.

docs/user_guide.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,3 +541,19 @@ create your virtual env for the developers.
541541
py310-lint -> [no description]
542542
py311-black -> [no description]
543543
py311-lint -> [no description]
544+
545+
Disallow command line environments which are not explicitly specified in the config file
546+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
547+
548+
Previously, any environment would be implicitly created even if no such environment was specified in the configuration
549+
file.For example, given this config:
550+
551+
.. code-block:: ini
552+
553+
[testenv:unit]
554+
deps = pytest
555+
commands = pytest
556+
557+
Running ``tox -e unit`` would run our tests but running ``tox -e unt`` or ``tox -e unti`` would ultimately succeed
558+
without running any tests. A special exception is made for environments starting in ``py*``. In the above example
559+
running ``tox -e py310`` would still function as intended.

src/tox/session/env_select.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ def _collect_names(self) -> Iterator[tuple[Iterable[str], bool]]:
152152
elif self._cli_envs.is_all:
153153
everything_active = True
154154
else:
155+
cli_envs_not_in_config = set(self._cli_envs) - set(self._state.conf)
156+
if cli_envs_not_in_config:
157+
# allow cli_envs matching ".pkg" and starting with "py" to be implicitly created.
158+
disallowed_cli_envs = [
159+
env for env in cli_envs_not_in_config if not env.startswith("py") and env not in (".pkg",)
160+
]
161+
if disallowed_cli_envs:
162+
msg = f"provided environments not found in configuration file: {disallowed_cli_envs}"
163+
raise HandledError(msg)
155164
yield self._cli_envs, True
156165
yield self._state.conf, everything_active
157166
label_envs = dict.fromkeys(chain.from_iterable(self._state.conf.core["labels"].values()))

tests/plugin/test_plugin.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,16 @@ def tox_env_teardown(tox_env: ToxEnv) -> None:
7676
plugins = tuple(v for v in locals().values() if callable(v) and hasattr(v, "tox_impl"))
7777
assert len(plugins) == 8
7878
register_inline_plugin(mocker, *plugins)
79-
project = tox_project({"tox.ini": "[testenv]\npackage=skip\ncommands=python -c 'print(1)'"})
79+
80+
tox_ini = """
81+
[tox]
82+
env_list=a,b
83+
[testenv]
84+
package=skip
85+
commands=python -c 'print(1)'
86+
env_list=a,b
87+
"""
88+
project = tox_project({"tox.ini": tox_ini})
8089
result = project.run("r", "-e", "a,b")
8190
result.assert_success()
8291
cmd = "print(1)" if sys.platform == "win32" else "'print(1)'"

tests/session/cmd/test_devenv.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
def test_devenv_fail_multiple_target(tox_project: ToxProjectCreator) -> None:
12-
outcome = tox_project({"tox.ini": ""}).run("d", "-e", "a,b")
12+
outcome = tox_project({"tox.ini": "[tox]\nenv_list=a,b"}).run("d", "-e", "a,b")
1313
outcome.assert_failed()
1414
msg = "ROOT: HandledError| exactly one target environment allowed in devenv mode but found a, b\n"
1515
outcome.assert_out_err(msg, "")

tests/session/cmd/test_parallel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def test_keyboard_interrupt(tox_project: ToxProjectCreator, demo_pkg_inline: Pat
141141
)
142142
cmd = ["-c", str(proj.path / "tox.ini"), "p", "-p", "1", "-e", f"py,py{sys.version_info[0]},dep"]
143143
process = Popen([sys.executable, "-m", "tox", *cmd], stdout=PIPE, stderr=PIPE, universal_newlines=True)
144-
while not marker.exists():
144+
while not marker.exists() and (process.poll() is None):
145145
sleep(0.05)
146146
process.send_signal(SIGINT)
147147
out, err = process.communicate()

tests/session/cmd/test_sequential.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ def test_platform_matches_run_env(tox_project: ToxProjectCreator) -> None:
394394
def test_platform_does_not_match_package_env(tox_project: ToxProjectCreator, demo_pkg_inline: Path) -> None:
395395
toml = (demo_pkg_inline / "pyproject.toml").read_text()
396396
build = (demo_pkg_inline / "build.py").read_text()
397-
ini = "[testenv]\npackage=wheel\n[testenv:.pkg]\nplatform=wrong_platform"
397+
ini = "[tox]\nenv_list=a,b\n[testenv]\npackage=wheel\n[testenv:.pkg]\nplatform=wrong_platform"
398398
proj = tox_project({"tox.ini": ini, "pyproject.toml": toml, "build.py": build})
399399
result = proj.run("r", "-e", "a,b")
400400
result.assert_failed() # tox run fails as all envs are skipped
@@ -430,7 +430,7 @@ def test_sequential_help(tox_project: ToxProjectCreator) -> None:
430430

431431

432432
def test_sequential_clears_pkg_at_most_once(tox_project: ToxProjectCreator, demo_pkg_inline: Path) -> None:
433-
project = tox_project({"tox.ini": ""})
433+
project = tox_project({"tox.ini": "[tox]\nenv_list=a,b"})
434434
result = project.run("r", "--root", str(demo_pkg_inline), "-e", "a,b", "-r")
435435
result.assert_success()
436436

tests/session/test_env_select.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,35 @@ def test_env_select_lazily_looks_at_envs() -> None:
132132
# late-assigning env should be reflected in env_selector
133133
state.conf.options.env = CliEnv("py")
134134
assert set(env_selector.iter()) == {"py"}
135+
136+
137+
def test_cli_env_can_be_specified_in_default(tox_project: ToxProjectCreator) -> None:
138+
proj = tox_project({"tox.ini": "[tox]\nenv_list=exists"})
139+
outcome = proj.run("r", "-e", "exists")
140+
outcome.assert_success()
141+
assert "exists" in outcome.out
142+
assert not outcome.err
143+
144+
145+
def test_cli_env_can_be_specified_in_additional_environments(tox_project: ToxProjectCreator) -> None:
146+
proj = tox_project({"tox.ini": "[testenv:exists]"})
147+
outcome = proj.run("r", "-e", "exists")
148+
outcome.assert_success()
149+
assert "exists" in outcome.out
150+
assert not outcome.err
151+
152+
153+
def test_cli_env_not_in_tox_config_fails(tox_project: ToxProjectCreator) -> None:
154+
proj = tox_project({"tox.ini": ""})
155+
outcome = proj.run("r", "-e", "does_not_exist")
156+
outcome.assert_failed(code=-2)
157+
assert "provided environments not found in configuration file: ['does_not_exist']" in outcome.out, outcome.out
158+
159+
160+
@pytest.mark.parametrize("env_name", ["py", "py310", ".pkg"])
161+
def test_allowed_implicit_cli_envs(env_name: str, tox_project: ToxProjectCreator) -> None:
162+
proj = tox_project({"tox.ini": ""})
163+
outcome = proj.run("r", "-e", env_name)
164+
outcome.assert_success()
165+
assert env_name in outcome.out
166+
assert not outcome.err

tests/tox_env/python/virtual_env/package/test_package_pyproject.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def test_pyproject_deps_static_with_dynamic( # noqa: PLR0913
228228

229229

230230
def test_pyproject_no_build_editable_fallback(tox_project: ToxProjectCreator, demo_pkg_inline: Path) -> None:
231-
proj = tox_project({"tox.ini": ""}, base=demo_pkg_inline)
231+
proj = tox_project({"tox.ini": "[tox]\nenv_list=a,b"}, base=demo_pkg_inline)
232232
execute_calls = proj.patch_execute(lambda r: 0 if "install" in r.run_id else None)
233233
result = proj.run("r", "-e", "a,b", "--notest", "--develop")
234234
result.assert_success()

0 commit comments

Comments
 (0)