diff --git a/crates/uv-python/src/discovery.rs b/crates/uv-python/src/discovery.rs index d741a3e4e..04bc874b1 100644 --- a/crates/uv-python/src/discovery.rs +++ b/crates/uv-python/src/discovery.rs @@ -2690,6 +2690,9 @@ impl VersionRequest { interpreter.python_minor(), interpreter.python_patch(), ) == (*major, *minor, *patch) + // When a patch version is included, we treat it as a request for a stable + // release + && interpreter.python_version().pre().is_none() && variant.matches_interpreter(interpreter) } Self::Range(specifiers, variant) => { @@ -2814,6 +2817,9 @@ impl VersionRequest { } Self::MajorMinorPatch(self_major, self_minor, self_patch, _) => { (*self_major, *self_minor, *self_patch) == (major, minor, patch) + // When a patch version is included, we treat it as a request for a stable + // release + && prerelease.is_none() } Self::Range(specifiers, _) => specifiers.contains( &Version::new([u64::from(major), u64::from(minor), u64::from(patch)]) diff --git a/crates/uv/tests/it/python_find.rs b/crates/uv/tests/it/python_find.rs index 29e56d005..efe52e509 100644 --- a/crates/uv/tests/it/python_find.rs +++ b/crates/uv/tests/it/python_find.rs @@ -1433,3 +1433,50 @@ fn python_find_prerelease_version_specifiers() { ----- stderr ----- "); } + +#[test] +fn python_find_prerelease_with_patch_request() { + let context: TestContext = TestContext::new_with_versions(&[]) + .with_filtered_python_keys() + .with_filtered_python_sources() + .with_managed_python_dirs() + .with_python_download_cache() + .with_filtered_python_install_bin() + .with_filtered_python_names() + .with_filtered_exe_suffix(); + + // Install 3.14.0rc3 + context.python_install().arg("3.14.0rc3").assert().success(); + + // When no `.0` patch version is included, we'll allow selection of a pre-release + uv_snapshot!(context.filters(), context.python_find().arg("3.14"), @r" + success: true + exit_code: 0 + ----- stdout ----- + [TEMP_DIR]/managed/cpython-3.14.0rc3-[PLATFORM]/[INSTALL-BIN]/[PYTHON] + + ----- stderr ----- + "); + + // When `.0` is explicitly included, we will require a stable release + uv_snapshot!(context.filters(), context.python_find().arg("3.14.0"), @r" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: No interpreter found for Python 3.14.0 in [PYTHON SOURCES] + "); + + // Install 3.14.0 stable + context.python_install().arg("3.14.0").assert().success(); + + uv_snapshot!(context.filters(), context.python_find().arg("3.14.0"), @r" + success: true + exit_code: 0 + ----- stdout ----- + [TEMP_DIR]/managed/cpython-3.14.0-[PLATFORM]/[INSTALL-BIN]/[PYTHON] + + ----- stderr ----- + "); +}