Skip to content

Commit 64e91a7

Browse files
authored
Fix handling of python-preference = system when managed interpreters are on the PATH (#15059)
This is the first part of fixing a 0.8.0 regression from #7934 There, we added handling for skipping managed interpreters on the PATH when `only-system` is used, but did not update the logic to prefer system interpreters over managed ones when `system` is used. Here, we fix that by skipping managed interpreters when `system` is used unless _only_ managed interpreters are available. While this logic is applied during in a general discovery method, it's only relevant for the PATH (and the Windows registry) because we already change the _order_ that we inspect installations in when `system` is used, so the managed installation directory is inspected last. This behavior did not regress in 0.8, it's always been this way, however, I need this change in order to fix a different bug.
1 parent 8186aa9 commit 64e91a7

File tree

2 files changed

+72
-20
lines changed

2 files changed

+72
-20
lines changed

crates/uv-python/src/discovery.rs

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -918,24 +918,7 @@ pub fn satisfies_python_preference(
918918
// If not "only" a kind, any interpreter is okay
919919
PythonPreference::Managed | PythonPreference::System => true,
920920
PythonPreference::OnlySystem => {
921-
let is_system = match source {
922-
// A managed interpreter is never a system interpreter
923-
PythonSource::Managed => false,
924-
// We can't be sure if this is a system interpreter without checking
925-
PythonSource::ProvidedPath
926-
| PythonSource::ParentInterpreter
927-
| PythonSource::ActiveEnvironment
928-
| PythonSource::CondaPrefix
929-
| PythonSource::DiscoveredEnvironment
930-
| PythonSource::SearchPath
931-
| PythonSource::SearchPathFirst
932-
| PythonSource::Registry
933-
| PythonSource::BaseCondaPrefix => !interpreter.is_managed(),
934-
// Managed interpreters should never be found in the store
935-
PythonSource::MicrosoftStore => true,
936-
};
937-
938-
if is_system {
921+
if is_system_interpreter(source, interpreter) {
939922
true
940923
} else {
941924
if is_explicit {
@@ -956,6 +939,25 @@ pub fn satisfies_python_preference(
956939
}
957940
}
958941

942+
pub(crate) fn is_system_interpreter(source: PythonSource, interpreter: &Interpreter) -> bool {
943+
match source {
944+
// A managed interpreter is never a system interpreter
945+
PythonSource::Managed => false,
946+
// We can't be sure if this is a system interpreter without checking
947+
PythonSource::ProvidedPath
948+
| PythonSource::ParentInterpreter
949+
| PythonSource::ActiveEnvironment
950+
| PythonSource::CondaPrefix
951+
| PythonSource::DiscoveredEnvironment
952+
| PythonSource::SearchPath
953+
| PythonSource::SearchPathFirst
954+
| PythonSource::Registry
955+
| PythonSource::BaseCondaPrefix => !interpreter.is_managed(),
956+
// Managed interpreters should never be found in the store
957+
PythonSource::MicrosoftStore => true,
958+
}
959+
}
960+
959961
/// Check if an encountered error is critical and should stop discovery.
960962
///
961963
/// Returns false when an error could be due to a faulty Python installation and we should continue searching for a working one.
@@ -1259,6 +1261,7 @@ pub(crate) fn find_python_installation(
12591261
let installations =
12601262
find_python_installations(request, environments, preference, cache, preview);
12611263
let mut first_prerelease = None;
1264+
let mut first_managed = None;
12621265
let mut first_error = None;
12631266
for result in installations {
12641267
// Iterate until the first critical error or happy result
@@ -1295,7 +1298,7 @@ pub(crate) fn find_python_installation(
12951298
&& !installation.source.allows_prereleases()
12961299
&& !has_default_executable_name
12971300
{
1298-
debug!("Skipping pre-release {}", installation.key());
1301+
debug!("Skipping pre-release installation {}", installation.key());
12991302
if first_prerelease.is_none() {
13001303
first_prerelease = Some(installation.clone());
13011304
}
@@ -1315,12 +1318,41 @@ pub(crate) fn find_python_installation(
13151318
continue;
13161319
}
13171320

1321+
// If it's a managed Python installation, and system interpreters are preferred, skip it
1322+
// for now.
1323+
if matches!(preference, PythonPreference::System)
1324+
&& !is_system_interpreter(installation.source, installation.interpreter())
1325+
{
1326+
debug!(
1327+
"Skipping managed installation {}: system installation preferred",
1328+
installation.key()
1329+
);
1330+
if first_managed.is_none() {
1331+
first_managed = Some(installation.clone());
1332+
}
1333+
continue;
1334+
}
1335+
13181336
// If we didn't skip it, this is the installation to use
13191337
return result;
13201338
}
13211339

1340+
// If we only found managed installations, and the preference allows them, we should return
1341+
// the first one.
1342+
if let Some(installation) = first_managed {
1343+
debug!(
1344+
"Allowing managed installation {}: no system installations",
1345+
installation.key()
1346+
);
1347+
return Ok(Ok(installation));
1348+
}
1349+
13221350
// If we only found pre-releases, they're implicitly allowed and we should return the first one.
13231351
if let Some(installation) = first_prerelease {
1352+
debug!(
1353+
"Allowing pre-release installation {}: no stable installations",
1354+
installation.key()
1355+
);
13241356
return Ok(Ok(installation));
13251357
}
13261358

crates/uv/tests/it/python_find.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,7 @@ fn python_find_managed() {
758758
.with_filtered_python_sources()
759759
.with_versions_as_managed(&["3.11"]);
760760

761-
// We find the unmanaged interpreter
761+
// We find the unmanaged interpreter with managed Python disabled
762762
uv_snapshot!(context.filters(), context.python_find().arg("--no-managed-python"), @r"
763763
success: true
764764
exit_code: 0
@@ -777,6 +777,26 @@ fn python_find_managed() {
777777
----- stderr -----
778778
error: No interpreter found for Python 3.11 in [PYTHON SOURCES]
779779
");
780+
781+
// We find the unmanaged interpreter with system Python preferred
782+
uv_snapshot!(context.filters(), context.python_find().arg("--python-preference").arg("system"), @r"
783+
success: true
784+
exit_code: 0
785+
----- stdout -----
786+
[PYTHON-3.12]
787+
788+
----- stderr -----
789+
");
790+
791+
// But, if no system Python meets the request, we'll use the managed interpreter
792+
uv_snapshot!(context.filters(), context.python_find().arg("--python-preference").arg("system").arg("3.11"), @r"
793+
success: true
794+
exit_code: 0
795+
----- stdout -----
796+
[PYTHON-3.11]
797+
798+
----- stderr -----
799+
");
780800
}
781801

782802
/// See: <https://github.com/astral-sh/uv/issues/11825>

0 commit comments

Comments
 (0)