Skip to content

fix(python): honour --tool when creating a venv - #11567

Merged
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:fix/venv-honour-tool-override
Aug 1, 2026
Merged

jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:fix/venv-honour-tool-override

Conversation

@JamBalaya56562

@JamBalaya56562 JamBalaya56562 commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #5281.

_.python.venv builds the venv from the first [tools] python entry even when the run selected a different one, so

mise run --tool python@3.12 test

silently executes under 3.13. @risu729 reduced it to this a year ago and it still reproduces on v2026.7.15:

[tools]
python = ["3.13", "3.12"]
[env._.python]
venv = { path = ".venv", create = true }
[tasks]
version = "python --version"
config mise run version mise run --tool python@3.12 version
with _.python.venv Python 3.13.14 Python 3.13.14
without it Python 3.13.14 Python 3.12.13

Cause

Not the version selection itself. EnvResults::venv resolves its own python/uv-only toolset — deliberately, to avoid the circular wait when unrelated backends re-enter config.env() during env resolution (the go: deadlock from #7059) — and that toolset is rebuilt from the config files. A --tool override only ever reaches a Toolset built by ToolsetBuilder::with_args; it is never written back to Config, so it cannot be seen from there.

Giving the venv a version-templated path makes the split visible. Under --tool python@3.12 the directory is named for the override while its pyvenv.cfg records the other interpreter:

$ cat .venv-3.12.13/pyvenv.cfg
home = .../installs/python/3.13.14
version = 3.13.14
command = .../installs/python/3.13.14/python.exe -m venv .../.venv-3.12.13

Template rendering saw the override; interpreter selection did not.

Fix

Toolset::env does have the right toolset, so it now passes it down via EnvResults::resolve_with_toolset, and the venv directive records the active python version.

The rebuild and its filter are unchanged — this only changes which entry is selected within the rebuilt toolset, so the deadlock it guards against is untouched. Passing the full toolset through to create_python_venv would not be safe: which_bin walks every backend's which, which is the re-entrancy path the filter exists to avoid.

The active version is kept in a separate field from the user-facing _.python.venv.python option, and is a preference rather than a request: if the config-derived toolset cannot offer it we fall back to the previous behaviour instead of failing. Without that, a --tool version absent from [tools] would start erroring where it used to work.

uv.rs already passed the real toolset to create_python_venv, so uv_venv_auto was never affected and is untouched — that path is the precedent this follows.

Out of scope

An existing venv is not rebuilt (if !venv.exists() && create). A venv cannot be retargeted after creation, so a matrix over interpreters still needs one venv per version — a templated path, or venv = { path = "...", python = "3.12" }, which I verified works today.

Verification

Built and tested on a fork before opening this, since the change touches the deadlock-sensitive path:

The two risks worth naming — reintroducing the deadlock, and changing venv PATH ordering — are both covered by existing tests that pass.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed Python virtual environment creation to honor the interpreter selected with the --tool python@... option.
    • Ensured environment configuration consistently uses the active Python version when resolving virtual environments.
  • Tests

    • Added regression coverage confirming both default and command-line-selected Python interpreters are used correctly.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 0561d5de-7aaa-4f96-bf8d-41a8c9aadc5a

📥 Commits

Reviewing files that changed from the base of the PR and between cbcc32d and 076c667.

📒 Files selected for processing (4)
  • e2e/core/test_python_venv_tool_override
  • src/config/env_directive/mod.rs
  • src/config/env_directive/venv.rs
  • src/toolset/toolset_env.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/toolset/toolset_env.rs
  • src/config/env_directive/venv.rs
  • e2e/core/test_python_venv_tool_override
  • src/config/env_directive/mod.rs

📝 Walkthrough

Walkthrough

The change passes the active Toolset through environment directive resolution. Python virtual-environment creation uses the active Python version when no explicit version is set. A regression test verifies CLI-selected Python overrides.

Changes

Python venv tool override

Layer / File(s) Summary
Propagate toolset context
src/config/env_directive/mod.rs, src/toolset/toolset_env.rs
EnvResults::resolve_with_toolset accepts an optional caller Toolset. load_post_env passes the current toolset into resolution.
Select the active Python version
src/config/env_directive/venv.rs
Python venv creation prefers the caller toolset’s active Python version and falls back to the first resolved version.
Verify CLI tool override
e2e/core/test_python_venv_tool_override
The end-to-end test verifies default Python selection and selection through --tool python@3.12.13 for a shared virtual environment.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant load_post_env
  participant EnvResults.resolve_with_toolset
  participant PythonVenv
  CLI->>load_post_env: select Python tool
  load_post_env->>EnvResults.resolve_with_toolset: pass current Toolset
  EnvResults.resolve_with_toolset->>PythonVenv: pass active Python
  PythonVenv-->>CLI: create venv with selected interpreter
Loading

Possibly related PRs

  • jdx/mise#11475: Both modify Python virtual-environment resolution and environment handling.
  • jdx/mise#11521: This change extends the same environment-directive and Python venv resolution code.

Poem

A rabbit hops through Python’s den,
And brings the chosen toolset in.
The venv hears the version call,
Then builds the right one for them all.
“CLI selects!” the bunny sings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the fix for honoring the Python tool override during virtual environment creation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR propagates the active toolset into environment resolution so Python virtual environments honor --tool selections.

  • Records the caller’s resolved Python version separately from the explicit _.python.venv.python option.
  • Selects the matching interpreter from the filtered Python/uv toolset using exact resolved-version equality.
  • Adds end-to-end coverage for default and overridden Python versions.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; exact resolved-version matching prevents the previously reported 3.12.0 versus 3.12.0a1 prefix collision.

Important Files Changed

Filename Overview
src/config/env_directive/venv.rs Uses the caller’s active Python as a preference during venv creation; exact equality resolves the previously reported prerelease prefix collision.
src/config/env_directive/mod.rs Adds optional caller-toolset context while preserving the existing resolution entry point for callers without a toolset.
src/toolset/toolset_env.rs Passes the resolved toolset into post-environment directive processing.
e2e/core/test_python_venv_tool_override Verifies that venv creation uses the default interpreter normally and the overridden interpreter with --tool.

Reviews (2): Last reviewed commit: "fix(python): honour --tool when creating..." | Re-trigger Greptile

Comment thread src/config/env_directive/venv.rs
`_.python.venv` builds the venv from the first `[tools] python` entry even
when the run selected a different one, so

    mise run --tool python@3.12 test

silently executes under 3.13. Reported in jdx#5281, where @risu729 reduced it to:

    [tools]
    python = ["3.13", "3.12"]
    [env._.python]
    venv = { path = ".venv", create = true }
    [tasks]
    version = "python --version"

`mise run version` and `mise run --tool python@3.12 version` both print the
3.13 patch. Without the venv directive the second prints 3.12.

The cause is not the version selection itself. `EnvResults::venv` resolves its
own python/uv-only toolset — deliberately, to avoid a circular wait when
unrelated backends re-enter `config.env()` during env resolution — and that
toolset is rebuilt from the config files. A `--tool` override only ever reaches
a `Toolset` built by `ToolsetBuilder::with_args`, never `Config`, so it cannot
be seen from there. Giving a venv a version-templated path makes the split
visible: the directory is named for the overridden version while its
`pyvenv.cfg` records the interpreter of the first `[tools]` entry.

`Toolset::env` does have the right toolset, so it now passes it down through
`EnvResults::resolve_with_toolset`, and the venv directive records the active
python version. The rebuild and its filter are unchanged, so the deadlock it
guards against is unaffected — only the selection within the rebuilt toolset
moves.

The active version is a preference, not a request: it is kept separate from the
user-facing `_.python.venv.python` option so that a `--tool` version absent from
`[tools]` falls back to the previous behaviour instead of failing. `uv.rs`
already passed the real toolset to `create_python_venv`, so `uv_venv_auto` was
never affected and is untouched.

Out of scope: an existing venv is not rebuilt (`if !venv.exists() && create`).
A venv cannot be retargeted after creation, so a matrix over interpreters still
needs one venv per version — via a templated path or `venv = { python = "..." }`.
@JamBalaya56562
JamBalaya56562 force-pushed the fix/venv-honour-tool-override branch from cbcc32d to 076c667 Compare July 31, 2026 22:51
@jdx
jdx merged commit 47b5182 into jdx:main Aug 1, 2026
29 checks passed
@JamBalaya56562
JamBalaya56562 deleted the fix/venv-honour-tool-override branch August 1, 2026 02:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants