Ignore arbitrary Python requests in version files (#12909)

Closes https://github.com/astral-sh/uv/issues/12605
This commit is contained in:
Zanie Blue 2025-04-18 11:45:04 -05:00
parent 1988e209ef
commit 6cc2202799
3 changed files with 92 additions and 0 deletions

View file

@ -6,6 +6,7 @@ use itertools::Itertools;
use tracing::debug;
use uv_dirs::user_uv_config_dir;
use uv_fs::Simplified;
use uv_warnings::warn_user_once;
use crate::PythonRequest;
@ -171,6 +172,17 @@ impl PythonVersionFile {
})
.map(ToString::to_string)
.map(|version| PythonRequest::parse(&version))
.filter(|request| {
if let PythonRequest::ExecutableName(name) = request {
warn_user_once!(
"Ignoring unsupported Python request `{name}` in version file: {}",
path.display()
);
false
} else {
true
}
})
.collect();
Ok(Some(Self { path, versions }))
}

View file

@ -76,6 +76,10 @@ pub(crate) async fn pin(
};
let request = PythonRequest::parse(&request);
if let PythonRequest::ExecutableName(name) = request {
bail!("Requests for arbitrary names (e.g., `{name}`) are not supported in version files");
}
let python = match PythonInstallation::find(
&request,
EnvironmentPreference::OnlySystem,

View file

@ -217,6 +217,82 @@ fn python_find_pin() {
"###);
}
#[test]
fn python_find_pin_arbitrary_name() {
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"]);
// Try to pin to an arbitrary name
uv_snapshot!(context.filters(), context.python_pin().arg("foo"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: Requests for arbitrary names (e.g., `foo`) are not supported in version files
");
// Pin to an arbitrary name, bypassing uv
context
.temp_dir
.child(".python-version")
.write_str("foo")
.unwrap();
// The arbitrary name should be ignored
uv_snapshot!(context.filters(), context.python_find(), @r"
success: true
exit_code: 0
----- stdout -----
[PYTHON-3.11]
----- stderr -----
warning: Ignoring unsupported Python request `foo` in version file: [TEMP_DIR]/.python-version
");
// The pin should be updatable
uv_snapshot!(context.filters(), context.python_pin().arg("3.11"), @r"
success: true
exit_code: 0
----- stdout -----
Pinned `.python-version` to `3.11`
----- stderr -----
warning: Ignoring unsupported Python request `foo` in version file: [TEMP_DIR]/.python-version
");
// Warnings shouldn't appear afterwards...
uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r"
success: true
exit_code: 0
----- stdout -----
Updated `.python-version` from `3.11` -> `3.12`
----- stderr -----
");
// Pin in a sub-directory
context.temp_dir.child("foo").create_dir_all().unwrap();
context
.temp_dir
.child("foo")
.child(".python-version")
.write_str("foo")
.unwrap();
// The arbitrary name should be ignored, but we won't walk up to the parent `.python-version`
// file (which contains 3.12); this behavior is a little questionable but we probably want to
// ignore all empty version files if we want to change this?
uv_snapshot!(context.filters(), context.python_find().current_dir(context.temp_dir.child("foo").path()), @r"
success: true
exit_code: 0
----- stdout -----
[PYTHON-3.11]
----- stderr -----
warning: Ignoring unsupported Python request `foo` in version file: [TEMP_DIR]/foo/.python-version
");
}
#[test]
fn python_find_project() {
let context: TestContext = TestContext::new_with_versions(&["3.10", "3.11", "3.12"]);