Skip to content

Commit 14fd4c1

Browse files
committed
Improve error message when requested Python version is unsupported
1 parent 91b4925 commit 14fd4c1

File tree

2 files changed

+132
-54
lines changed

2 files changed

+132
-54
lines changed

crates/uv-python/src/discovery.rs

Lines changed: 100 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -779,77 +779,94 @@ pub fn find_python_installations<'a>(
779779
.map(FindPythonResult::Ok)
780780
})
781781
}),
782-
PythonRequest::Version(version) => Box::new({
783-
debug!("Searching for {request} in {preference}");
784-
python_interpreters(Some(version), None, environments, preference, cache)
782+
PythonRequest::Version(version) => {
783+
if let Err(err) = version.check_supported() {
784+
return Box::new(std::iter::once(Err(Error::InvalidVersionRequest(err))));
785+
};
786+
Box::new({
787+
debug!("Searching for {request} in {preference}");
788+
python_interpreters(Some(version), None, environments, preference, cache)
789+
.filter(|result| match result {
790+
Err(_) => true,
791+
Ok((_source, interpreter)) => version.matches_interpreter(interpreter),
792+
})
793+
.map(|result| {
794+
result
795+
.map(PythonInstallation::from_tuple)
796+
.map(FindPythonResult::Ok)
797+
})
798+
})
799+
}
800+
PythonRequest::Implementation(implementation) => Box::new({
801+
debug!("Searching for a {request} interpreter in {preference}");
802+
python_interpreters(None, Some(implementation), environments, preference, cache)
785803
.filter(|result| match result {
786804
Err(_) => true,
787-
Ok((_source, interpreter)) => version.matches_interpreter(interpreter),
805+
Ok((_source, interpreter)) => interpreter
806+
.implementation_name()
807+
.eq_ignore_ascii_case(implementation.into()),
788808
})
789809
.map(|result| {
790810
result
791811
.map(PythonInstallation::from_tuple)
792812
.map(FindPythonResult::Ok)
793813
})
794814
}),
795-
PythonRequest::Implementation(implementation) => Box::new({
796-
debug!("Searching for a {request} interpreter in {preference}");
797-
python_interpreters(None, Some(implementation), environments, preference, cache)
815+
PythonRequest::ImplementationVersion(implementation, version) => {
816+
if let Err(err) = version.check_supported() {
817+
return Box::new(std::iter::once(Err(Error::InvalidVersionRequest(err))));
818+
};
819+
Box::new({
820+
debug!("Searching for {request} in {preference}");
821+
python_interpreters(
822+
Some(version),
823+
Some(implementation),
824+
environments,
825+
preference,
826+
cache,
827+
)
798828
.filter(|result| match result {
799829
Err(_) => true,
800-
Ok((_source, interpreter)) => interpreter
801-
.implementation_name()
802-
.eq_ignore_ascii_case(implementation.into()),
830+
Ok((_source, interpreter)) => {
831+
version.matches_interpreter(interpreter)
832+
&& interpreter
833+
.implementation_name()
834+
.eq_ignore_ascii_case(implementation.into())
835+
}
803836
})
804837
.map(|result| {
805838
result
806839
.map(PythonInstallation::from_tuple)
807840
.map(FindPythonResult::Ok)
808841
})
809-
}),
810-
PythonRequest::ImplementationVersion(implementation, version) => Box::new({
811-
debug!("Searching for {request} in {preference}");
812-
python_interpreters(
813-
Some(version),
814-
Some(implementation),
815-
environments,
816-
preference,
817-
cache,
818-
)
819-
.filter(|result| match result {
820-
Err(_) => true,
821-
Ok((_source, interpreter)) => {
822-
version.matches_interpreter(interpreter)
823-
&& interpreter
824-
.implementation_name()
825-
.eq_ignore_ascii_case(implementation.into())
826-
}
827-
})
828-
.map(|result| {
829-
result
830-
.map(PythonInstallation::from_tuple)
831-
.map(FindPythonResult::Ok)
832-
})
833-
}),
834-
PythonRequest::Key(request) => Box::new({
835-
debug!("Searching for {request} in {preference}");
836-
python_interpreters(
837-
request.version(),
838-
request.implementation(),
839-
environments,
840-
preference,
841-
cache,
842-
)
843-
.filter(|result| match result {
844-
Err(_) => true,
845-
Ok((_source, interpreter)) => request.satisfied_by_interpreter(interpreter),
846842
})
847-
.map(|result| {
848-
result
849-
.map(PythonInstallation::from_tuple)
850-
.map(FindPythonResult::Ok)
843+
}
844+
PythonRequest::Key(request) => {
845+
if let Some(version) = request.version() {
846+
if let Err(err) = version.check_supported() {
847+
return Box::new(std::iter::once(Err(Error::InvalidVersionRequest(err))));
848+
};
849+
};
850+
Box::new({
851+
debug!("Searching for {request} in {preference}");
852+
python_interpreters(
853+
request.version(),
854+
request.implementation(),
855+
environments,
856+
preference,
857+
cache,
858+
)
859+
.filter(|result| match result {
860+
Err(_) => true,
861+
Ok((_source, interpreter)) => request.satisfied_by_interpreter(interpreter),
862+
})
863+
.map(|result| {
864+
result
865+
.map(PythonInstallation::from_tuple)
866+
.map(FindPythonResult::Ok)
867+
})
851868
})
852-
}),
869+
}
853870
}
854871
}
855872

@@ -1449,6 +1466,37 @@ impl VersionRequest {
14491466
.flatten()
14501467
}
14511468

1469+
pub(crate) fn check_supported(&self) -> Result<(), String> {
1470+
match self {
1471+
Self::Any => (),
1472+
Self::Major(major) => {
1473+
if *major < 3 {
1474+
return Err(format!(
1475+
"Python <3 is not supported but {major} was requested."
1476+
));
1477+
}
1478+
}
1479+
Self::MajorMinor(major, minor) => {
1480+
if (*major, *minor) < (3, 7) {
1481+
return Err(format!(
1482+
"Python <3.7 is not supported but {major}.{minor} was requested."
1483+
));
1484+
}
1485+
}
1486+
Self::MajorMinorPatch(major, minor, patch) => {
1487+
if (*major, *minor) < (3, 7) {
1488+
return Err(format!(
1489+
"Python <3.7 is not supported but {major}.{minor}.{patch} was requested."
1490+
));
1491+
}
1492+
}
1493+
// TODO(zanieb): We could do some checking here to see if the range can be satisfied
1494+
Self::Range(_) => (),
1495+
}
1496+
1497+
Ok(())
1498+
}
1499+
14521500
/// Check if a interpreter matches the requested Python version.
14531501
pub(crate) fn matches_interpreter(&self, interpreter: &Interpreter) -> bool {
14541502
match self {

crates/uv/tests/python_find.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,17 @@ fn python_find_unsupported_version() {
403403
----- stdout -----
404404
405405
----- stderr -----
406-
error: No interpreter found for Python 3.6 in virtual environments or system path
406+
error: Invalid version request: Python <3.7 is not supported but 3.6 was requested.
407+
"###);
408+
409+
// Request a low version with a patch
410+
uv_snapshot!(context.filters(), context.python_find().arg("3.6.9"), @r###"
411+
success: false
412+
exit_code: 2
413+
----- stdout -----
414+
415+
----- stderr -----
416+
error: Invalid version request: Python <3.7 is not supported but 3.6.9 was requested.
407417
"###);
408418

409419
// Request a really low version
@@ -413,7 +423,17 @@ fn python_find_unsupported_version() {
413423
----- stdout -----
414424
415425
----- stderr -----
416-
error: No interpreter found for Python 2.6 in virtual environments or system path
426+
error: Invalid version request: Python <3.7 is not supported but 2.6 was requested.
427+
"###);
428+
429+
// Request a really low version with a patch
430+
uv_snapshot!(context.filters(), context.python_find().arg("2.6.8"), @r###"
431+
success: false
432+
exit_code: 2
433+
----- stdout -----
434+
435+
----- stderr -----
436+
error: Invalid version request: Python <3.7 is not supported but 2.6.8 was requested.
417437
"###);
418438

419439
// Request a future version
@@ -425,4 +445,14 @@ fn python_find_unsupported_version() {
425445
----- stderr -----
426446
error: No interpreter found for Python 4.2 in virtual environments or system path
427447
"###);
448+
449+
// Request a low version with a range
450+
uv_snapshot!(context.filters(), context.python_find().arg("<3.0"), @r###"
451+
success: false
452+
exit_code: 2
453+
----- stdout -----
454+
455+
----- stderr -----
456+
error: No interpreter found for Python <3.0 in virtual environments or system path
457+
"###);
428458
}

0 commit comments

Comments
 (0)