Add support for ruff version constraints and exclude-newer in uv format#17651
Add support for ruff version constraints and exclude-newer in uv format#17651
exclude-newer in uv format#17651Conversation
101d3b0 to
3e0cfe1
Compare
| let path = bin_install( | ||
| Binary::Ruff, | ||
| &version, | ||
| &client, | ||
| &retry_policy, | ||
| &cache, | ||
| &reporter, | ||
| ) | ||
| .await | ||
| .with_context(|| format!("Failed to install ruff {version}"))?; |
There was a problem hiding this comment.
I thought this could be factored out at first, but I notice that we have bin_install and bin_install_resolved.
We could maybe make all of this simpler with just having bin_install_resolved (but maybe call it bin_install) and then have a TryFrom<Version> for ResolvedVersion which implements the below:
let platform = Platform::from_env()?;
let platform_name = platform.as_cargo_dist_triple();
let format = if platform.os.is_windows() {
ArchiveFormat::Zip
} else {
ArchiveFormat::TarGz
};
let download_url = binary.download_url(version, &platform_name, format)?;
Could be done another time though.
| let archive_format = match artifact.archive_format.as_str() { | ||
| "tar.gz" => ArchiveFormat::TarGz, | ||
| "zip" => ArchiveFormat::Zip, | ||
| _ => continue, | ||
| }; |
There was a problem hiding this comment.
There is a bit of an interesting inversion of the logic here. When we specify an explicit version we dictate that the archive format must be zip on windows and tar.gz otherwise. Here instead we could end up picking a zip for linux if it happened to be the first option.
Maybe we should dictate that this must be a zip on windows and a tar.gz otherwise?
There was a problem hiding this comment.
I don't think we want to dictate that, it's fine for either a zip or tar.gz to be used on arbitrary platforms depending on what the remote tells us is available. The reason it's different for explicit versions is we are constructing a known artifact URL to avoid doing any resolution at all.
There was a problem hiding this comment.
(I believe I made this a little clearer)
crates/uv-bin-install/src/lib.rs
Outdated
| } | ||
|
|
||
| /// Find an artifact for the given platform from the version info. | ||
| fn find_artifact_for_platform( |
There was a problem hiding this comment.
More of a nitpick than anything but this function is used once inside check_version_match. It seems somewhat weird to have it split out.
crates/uv-bin-install/src/lib.rs
Outdated
|
|
||
| #[error("Failed to parse version manifest")] | ||
| ManifestParse { | ||
| #[source] |
There was a problem hiding this comment.
Is it intentional that this is a #[source] in a normal struct and ManifestUtf8 below is a #[from] in a tuple struct?
| // Stream the response line by line | ||
| let mut stream = response.bytes_stream(); | ||
| let mut buffer = Vec::new(); | ||
|
|
||
| while let Some(chunk) = stream.next().await { | ||
| let chunk = chunk.map_err(|err| Error::ManifestFetch { | ||
| url: manifest_url.to_string(), | ||
| source: reqwest_middleware::Error::Reqwest(err), | ||
| })?; | ||
| buffer.extend_from_slice(&chunk); | ||
|
|
||
| // Process complete lines | ||
| while let Some(newline_pos) = buffer.iter().position(|&b| b == b'\n') { | ||
| let line = &buffer[..newline_pos]; | ||
|
|
||
| // Skip empty lines | ||
| if line.is_empty() { | ||
| buffer.drain(..=newline_pos); | ||
| continue; | ||
| } | ||
|
|
||
| // Parse the JSON line | ||
| let line_str = std::str::from_utf8(line)?; | ||
| let version_info: BinVersionInfo = | ||
| serde_json::from_str(line_str).map_err(|source| Error::ManifestParse { source })?; | ||
|
|
||
| buffer.drain(..=newline_pos); | ||
|
|
||
| if let Some(resolved) = | ||
| check_version_match(&version_info, constraints, exclude_newer, platform_name) | ||
| { | ||
| return Ok(resolved); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Got to say, not the nicest bit of code but I also tried to find a way to use tokio_util::io::StreamReader here and I don't think it's necessarily possible.
There was a problem hiding this comment.
Yeah it kind of stinks but 🤷♀️ it's not horrible at the same time
crates/uv-bin-install/src/lib.rs
Outdated
| let line_str = std::str::from_utf8(&buffer)?; | ||
| if !line_str.trim().is_empty() { | ||
| let version_info: BinVersionInfo = | ||
| serde_json::from_str(line_str).map_err(|source| Error::ManifestParse { source })?; | ||
|
|
||
| if let Some(resolved) = | ||
| check_version_match(&version_info, constraints, exclude_newer, platform_name) | ||
| { | ||
| return Ok(resolved); | ||
| } | ||
| } |
There was a problem hiding this comment.
This could be thrown into a lambda or inline function just so we don't have it copied here and above.
68abeac to
8a67f7c
Compare
8a67f7c to
c96278f
Compare
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [uv](https://github.com/astral-sh/uv) | patch | `0.10.2` → `0.10.3` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>astral-sh/uv (uv)</summary> ### [`v0.10.3`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#0103) [Compare Source](astral-sh/uv@0.10.2...0.10.3) Released on 2026-02-16. ##### Python - Add CPython 3.15.0a6 ##### Enhancements - Don't open file locks for writing ([#​17956](astral-sh/uv#17956)) - Make Windows trampoline error messages consistent with uv proper ([#​17969](astral-sh/uv#17969)) - Log which preview features are enabled ([#​17968](astral-sh/uv#17968)) ##### Preview features - Add support for ruff version constraints and `exclude-newer` in `uv format` ([#​17651](astral-sh/uv#17651)) - Fix script path handling when `target-workspace-discovery` is enabled ([#​17965](astral-sh/uv#17965)) - Use version constraints to select the default ruff version used by `uv format` ([#​17977](astral-sh/uv#17977)) ##### Bug fixes - Avoid matching managed Python versions by prefixes, e.g. don't match CPython 3.10 when `cpython-3.1` is specified ([#​17972](astral-sh/uv#17972)) - Fix handling of `--allow-existing` with minor version links on Windows ([#​17978](astral-sh/uv#17978)) - Fix panic when encountering unmanaged workspace members ([#​17974](astral-sh/uv#17974)) - Improve accuracy of request timing ([#​18007](astral-sh/uv#18007)) - Reject `u64::MAX` in version segments to prevent overflow ([#​17985](astral-sh/uv#17985)) ##### Documentation - Reference Debian Trixie instead of Bookworm ([#​17991](astral-sh/uv#17991)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNS4yIiwidXBkYXRlZEluVmVyIjoiNDMuMTUuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6OnBhdGNoIl19-->
I'm picking up some pretty old work here prompted by astral-sh/setup-uv#737 and a desire to be able to fetch newer
python-build-standaloneversions.Previously, we only supported a static version which means we can construct a known GitHub asset URL trivially. However, to support the "latest" version or version constraints, we need a registry with metadata. The GitHub API is notoriously rate limited, so we don't want to use that. It'd be great to use PyPI (and more broadly, the resolver), but I don't want to introduce it in this code path yet. Instead, this hits https://github.com/astral-sh/versions in order to determine the available versions. We stream the NDJSON line by line to avoid downloading the whole file in order to read one version.
Loosely requires #17648 to reach production and be ported to
ruff, though it's not a blocker.