Skip to content

🐛 fix(config): factor filter fallback to default value#3751

Merged
gaborbernat merged 2 commits intotox-dev:mainfrom
gaborbernat:fix-base-python-factor-conditional-3189
Feb 18, 2026
Merged

🐛 fix(config): factor filter fallback to default value#3751
gaborbernat merged 2 commits intotox-dev:mainfrom
gaborbernat:fix-base-python-factor-conditional-3189

Conversation

@gaborbernat
Copy link
Copy Markdown
Member

When using factor-conditional config values like base_python = py312: python3.12, environments whose factors don't match any conditional line silently received an empty value instead of the config default. 🐛 For base_python this meant the wrong Python interpreter was used — e.g. a py313 environment would quietly run with whatever Python tox itself was installed under, rather than the py313 spec derived from the env name.

The root cause is that INI factor filtering can reduce a non-empty value to an empty string when no factors match. This empty string was treated as a valid loaded value, preventing the default from being applied. The fix detects when factor filtering empties a value that originally had content and raises KeyError, allowing the loader to fall through to the default as intended.

This affects any factor-conditional config key, not just base_python. Previously, unmatched factor conditionals would produce empty values; now they correctly fall back to the configured or computed default.

Fixes #3189.

@gaborbernat gaborbernat added the bug:normal affects many people or has quite an impact label Feb 18, 2026
When using factor-conditional values like base_python = py312:
python3.12, environments without a matching factor got an empty
value instead of the config default. This caused py313 envs to
silently use the wrong Python interpreter.

The empty result from factor filtering was treated as a valid
value, preventing the default from being applied. Now when factor
filtering removes all content, a KeyError is raised so the loader
falls through to the default value.
When all lines in a setting are conditional and none match the current
environment, the setting falls back to its default value rather than
becoming empty. This was fixed in the accompanying code change but not
documented.
@gaborbernat gaborbernat force-pushed the fix-base-python-factor-conditional-3189 branch from d7e87c2 to 65f3f71 Compare February 18, 2026 07:09
@gaborbernat gaborbernat enabled auto-merge (squash) February 18, 2026 07:10
@gaborbernat gaborbernat merged commit e21679c into tox-dev:main Feb 18, 2026
28 checks passed
@gaborbernat gaborbernat deleted the fix-base-python-factor-conditional-3189 branch February 18, 2026 07:29
worksbyfriday added a commit to worksbyfriday/tox that referenced this pull request Feb 22, 2026
When {[section]key} references a value with factor-conditional lines and
no factors match, the substitution should resolve to empty string rather
than remaining unresolved as a literal reference.

The root cause: process_raw() raises KeyError when factor filtering empties
a value that originally had content (added in tox-dev#3751 to fix tox-dev#3189). For
same-section config values this is correct — it allows fallback to computed
defaults (e.g. base_python derived from env name). But for cross-section
references via SectionProxy, the KeyError propagated all the way up to
the replacer, which returned None (unresolved), leaving the literal
{[section]key} in the output.

The fix: when a SectionProxy lookup raises KeyError but the key exists in
the section, the factor filtering emptied the value — return empty string
instead of propagating the error. This preserves the tox-dev#3189 fix for
same-section config values while correctly handling cross-section references.

Fixes tox-dev#3809

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided bug:normal affects many people or has quite an impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

base_python settings may break ignore_base_python_conflict = false behaviour

1 participant