diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index 52561b70a..6486b2cf7 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -264,7 +264,7 @@ pub(crate) async fn install( .collect::>>()? }; - let Some(first_request) = requests.first() else { + if requests.is_empty() { if upgrade { writeln!( printer.stderr(), @@ -272,7 +272,7 @@ pub(crate) async fn install( )?; } return Ok(ExitStatus::Success); - }; + } let requested_minor_versions = requests .iter() @@ -500,7 +500,6 @@ pub(crate) async fn install( upgradeable, upgrade, is_default_install, - first_request, &existing_installations, &installations, &mut changelog, @@ -764,7 +763,6 @@ fn create_bin_links( upgradeable: bool, upgrade: bool, is_default_install: bool, - first_request: &InstallRequest, existing_installations: &[ManagedPythonInstallation], installations: &[&ManagedPythonInstallation], changelog: &mut Changelog, @@ -774,10 +772,10 @@ fn create_bin_links( // TODO(zanieb): We want more feedback on the `is_default_install` behavior before stabilizing // it. In particular, it may be confusing because it does not apply when versions are loaded // from a `.python-version` file. - let targets = if (default - || (is_default_install && preview.is_enabled(PreviewFeatures::PYTHON_INSTALL_DEFAULT))) - && first_request.matches_installation(installation) - { + let should_create_default_links = default + || (is_default_install && preview.is_enabled(PreviewFeatures::PYTHON_INSTALL_DEFAULT)); + + let targets = if should_create_default_links { vec![ installation.key().executable_name_minor(), installation.key().executable_name_major(), diff --git a/crates/uv/tests/it/python_install.rs b/crates/uv/tests/it/python_install.rs index 5cd76d8e8..86da14eb4 100644 --- a/crates/uv/tests/it/python_install.rs +++ b/crates/uv/tests/it/python_install.rs @@ -2255,6 +2255,45 @@ fn python_install_broken_link() { }); } +/// Test that --default works with pre-release versions (e.g., 3.15.0a1). +/// This test verifies the fix for issue #16696 where --default didn't create +/// python.exe and python3.exe links for pre-release versions. +#[test] +fn python_install_default_prerelease() { + let context: TestContext = TestContext::new_with_versions(&[]) + .with_filtered_python_keys() + .with_filtered_exe_suffix() + .with_managed_python_dirs() + .with_python_download_cache(); + + // Install Python 3.15, which currently only exists as a pre-release (3.15.0a1). + context + .python_install() + .arg("--default") + .arg("--preview-features") + .arg("python-install-default") + .arg("3.15") + .assert() + .success(); + + let bin_python_minor_15 = context + .bin_dir + .child(format!("python3.15{}", std::env::consts::EXE_SUFFIX)); + + let bin_python_major = context + .bin_dir + .child(format!("python3{}", std::env::consts::EXE_SUFFIX)); + + let bin_python_default = context + .bin_dir + .child(format!("python{}", std::env::consts::EXE_SUFFIX)); + + // Verify that all three executables are created when --default is used with a pre-release version + bin_python_minor_15.assert(predicate::path::exists()); + bin_python_major.assert(predicate::path::exists()); + bin_python_default.assert(predicate::path::exists()); +} + #[test] fn python_install_default_from_env() { let context: TestContext = TestContext::new_with_versions(&[])