Skip to content

Comments

Add support for ruff version constraints and exclude-newer in uv format#17651

Merged
zanieb merged 4 commits intomainfrom
zb/ruff-version-fetch
Feb 11, 2026
Merged

Add support for ruff version constraints and exclude-newer in uv format#17651
zanieb merged 4 commits intomainfrom
zb/ruff-version-fetch

Conversation

@zanieb
Copy link
Member

@zanieb zanieb commented Jan 21, 2026

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-standalone versions.

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.

@zanieb zanieb added the preview Experimental behavior label Jan 21, 2026
@zanieb zanieb force-pushed the zb/ruff-version-fetch branch 4 times, most recently from 101d3b0 to 3e0cfe1 Compare January 27, 2026 01:01
@zanieb zanieb marked this pull request as ready for review February 3, 2026 20:27
@zanieb zanieb temporarily deployed to uv-test-publish February 3, 2026 20:59 — with GitHub Actions Inactive
Comment on lines 103 to 112
let path = bin_install(
Binary::Ruff,
&version,
&client,
&retry_policy,
&cache,
&reporter,
)
.await
.with_context(|| format!("Failed to install ruff {version}"))?;
Copy link
Contributor

@EliteTK EliteTK Feb 6, 2026

Choose a reason for hiding this comment

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

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.

Comment on lines +475 to +479
let archive_format = match artifact.archive_format.as_str() {
"tar.gz" => ArchiveFormat::TarGz,
"zip" => ArchiveFormat::Zip,
_ => continue,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Member Author

Choose a reason for hiding this comment

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

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.

Copy link
Member Author

Choose a reason for hiding this comment

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

(I believe I made this a little clearer)

}

/// Find an artifact for the given platform from the version info.
fn find_artifact_for_platform(
Copy link
Contributor

Choose a reason for hiding this comment

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

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.


#[error("Failed to parse version manifest")]
ManifestParse {
#[source]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it intentional that this is a #[source] in a normal struct and ManifestUtf8 below is a #[from] in a tuple struct?

Comment on lines 369 to 403
// 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);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah it kind of stinks but 🤷‍♀️ it's not horrible at the same time

Comment on lines 407 to 417
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);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be thrown into a lambda or inline function just so we don't have it copied here and above.

@zanieb zanieb temporarily deployed to uv-test-publish February 6, 2026 22:07 — with GitHub Actions Inactive
@zanieb zanieb force-pushed the zb/ruff-version-fetch branch from 68abeac to 8a67f7c Compare February 10, 2026 02:31
@zanieb zanieb force-pushed the zb/ruff-version-fetch branch from 8a67f7c to c96278f Compare February 11, 2026 16:03
@zanieb zanieb merged commit d07c5cf into main Feb 11, 2026
190 of 198 checks passed
@zanieb zanieb deleted the zb/ruff-version-fetch branch February 11, 2026 16:54
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Feb 17, 2026
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 ([#&#8203;17956](astral-sh/uv#17956))
- Make Windows trampoline error messages consistent with uv proper ([#&#8203;17969](astral-sh/uv#17969))
- Log which preview features are enabled ([#&#8203;17968](astral-sh/uv#17968))

##### Preview features

- Add support for ruff version constraints and `exclude-newer` in `uv format` ([#&#8203;17651](astral-sh/uv#17651))
- Fix script path handling when `target-workspace-discovery` is enabled ([#&#8203;17965](astral-sh/uv#17965))
- Use version constraints to select the default ruff version used by `uv format` ([#&#8203;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 ([#&#8203;17972](astral-sh/uv#17972))
- Fix handling of `--allow-existing` with minor version links on Windows ([#&#8203;17978](astral-sh/uv#17978))
- Fix panic when encountering unmanaged workspace members ([#&#8203;17974](astral-sh/uv#17974))
- Improve accuracy of request timing ([#&#8203;18007](astral-sh/uv#18007))
- Reject `u64::MAX` in version segments to prevent overflow ([#&#8203;17985](astral-sh/uv#17985))

##### Documentation

- Reference Debian Trixie instead of Bookworm ([#&#8203;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-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Experimental behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants