Skip to content
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

Improve error message when requested Python version is unsupported #7269

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 100 additions & 52 deletions crates/uv-python/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,77 +776,94 @@ pub fn find_python_installations<'a>(
.map(FindPythonResult::Ok)
})
}),
PythonRequest::Version(version) => Box::new({
debug!("Searching for {request} in {preference}");
python_interpreters(Some(version), None, environments, preference, cache)
PythonRequest::Version(version) => {
if let Err(err) = version.check_supported() {
return Box::new(std::iter::once(Err(Error::InvalidVersionRequest(err))));
};
Box::new({
debug!("Searching for {request} in {preference}");
python_interpreters(Some(version), None, environments, preference, cache)
.filter(|result| match result {
Err(_) => true,
Ok((_source, interpreter)) => version.matches_interpreter(interpreter),
})
.map(|result| {
result
.map(PythonInstallation::from_tuple)
.map(FindPythonResult::Ok)
})
})
}
PythonRequest::Implementation(implementation) => Box::new({
debug!("Searching for a {request} interpreter in {preference}");
python_interpreters(None, Some(implementation), environments, preference, cache)
.filter(|result| match result {
Err(_) => true,
Ok((_source, interpreter)) => version.matches_interpreter(interpreter),
Ok((_source, interpreter)) => interpreter
.implementation_name()
.eq_ignore_ascii_case(implementation.into()),
})
.map(|result| {
result
.map(PythonInstallation::from_tuple)
.map(FindPythonResult::Ok)
})
}),
PythonRequest::Implementation(implementation) => Box::new({
debug!("Searching for a {request} interpreter in {preference}");
python_interpreters(None, Some(implementation), environments, preference, cache)
PythonRequest::ImplementationVersion(implementation, version) => {
if let Err(err) = version.check_supported() {
return Box::new(std::iter::once(Err(Error::InvalidVersionRequest(err))));
};
Box::new({
debug!("Searching for {request} in {preference}");
python_interpreters(
Some(version),
Some(implementation),
environments,
preference,
cache,
)
.filter(|result| match result {
Err(_) => true,
Ok((_source, interpreter)) => interpreter
.implementation_name()
.eq_ignore_ascii_case(implementation.into()),
Ok((_source, interpreter)) => {
version.matches_interpreter(interpreter)
&& interpreter
.implementation_name()
.eq_ignore_ascii_case(implementation.into())
}
})
.map(|result| {
result
.map(PythonInstallation::from_tuple)
.map(FindPythonResult::Ok)
})
}),
PythonRequest::ImplementationVersion(implementation, version) => Box::new({
debug!("Searching for {request} in {preference}");
python_interpreters(
Some(version),
Some(implementation),
environments,
preference,
cache,
)
.filter(|result| match result {
Err(_) => true,
Ok((_source, interpreter)) => {
version.matches_interpreter(interpreter)
&& interpreter
.implementation_name()
.eq_ignore_ascii_case(implementation.into())
}
})
.map(|result| {
result
.map(PythonInstallation::from_tuple)
.map(FindPythonResult::Ok)
})
}),
PythonRequest::Key(request) => Box::new({
debug!("Searching for {request} in {preference}");
python_interpreters(
request.version(),
request.implementation(),
environments,
preference,
cache,
)
.filter(|result| match result {
Err(_) => true,
Ok((_source, interpreter)) => request.satisfied_by_interpreter(interpreter),
})
.map(|result| {
result
.map(PythonInstallation::from_tuple)
.map(FindPythonResult::Ok)
}
PythonRequest::Key(request) => {
if let Some(version) = request.version() {
if let Err(err) = version.check_supported() {
return Box::new(std::iter::once(Err(Error::InvalidVersionRequest(err))));
};
};
Box::new({
debug!("Searching for {request} in {preference}");
python_interpreters(
request.version(),
request.implementation(),
environments,
preference,
cache,
)
.filter(|result| match result {
Err(_) => true,
Ok((_source, interpreter)) => request.satisfied_by_interpreter(interpreter),
})
.map(|result| {
result
.map(PythonInstallation::from_tuple)
.map(FindPythonResult::Ok)
})
})
}),
}
}
}

Expand Down Expand Up @@ -1446,6 +1463,37 @@ impl VersionRequest {
.flatten()
}

pub(crate) fn check_supported(&self) -> Result<(), String> {
match self {
Self::Any => (),
Self::Major(major) => {
if *major < 3 {
return Err(format!(
"Python <3 is not supported but {major} was requested."
));
}
}
Self::MajorMinor(major, minor) => {
if (*major, *minor) < (3, 7) {
return Err(format!(
"Python <3.7 is not supported but {major}.{minor} was requested."
));
}
}
Self::MajorMinorPatch(major, minor, patch) => {
if (*major, *minor) < (3, 7) {
return Err(format!(
"Python <3.7 is not supported but {major}.{minor}.{patch} was requested."
));
}
}
// TODO(zanieb): We could do some checking here to see if the range can be satisfied
Self::Range(_) => (),
}

Ok(())
}

/// Check if a interpreter matches the requested Python version.
pub(crate) fn matches_interpreter(&self, interpreter: &Interpreter) -> bool {
match self {
Expand Down
34 changes: 32 additions & 2 deletions crates/uv/tests/python_find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,17 @@ fn python_find_unsupported_version() {
----- stdout -----

----- stderr -----
error: No interpreter found for Python 3.6 in virtual environments or system path
error: Invalid version request: Python <3.7 is not supported but 3.6 was requested.
"###);

// Request a low version with a patch
uv_snapshot!(context.filters(), context.python_find().arg("3.6.9"), @r###"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
error: Invalid version request: Python <3.7 is not supported but 3.6.9 was requested.
"###);

// Request a really low version
Expand All @@ -413,7 +423,17 @@ fn python_find_unsupported_version() {
----- stdout -----

----- stderr -----
error: No interpreter found for Python 2.6 in virtual environments or system path
error: Invalid version request: Python <3.7 is not supported but 2.6 was requested.
"###);

// Request a really low version with a patch
uv_snapshot!(context.filters(), context.python_find().arg("2.6.8"), @r###"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
error: Invalid version request: Python <3.7 is not supported but 2.6.8 was requested.
"###);

// Request a future version
Expand All @@ -425,4 +445,14 @@ fn python_find_unsupported_version() {
----- stderr -----
error: No interpreter found for Python 4.2 in virtual environments or system path
"###);

// Request a low version with a range
uv_snapshot!(context.filters(), context.python_find().arg("<3.0"), @r###"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
error: No interpreter found for Python <3.0 in virtual environments or system path
"###);
}
Loading