Skip to content
19 changes: 17 additions & 2 deletions crates/uv/src/commands/python/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,18 @@ pub(crate) async fn list(
let installed =
match kinds {
PythonListKinds::Installed | PythonListKinds::Default => {
// While usually [`PythonPreference::OnlyManaged`] means we can skip searching the `PATH`,
// in `uv python list` we want to enumerate links to managed Python interpreters for inspection.
// Consequently, we widen the preference here and perform post-filtering.
let discovery_preference = if python_preference == PythonPreference::OnlyManaged {
PythonPreference::Managed
} else {
python_preference
};
Some(find_python_installations(
request.as_ref().unwrap_or(&PythonRequest::Any),
EnvironmentPreference::OnlySystem,
python_preference,
discovery_preference,
cache,
preview,
)
Expand All @@ -159,7 +167,14 @@ pub(crate) async fn list(
.collect::<Result<Vec<Result<PythonInstallation, PythonNotFound>>, DiscoveryError>>()?
.into_iter()
// Drop any "missing" installations
.filter_map(Result::ok))
.filter_map(Result::ok)
// Apply the `PythonPreference` to discovered interpreters, since we may have
// expanded it above
.filter(|installation| match python_preference {
PythonPreference::OnlyManaged => installation.interpreter().is_managed(),
PythonPreference::OnlySystem => !installation.interpreter().is_managed(),
PythonPreference::Managed | PythonPreference::System => true,
Comment on lines +173 to +176
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this just be python_preference.allows(installation.source)?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, here we want to check if the installation is managed regardless of the source, so we need to actually check where interpreter's base prefix is. It does seem like PythonPreference::allows_interpreter would be useful though.

}))
}
PythonListKinds::Downloads => None,
};
Expand Down
62 changes: 62 additions & 0 deletions crates/uv/tests/it/python_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,68 @@ fn python_list_downloads_installed() {

----- stderr -----
");

// When `--managed-python` is used, managed installations should still be shown
uv_snapshot!(context.filters(), context.python_list().arg("3.10").arg("--managed-python").env_remove(EnvVars::UV_PYTHON_DOWNLOADS), @"
success: true
exit_code: 0
----- stdout -----
cpython-3.10.[LATEST]-[PLATFORM] managed/cpython-3.10-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
pypy-3.10.16-[PLATFORM] <download available>
graalpy-3.10.0-[PLATFORM] <download available>

----- stderr -----
");
}

/// Test that symlinks installed by `python install` on the search path are correctly
/// filtered by `--managed-python` and `--no-managed-python`.
#[test]
#[cfg(all(unix, feature = "test-python-managed"))]
fn python_list_managed_symlinks() {
use assert_cmd::assert::OutputAssertExt;

let context = uv_test::test_context_with_versions!(&[])
.with_filtered_python_keys()
.with_filtered_python_install_bin()
.with_filtered_python_names()
.with_managed_python_dirs()
.with_filtered_latest_python_versions();

// Install a Python version; this creates a symlink in `bin_dir` (on the search path)
context.python_install().arg("3.10").assert().success();

// Include `bin_dir` in the test search path so the symlink is discoverable
let bin_dir = context.bin_dir.to_path_buf();

// With `--no-managed-python`, the symlink should be excluded since it points to a
// managed installation
uv_snapshot!(context.filters(), context.python_list()
.arg("3.10")
.arg("--only-installed")
.arg("--no-managed-python")
.env(EnvVars::UV_TEST_PYTHON_PATH, &bin_dir), @"
success: true
exit_code: 0
----- stdout -----

----- stderr -----
");

// With `--managed-python`, both the managed installation and the symlink are shown
uv_snapshot!(context.filters(), context.python_list()
.arg("3.10")
.arg("--only-installed")
.arg("--managed-python")
.env(EnvVars::UV_TEST_PYTHON_PATH, &bin_dir), @"
success: true
exit_code: 0
----- stdout -----
cpython-3.10.[LATEST]-[PLATFORM] [BIN]/[PYTHON] -> managed/cpython-3.10-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
cpython-3.10.[LATEST]-[PLATFORM] managed/cpython-3.10-[PLATFORM]/[INSTALL-BIN]/[PYTHON]

----- stderr -----
");
}

#[tokio::test]
Expand Down
Loading