Skip to content

Fix install_command ignored from TOML config#3724

Merged
gaborbernat merged 4 commits intotox-dev:mainfrom
worksbyfriday:fix-toml-install-command
Feb 17, 2026
Merged

Fix install_command ignored from TOML config#3724
gaborbernat merged 4 commits intotox-dev:mainfrom
worksbyfriday:fix-toml-install-command

Conversation

@worksbyfriday
Copy link
Contributor

Summary

Fixes #3574 — custom install_command was silently ignored when specified in tox.toml or pyproject.toml, always falling back to the default pip install command.

Root cause

Two bugs conspired to produce this behavior:

  1. get_base_sections placed base sections under the env namespace. It used test_env(b) which wraps the section name under env.*, but env_run_base and env_pkg_base are top-level sections — [env_run_base] in tox.toml and [tool.tox.env_run_base] in pyproject.toml. This caused the loader to navigate to env.env_run_base (which doesn't exist) and return None, so no settings from env_run_base were applied.

  2. The TOML replacement engine raised on unknown {...} placeholders. When Unroll processed strings like {packages} inside an install_command array, TomlReplaceLoader tried to resolve packages as a config key, failed with KeyError, and raised — causing the config loader to fall through to the default. The INI loader's equivalent (ReplaceReferenceIni) returns None for unresolvable references (keeping the original text), matching the documented contract of ReplaceReference.__call__.

Changes

  • toml_pyproject.py: get_base_sections now constructs sections with the core prefix (or None), correctly placing env_run_base/env_pkg_base at the top level rather than under env.
  • _replace.py: TomlReplaceLoader.__call__ returns None (instead of raising) when a KeyError occurs with no default, consistent with the INI loader and the ReplaceReference protocol.
  • Test: Parameterized test verifying install_command is respected from both tox.toml and pyproject.toml.
  • Changelog: Added docs/changelog/3574.bugfix.rst.

Test plan

  • New parameterized test (tox.toml and pyproject.toml variants)
  • All 27 show_config tests pass
  • All 143 config/loader/source tests pass
  • All 49 pip install tests pass

Copy link
Member

@gaborbernat gaborbernat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI failing.

@worksbyfriday
Copy link
Contributor Author

Addressed the review feedback: removed single-letter variables, compacted comments and the keys property, inlined the name temp var in get_base_sections. The type/type-min CI failures are pre-existing on main (ty can't resolve argcomplete).

@worksbyfriday worksbyfriday marked this pull request as draft February 17, 2026 16:40
@worksbyfriday
Copy link
Contributor Author

Converted to draft. Rebased on main — will mark ready once CI passes.

@worksbyfriday
Copy link
Contributor Author

CI update: all test jobs pass on all platforms (3.9-3.14, Ubuntu/Windows/macOS) and all type/type-min jobs pass. The only failures are test_provision_requires_ok timeouts on macOS 15 (3.13 + 3.14), which are a pre-existing flaky test — the same test times out on other PRs and main when macOS runners are slow.

@worksbyfriday worksbyfriday marked this pull request as ready for review February 17, 2026 17:05
@gaborbernat
Copy link
Member

CI update: all test jobs pass on all platforms (3.9-3.14, Ubuntu/Windows/macOS) and all type/type-min jobs pass. The only failures are test_provision_requires_ok timeouts on macOS 15 (3.13 + 3.14), which are a pre-existing flaky test — the same test times out on other PRs and main when macOS runners are slow.

Let's bump the timeout for those then, can be part of this PR.

worksbyfriday and others added 4 commits February 17, 2026 17:28
Two bugs prevented install_command from working in TOML configuration:

1. get_base_sections used test_env() for base sections (env_run_base,
   env_pkg_base), placing them under the env namespace. But base sections
   are top-level — env_run_base lives at [env_run_base] in tox.toml and
   [tool.tox.env_run_base] in pyproject.toml, not under [env].

2. The TOML replacement engine (Unroll) raised KeyError when encountering
   {packages} or {opts} placeholders in string values, because it tried
   to resolve them as config references. The INI loader returns None for
   unknown references (keeping the original text); the TOML loader raised
   instead. This caused the config loader to fall through to the default
   install_command.

Fixes tox-dev#3574.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use shell_cmd() for expected output so quoting matches the platform
(shlex.quote on Unix, list2cmdline on Windows).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@worksbyfriday worksbyfriday force-pushed the fix-toml-install-command branch from a8a2618 to adc65ff Compare February 17, 2026 17:28
@gaborbernat gaborbernat merged commit e84002a into tox-dev:main Feb 17, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: custom install_command is only respected from tox.ini, not pyproject.toml

2 participants