Skip to content

Commit 473af4d

Browse files
committed
Ensure tox_extend_envs list can be read twice
Previously (PR #3591) a new hook point was added. The tests checked that it works well with the `tox config` and `tox list` commands. However, `tox run` turned out to have a problem — it would complain that there's no such env, when invoked: ```console ROOT: 170 E HandledError| provided environments not found in configuration file: pip-compile-tox-env-lock [tox/run.py:23] ``` Turned out, this was because the config object is being interated twice in some subcommands. This in turn iterates over the discovered additional ephemeral environments list object. The implementation passes an iterator into it and so when it's first accessed, it's exhausted and the second attempt does not give the same envs, causing inconsistency. The patch solves this by using `itertools.tee()`, making sure that the underlying iterable is always cached and it's possible to repeat iteration as many times as possible without loosing the data in the process.
1 parent e911788 commit 473af4d

File tree

3 files changed

+12
-4
lines changed

3 files changed

+12
-4
lines changed

src/tox/config/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import os
44
from collections import OrderedDict, defaultdict
5-
from itertools import chain
5+
from itertools import chain, tee
66
from pathlib import Path
77
from typing import TYPE_CHECKING, Any, Iterable, Iterator, Sequence, TypeVar
88

@@ -81,7 +81,7 @@ def src_path(self) -> Path:
8181

8282
def __iter__(self) -> Iterator[str]:
8383
""":return: an iterator that goes through existing environments"""
84-
return chain(self._src.envs(self.core), self._extra_envs)
84+
return chain(self._src.envs(self.core), chain.from_iterable(tee(self._extra_envs)))
8585

8686
def sections(self) -> Iterator[Section]:
8787
yield from self._src.sections()

src/tox/session/state.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import sys
4-
from itertools import chain
4+
from itertools import chain, tee
55
from typing import TYPE_CHECKING, Sequence
66

77
from tox.config.main import Config
@@ -20,7 +20,7 @@ class State:
2020
"""Runtime state holder."""
2121

2222
def __init__(self, options: Options, args: Sequence[str]) -> None:
23-
extended_envs = chain.from_iterable(MANAGER.tox_extend_envs())
23+
(extended_envs,) = tee(chain.from_iterable(MANAGER.tox_extend_envs()), 1)
2424
self.conf = Config.make(options.parsed, options.pos_args, options.source, extended_envs)
2525
self.conf.core.add_constant(
2626
keys=["on_platform"],

tests/plugin/test_inline.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def tox_extend_envs() -> tuple[str]:
4343
def tox_add_core_config(core_conf: ConfigSet, state: State) -> None: # noqa: ARG001
4444
in_memory_config_loader = MemoryLoader(
4545
base=["sentinel-base"],
46+
commands_pre=["sentinel-cmd"],
4647
description="sentinel-description",
4748
)
4849
state.conf.memory_seed_loaders[env_name].append(
@@ -59,3 +60,10 @@ def tox_add_core_config(core_conf: ConfigSet, state: State) -> None: # noqa: AR
5960
tox_config_result = project.run("config", "-e", "sentinel-env-name", "-qq")
6061
tox_config_result.assert_success()
6162
assert "base = sentinel-base" in tox_config_result.out
63+
64+
tox_run_result = project.run("run", "-e", "sentinel-env-name", "-q")
65+
tox_run_result.assert_failed()
66+
expected_cmd_lookup_error_txt = (
67+
"sentinel-env-name: Exception running subprocess [Errno 2] No such file or directory: 'sentinel-cmd'\n"
68+
)
69+
assert expected_cmd_lookup_error_txt in tox_run_result.out

0 commit comments

Comments
 (0)