Skip to content

Commit 113bcf8

Browse files
🐛 fix(toml): extract args from Command in ref replacement (#3863)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent fc50406 commit 113bcf8

4 files changed

Lines changed: 37 additions & 2 deletions

File tree

docs/changelog/3830.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Command-type configuration values like ``list_dependencies_command`` can now be referenced in TOML using the structured
2+
``{replace = "ref"}`` syntax. The reference automatically extracts the command's argument list for compatibility with
3+
TOML's ``list[list[str]]`` structure - by :user:`gaborbernat`.

docs/reference/config.rst

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1920,7 +1920,8 @@ Pip installer
19201920

19211921
The ``list_dependencies_command`` setting is used for listing the packages installed into the virtual environment.
19221922
This command will be executed only if executing on Continuous Integrations is detected (for example set environment
1923-
variable ``CI=1``) or if journal is active.
1923+
variable ``CI=1``) or if journal is active. In TOML configurations, reference this Command value using
1924+
``{replace = "ref"}`` with ``extend = true`` rather than string interpolation.
19241925

19251926
.. conf::
19261927
:keys: pip_pre
@@ -2164,6 +2165,18 @@ You can reference other configurations via the ``ref`` replacement. This can eit
21642165
The ``extend`` flag controls if after replacement the value should be replaced as is in the host structure (when flag is
21652166
false -- by default) or be extended into. This flag only operates when the host is a list.
21662167

2168+
When referencing Command-type configuration values (like ``list_dependencies_command``), the reference automatically
2169+
extracts the command's argument list, making it compatible with TOML's structured ``commands`` format. For example:
2170+
2171+
.. code-block:: toml
2172+
2173+
[tool.tox.env.a]
2174+
package = "skip"
2175+
commands = [[{ replace = "ref", env = "a", key = "list_dependencies_command", extend = true }]]
2176+
2177+
This expands the Command's args into the command list, avoiding the need for string interpolation which doesn't work
2178+
properly with Command values in TOML.
2179+
21672180
Positional argument reference
21682181
=============================
21692182

src/tox/config/loader/toml/_replace.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
replace_env,
2121
)
2222
from tox.config.loader.stringify import stringify
23+
from tox.config.types import Command
2324

2425
from ._validate import validate
2526

@@ -117,7 +118,10 @@ def __call__( # noqa: C901, PLR0912
117118

118119
def _replace_ref(self, value: dict[str, TomlTypes], depth: int, *, skip_str: bool = False) -> TomlTypes:
119120
if self.conf is not None and (env := value.get("env")) and (key := value.get("key")):
120-
return cast("TomlTypes", self.conf.get_env(cast("str", env))[cast("str", key)])
121+
result = self.conf.get_env(cast("str", env))[cast("str", key)]
122+
if isinstance(result, Command):
123+
return cast("TomlTypes", result.args)
124+
return cast("TomlTypes", result)
121125
if of := value.get("of"):
122126
validated_of = validate(of, list[str])
123127
loaded = self.loader.load_raw_from_root(self.loader.section.SEP.join(validated_of))

tests/config/source/test_toml_pyproject.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,3 +979,18 @@ def test_config_in_toml_handled_error_on_run(tox_project: ToxProjectCreator) ->
979979
assert "internal error" not in outcome.out
980980
assert "failed to load py.deps" in outcome.out
981981
assert "deps expected str, list[str], or list[Requirement]" in outcome.out
982+
983+
984+
def test_config_in_toml_replace_ref_command(tox_project: ToxProjectCreator) -> None:
985+
project = tox_project({
986+
"pyproject.toml": dedent("""
987+
[tool.tox.env.a]
988+
package = "skip"
989+
commands = [[{ replace = "ref", env = "a", key = "list_dependencies_command", extend = true }]]
990+
"""),
991+
})
992+
outcome = project.run("c", "-e", "a", "-k", "commands")
993+
outcome.assert_success()
994+
assert "python" in outcome.out
995+
assert "pip" in outcome.out
996+
assert "freeze" in outcome.out

0 commit comments

Comments
 (0)