-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detect python version from python project by default in uv venv
#5592
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code here is adopted from run.rs
:
uv/crates/uv/src/commands/project/run.rs
Lines 176 to 182 in c0d3da8
match VirtualProject::discover(&CWD, &DiscoveryOptions::default()).await { | |
Ok(project) => Some(project), | |
Err(WorkspaceError::MissingPyprojectToml) => None, | |
Err(WorkspaceError::NonWorkspace(_)) => None, | |
Err(err) => return Err(err.into()), | |
} | |
}; |
and
FoundInterpreter
in mod.rs
:uv/crates/uv/src/commands/project/mod.rs
Lines 158 to 172 in c0d3da8
let requires_python = find_requires_python(workspace)?; | |
// (1) Explicit request from user | |
let python_request = if let Some(request) = python_request { | |
Some(request) | |
// (2) Request from `.python-version` | |
} else if let Some(request) = request_from_version_file(workspace.install_path()).await? { | |
Some(request) | |
// (3) `Requires-Python` in `pyproject.toml` | |
} else { | |
requires_python | |
.as_ref() | |
.map(RequiresPython::specifiers) | |
.map(|specifiers| PythonRequest::Version(VersionRequest::Range(specifiers.clone()))) | |
}; |
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.10
will select3.11
.>=3.11
will select3.11
.>3.11
will select3.11
(becuase3.11.x
>3.11
).>=3.12
will select3.12
.
crates/uv/tests/venv.rs
Outdated
[project] | ||
name = "foo" | ||
version = "1.0.0" | ||
requires-python = ">=3.10" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be 3.11?
crates/uv/tests/venv.rs
Outdated
[project] | ||
name = "foo" | ||
version = "1.0.0" | ||
requires-python = ">=3.10" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be 3.12?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
## 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 venv
should support adopting python version specified inrequires-python
frompyproject.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 venv
in folder withpyroject.toml
specifyingrequries-python = "<3.10"
. Python 3.9 is selected for venv.requries-python = "<3.11"
and runuv venv
again. Python 3.10 is selected now.pyproject.toml
then runuv venv
. Python 3.12 is selected now.