Detect python version from python project by default in uv venv#5592
Conversation
crates/uv/src/commands/venv.rs
Outdated
| let project = match VirtualProject::discover( | ||
| &std::env::current_dir().into_diagnostic()?, | ||
| &DiscoveryOptions::default(), | ||
| ) | ||
| .await | ||
| { | ||
| Ok(project) => Some(project), | ||
| Err(WorkspaceError::MissingPyprojectToml) => None, | ||
| Err(WorkspaceError::NonWorkspace(_)) => None, | ||
| Err(err) => return Err(err).into_diagnostic(), | ||
| }; | ||
|
|
||
| if let Some(project) = project { | ||
| interpreter_request = find_requires_python(project.workspace()) | ||
| .into_diagnostic()? | ||
| .as_ref() | ||
| .map(RequiresPython::specifiers) | ||
| .map(|specifiers| { | ||
| PythonRequest::Version(VersionRequest::Range(specifiers.clone())) | ||
| }); | ||
| } |
There was a problem hiding this comment.
Code here is adopted from run.rs:
uv/crates/uv/src/commands/project/run.rs
Lines 176 to 182 in c0d3da8
and
FoundInterpreter in mod.rs:uv/crates/uv/src/commands/project/mod.rs
Lines 158 to 172 in c0d3da8
81c2502 to
c704aee
Compare
|
Can you add a test for this in |
|
When initializing python project, it is common to specify minimum version in pyproject.toml (e.g. Is it make sense to have an option to change resolution strategy for python discovery? just like Example
[project]
requires-python = ">=3.8"By using |
|
Note this interacts with some work-in-progress at #5035; specifically the merged lines on my branch. I don't think that should block this change though. We'll also need to consider the following in the future (no changes needed here): |
|
I am tempted to agree that we should be selecting the lowest compatible version by default? This is tough to balance, as it's nice to get the performance and error handling improvements of a newer release. It may make sense to just expose a way to use the lowest compatible version instead (as suggested by @T-256) so it's easy to do in CI? However, I think in that case you'd want to actually guarantee you're getting the lowest version not the one nearest to your lower bound? |
Another case for inconsistency: today if you specify
Yes, it is makes sense to be strict on there. for example, with |
@zanieb what if minimum version is not provided and only used lower-bound? for example |
|
I prefer to use the newest version. Pros:
Cons:
A flag altering this behavior to use lowest possible version should be introduced indeed to ensure stable build in CI like docker image building, or for build script/devcontainer to ensure end users can run on happy path after their cloning. |
|
Note we basically never recommend adding an upper bound to Python version constraints and actively ignore it in some contexts.
Yeah this problem is solved by encouraging pin of the version (see also #4970) |
Vigilans
left a comment
There was a problem hiding this comment.
Test added. Found some more behaviors that should be reviewed.
crates/uv/tests/venv.rs
Outdated
| // With `requires-python = ">=3.10"`, we should prefer first possible version 3.11 | ||
| let pyproject_toml = context.temp_dir.child("pyproject.toml"); | ||
| pyproject_toml.write_str(indoc! { r###" | ||
| [project] | ||
| name = "foo" | ||
| version = "1.0.0" | ||
| requires-python = ">=3.10" | ||
| dependencies = [] | ||
| "### | ||
| })?; | ||
|
|
||
| uv_snapshot!(context.filters(), context.venv() | ||
| .arg("--preview"), @r###" | ||
| success: true | ||
| exit_code: 0 | ||
| ----- stdout ----- | ||
|
|
||
| ----- stderr ----- | ||
| Using Python 3.11.[X] interpreter at: [PYTHON-3.11] | ||
| Creating virtualenv at: .venv | ||
| Activate with: source .venv/bin/activate | ||
| "### | ||
| ); | ||
|
|
||
| // With `requires-python = ">=3.11"`, we should prefer first possible version 3.11 | ||
| let pyproject_toml = context.temp_dir.child("pyproject.toml"); | ||
| pyproject_toml.write_str(indoc! { r###" | ||
| [project] | ||
| name = "foo" | ||
| version = "1.0.0" | ||
| requires-python = ">=3.10" | ||
| dependencies = [] | ||
| "### | ||
| })?; | ||
|
|
||
| uv_snapshot!(context.filters(), context.venv() | ||
| .arg("--preview"), @r###" | ||
| success: true | ||
| exit_code: 0 | ||
| ----- stdout ----- | ||
|
|
||
| ----- stderr ----- | ||
| Using Python 3.11.[X] interpreter at: [PYTHON-3.11] | ||
| Creating virtualenv at: .venv | ||
| Activate with: source .venv/bin/activate | ||
| "### | ||
| ); | ||
|
|
||
| // With `requires-python = ">=3.12"`, we should prefer first possible version 3.12 | ||
| let pyproject_toml = context.temp_dir.child("pyproject.toml"); | ||
| pyproject_toml.write_str(indoc! { r###" | ||
| [project] | ||
| name = "foo" | ||
| version = "1.0.0" | ||
| requires-python = ">=3.10" | ||
| dependencies = [] | ||
| "### | ||
| })?; | ||
|
|
||
| uv_snapshot!(context.filters(), context.venv() | ||
| .arg("--preview"), @r###" | ||
| success: true | ||
| exit_code: 0 | ||
| ----- stdout ----- | ||
|
|
||
| ----- stderr ----- | ||
| Using Python 3.11.[X] interpreter at: [PYTHON-3.11] | ||
| Creating virtualenv at: .venv | ||
| Activate with: source .venv/bin/activate | ||
| "### | ||
| ); |
There was a problem hiding this comment.
I found that the default behavior for >=3.x is not select newest version, but select first possible version in the list. For a python list [3.11, 3.10, 3.12]:
>=3.10will select3.11.>=3.11will select3.11.>3.11will select3.11(becuase3.11.x>3.11).>=3.12will select3.12.
crates/uv/tests/venv.rs
Outdated
| [project] | ||
| name = "foo" | ||
| version = "1.0.0" | ||
| requires-python = ">=3.10" |
crates/uv/tests/venv.rs
Outdated
| [project] | ||
| name = "foo" | ||
| version = "1.0.0" | ||
| requires-python = ">=3.10" |
There was a problem hiding this comment.
Wow, I got these tests code totally wrong! Since the commit is pushed near my bed time I did not check them carefully after pasting. Good news that tests still pass after correction. Apology for my carelessness.
e4baa70 to
f12a994
Compare
## Summary Fixes #6177 This ensures a `pyproject.toml` file without a `[project]` table is not a fatal error for `uv venv`, which is just trying to discover/respect the project's `python-requires` (#5592). Similarly, any caught `WorkspaceError` is now also non-fatal and instead prints a warning message (feeback welcome here, felt less surprising than e.g. a malformed `pyproject.toml` breaking `uv venv`). ## Test Plan I added two test cases: `cargo test -p uv --test venv` Also, existing venv tests were failing for me since I use fish and the printed activation script was `source .venv/bin/activate.fish` (to repro, just run the tests with `SHELL=fish`). So added an insta filter to normalize that.
Summary
uv venvshould support adopting python version specified inrequires-pythonfrompyproject.toml. This allows customization on the venv setup when syncing from python project.Closes #5552.
It also serves as a workaround to close #5258.
Test Plan
uv venvin folder withpyroject.tomlspecifyingrequries-python = "<3.10". Python 3.9 is selected for venv.requries-python = "<3.11"and runuv venvagain. Python 3.10 is selected now.pyproject.tomlthen runuv venv. Python 3.12 is selected now.