Skip to content

Conversation

@aldanor
Copy link
Contributor

@aldanor aldanor commented May 15, 2025

Summary

(Related PR: #13438 - would be nice to have it merged as well since it touches on the same globwalker code)

There's a few issues with cache-key globs, which this PR attempts to address:

  • As of the current state, parent or absolute paths are not allowed, which is not obvious and is not documented. E.g., cache-key paths of the form {file = "../dep/**"} will be essentially ignored.
  • Absolute glob patterns also don't work (funnily enough, there's logic in globwalk itself that attempts to address it in globwalk::glob_builder(), which serves as inspiration to some parts of this PR).
  • The reason for parent paths being ignored is the way globwalker is currently being triggered in uv-cache-info: the base directory is being walked over completely and each entry is then being matched to one of the provided match patterns.
  • This may also end up being very inefficient if you have a huge root folder with thousands of files: if your match patterns are a/b/*.rs and a/c/*.py then instead of walking over the root directory, you can just walk over a/b and a/c and match the relevant patterns there.
  • Why supporting parent paths may be important to the point of being a blocker: in large codebases with python projects depending on other local non-python projects (e.g. rust crates), cache-keys can be very useful to track dependency on the source code of the latter (e.g. cache-keys = [{ file = "../../crates/some-dep/**" }].
  • TLDR: parent/absolute cache-key globs don't work, glob walk can be slow.

Solution

  • In this PR, user-provided glob patterns are first clustered (LCP-style) into pattern groups with longest common path prefix; each of these groups can then be walked over separately.
  • Pattern groups do not overlap, so we would never walk over the same directory twice (unless there's symlinks pointing to same folders).
  • Paths are not canonicalized nor virtually normalized (which is impossible on Unix without FS access), so the method is symlink-safe (i.e. we don't treat a/b/.. as a) and should work fine with Follow links when cache-key is a glob #13438.
  • Because of LCP logic, the minimal amount of directory space will be traversed to cover all patterns.
  • Absolute glob patterns will now work.
  • Parent-relative glob patterns will now work.
  • Glob walking will be more efficient in some cases.

Possible improvements

  • Efficiency can be further greatly improved if we limit max depth for globwalk. Currently, a simple ".toml" will deep-traverse the whole folder. Essentially, max depth can be always set to either N or infinity. If a pattern at a pivot node contains **, we collect all children nodes from the subtree into the same group and don't limit max depth; otherwise, we set max depth to the length of the glob pattern. This wouldn't change correctness though and can we done separately if needed.
  • If this is considered important enough, docs can be updated to indicate that parent and absolute globs are supported (and symlinks are resolved, if the relevant PR is algo merged in).

Test Plan

  • Glob splitting and clustering tests are included in the PR.
  • Relative and absolute glob cache-keys were tested in an actual codebase.

@konstin konstin requested a review from BurntSushi May 15, 2025 14:08
@aldanor aldanor force-pushed the feat/resolve-cache-key-globs branch 3 times, most recently from db64335 to ff2c2df Compare May 15, 2025 15:49
Copy link
Member

@BurntSushi BurntSushi left a comment

Choose a reason for hiding this comment

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

I started reviewing this, but realized that before I got too much into the weeds, it would be good to check the motivation first: do you have any benchmarks for this? The more realistic, the better. I do grant that there is likely some improvement here, but it's important to contextualize it with 1) how common that case is and 2) if the improvement is worth the extra implementation cost here.

@aldanor
Copy link
Contributor Author

aldanor commented May 15, 2025

I started reviewing this, but realized that before I got too much into the weeds, it would be good to check the motivation first: do you have any benchmarks for this? The more realistic, the better. I do grant that there is likely some improvement here, but it's important to contextualize it with 1) how common that case is and 2) if the improvement is worth the extra implementation cost here.

@BurntSushi - thanks again for taking a look.

I think PR title/description might be a bit confusing - sorry about that and let me try to explain:

  • The main point (and the blocker we are currently facing in a production codebase) is being able to use relative/parent paths in cache-key globs, to define dependencies on local folders that are not descendants of the current python project.
  • The way globwalk is currently being used, it's being called with project directory as the base, and simply traverses all of it. Of course, this will not catch any entries outside of CWD.
  • In order to cover patterns that may contain (or start with) .., we would have to:
    1. do multiple globwalks
    2. figure out which base directories to use for those globwalks
  • Point (ii) is the reason for split_glob() to exist (which is somewhat similar to glob_builder() in globwalk crate).
  • As for (i), if we simply kick off a globwalk for each glob entry, we will traverse the same folders multiple times. In order to not do that, we need some LCP logic - which is the reason for cluster_globs() to exist.
  • Finally, - and it just happens so, coincidentally and automatically, - we also happen to make the general use case more efficient in case your CWD has a deep tree and many entries but globs only point to particular parts of it. This is not the main intent of this PR, but more of a side bonus. There should be no perf regressions but sometimes there will be random perf wins.
    • As mentioned above, if we really want to make it as efficient as possible, we can also set max depth based on glob patterns and not always walk through the entire directory tree with infinite depth (this will also change the way globs are grouped into clusters). If need be, it can be implemented as a separate PR.

@konstin
Copy link
Member

konstin commented May 15, 2025

The PR looks great algorithmically, but I'm wondering if we can have less of that complexity in uv itself? We would be adding a custom trie having to maintain and explain it for something that's not a core algorithm, are there maybe off the shelve components we could use? Could these optimizations be implemented in one of the ecosystem globbing/walkdir crates which use?

Can you describe how the real-world project looks like that motivated these changes, and how uv is currently failing on it?

@aldanor
Copy link
Contributor Author

aldanor commented May 15, 2025

@konstin

Can you describe how the real-world project looks like that motivated these changes, and how uv is currently failing on it?

As mentioned above, we're currently facing a pretty heavy blocker because of the lack of relative/parent glob support in a big production codebase, where we have lots of pyo3/maturin python packages that depend on both pure-rust crates and/or static content that may affect the builds. In order to track freshness, we'd like to use relative globs like

[tool.uv]
cache-keys = [
    { file = "../../crates/foo/**" },
    { file = "../some/files/**.txt" },
]

which uv currently doesn't support because of the way globwalk is being used.

I'm wondering if we can have less of that complexity in uv itself? We would be adding a custom trie having to maintain and explain it for something that's not a core algorithm, are there maybe off the shelve components we could use? Could these optimizations be implemented in one of the ecosystem globbing/walkdir crates which use?

I couldn't find any glob-specific LCPs/tries... there's some generic trie crates but that sounds like an overkill and they provide too much functionality for what's needed here (also LCP grouping here is a bit special to globs - i.e. handling normal/non-normal edges differently).

Technically, it could be either

  • slammed into the globwalk crate itself, although it wouldn't be my preference since (a) it would change its core logic in an incompatible way and/or complicate its api and (b) it's marked by author as 'in perpetual maintenance, use other crates'.
  • made into a globwalk2 crate with default support for trie-based glob clustering, base dir inference, relative/absolute globs, max depth limiting, skipping irrelevant dirs, etc... although then it would have to copy/paste lots of code from walkdir/ignore to be self-sufficient.
  • a separate crate that takes (base, patterns) and returns vec<(base, patterns)>.
  • integrated into ignore crate?.. but it's used by ripgrep, plus globs outside of base dirs don't really match what it's doing.

I guess one of the latter is doable and I can take on that but is quite a lot more work, and if you have a strong preference to not have this code inside uv, wonder if it makes sense to get this merged into uv first to unblock and pull it out next? Btw globwalk is currently only being used in uv-workspace and uv-cache-info.

@aldanor
Copy link
Contributor Author

aldanor commented May 27, 2025

Hey folks - I went with the 3rd option above and took some time to clean it up, add tests, fix edge cases and make it into a standalone crate (which may perhaps be useful to the general public since it fixes most reported globwalk issues, errors and performance problems).

Would that be acceptable here? (cc @konstin) If yes, I can open a separate PR replacing globwalk. Feature-wise, I think it covers all the bases except ignore files (e.g. .gitignore), is it needed as of right now? (in which case I can try to quickly add it as well)

@BurntSushi would really appreciate if you could take a brief look, because it basically joins two crates of your authorship, in a somewhat obscure way 🙂 aldanor/multiglob#1

@konstin
Copy link
Member

konstin commented Jun 2, 2025

My main concern is that we want the same glob implementation across uv; this is already non-ideal as we're using both glob and globset, but we shouldn't add yet another implementation, rather reduce the differences. Specifically, have you seen the uv-globfilter crate? It implements optimizations very similar to the ones you describe.

I'm not sure if we want parent references and absolute paths for globs. Projects are usually intended to be self-contained so they can be built into source dists and wheels, and this breaks this isolation.

For Rust projects specifically, have you seen https://github.com/PyO3/maturin-import-hook?

As mentioned above, we're currently facing a pretty heavy blocker because of the lack of relative/parent glob support in a big production codebase, where we have lots of pyo3/maturin python packages that depend on both pure-rust crates and/or static content that may affect the builds. In order to track freshness, we'd like to use relative globs like

[tool.uv]
cache-keys = [
    { file = "../../crates/foo/**" },
    { file = "../some/files/**.txt" },
]

which uv currently doesn't support because of the way globwalk is being used.

Is the blocker specifically that the lack relative references requires manual rebuilds, or is there also a performance problem?

@aldanor
Copy link
Contributor Author

aldanor commented Jun 2, 2025

@konstin Let me try to clarify some of the points:

  • Is the blocker specifically that the lack relative references requires manual rebuilds, or is there also a performance problem?

    Primarily the lack of relative references. In large codebases I think disallowing relative references in cache keys is a restriction way too harsh.

    Performance-wise - it's just if you allow relative/absolute references and you have multiple base folders, so if you have 10 glob patterns and if you don't want to kick off 10 full glob walks, you might have to think of how to do it efficiently, see similar problems in e.g. globwalk looks at all files even when it doesn't need to Gilnaa/globwalk#29, skip walking irrelevant directories when matching globs Gilnaa/globwalk#31, etc). These problems are addressed in uv-globfilter but will pop back up if you allow multiple bases / rel paths.

  • Specifically, have you seen the uv-globfilter crate? It implements optimizations very similar to the ones you describe.

    Yea, I've seen it; it's somewhat close but would not work out of the box with relative paths because again, essentially there will be many overlapping bases.

  • For Rust projects specifically, have you seen https://github.com/PyO3/maturin-import-hook?

    Yea I'm aware of that, but doesn't quite suit our use case (> 100 crates, many dozen pyo3 projects, cross compilation required, not for 'local development'). We are looking for a clean way for uv to figure when particular packages need to get rebuilt if their dependencies changes (whether rust dependencies of pyo3 crates like ../dep-1/** or static files like protobuf schemas).

  • as we're using both glob and globset

  • I'm wondering if we can have less of that complexity in uv itself

    The initial reason I spent considerable amount of time pulling it into a separate crate (multiglob) was you suggesting uv shouldn't have this complexity internally :) Given that there's no working options supporting relpaths neither in uv nor in crates.io, I went with a new standalone crate which should now be pretty well tested; it's based on walkdir+globset, somewhat similar to uv-globfilter (but can be further aligned to uv-globfilter behaviour if need be, if there's any particular points).

  • this is already non-ideal as we're using both glob and globset, but we shouldn't add yet another implementation, rather reduce the differences

    The suggestion here is to remove one dependency (globwalk) and replace it with another (multiglob) which is also based on walkdir+globset.

Thanks!

patterns: Vec<&'a Path>,
}

impl<'a> Trie<'a> {
Copy link
Member

Choose a reason for hiding this comment

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

It looks like this Trie implementation makes pretty heavy use of recursion. And the call depth is in turn proportional to the length of data controlled by end users. That's generally something I think we should try to avoid. Although, in this case, paths are likely limited in length in most cases to a point where the call depth isn't going to get deep enough to overflow the stack. But I'm a little uneasy about relying on such things since that's more of a platform restriction as opposed to something we check and enforce ourselves.

Copy link
Contributor Author

@aldanor aldanor Jun 4, 2025

Choose a reason for hiding this comment

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

aldanor/multiglob@bbd43be

(fixed in that PR because there's some edge cases fixed there and lots of tests, including multi-platform ones)

// Apologies for all the confusion, I decided to pull it out into a standalone crate after @konstin asked if we can have less complexity in uv itself; but this also allowed to thoroughly go through all the multi-platform edge cases and write tons of tests. If uv requires this crate under its control I'm ok with passing ownership or whatever else you guys want (although it may be pretty useful for general public).

Copy link
Member

Choose a reason for hiding this comment

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

Personally, I'd rather the small targeted change you have in this PR. Maintaining a whole other crate for this is a lot to ask.

I'm not keen on depending on a crate made just so that the code doesn't live in uv proper. Its long term maintenance story isn't clear to me. I can't speak for @konstin, but "off the shelf components" to me means existing components. Sorry about the confusion here.

Copy link
Member

Choose a reason for hiding this comment

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

I'm different to @BurntSushi here for maintainability, he has more experience with glob implementations than me

@BurntSushi
Copy link
Member

I'm personally in favor of bringing this in without adding a dependency on a new crate. I think I grok the main logic here decently enough and the trie here looks pretty simple. Are you okay with that @konstin?

@aldanor aldanor force-pushed the feat/resolve-cache-key-globs branch from ee1cb13 to 90a365c Compare July 10, 2025 23:52
@BurntSushi
Copy link
Member

@aldanor Is there anything that your multiglob PR does that isn't addressed by the smaller code change here?

@aldanor
Copy link
Contributor Author

aldanor commented Jul 11, 2025

@aldanor Is there anything that your multiglob PR does that isn't addressed by the smaller code change here?

@BurntSushi I think it would be pretty close to being identical, except in multiglob there's no recursion (that you mentioned above in a comment), a few parts are cleaned up and lots more tests for the whole thing. But functionally, from uv's standpoint, there's not going to be much of a difference I guess. There may appear to be lots of code in multiglob PR because it has to replicate all the outer walkdir API plus there's tests boilerplate.

If this PR is looking more or less ok, my personal suggestion would be to:

  • try to get this pr over the line (unless there's any other feedback?), it's more self-contained
  • I should probably just publish multiglob separately, may be useful to other people in the ecosystem? (as an alternative to globwalk)
  • if you guys ever feel like switching off from globwalk that would be always doable

Copy link
Member

@BurntSushi BurntSushi left a comment

Choose a reason for hiding this comment

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

I think this LGTM. I feel comfortable that if we find a bug in this code, I could dive into it and fix it without too much trouble.

@zanieb zanieb merged commit 567468c into astral-sh:main Jul 11, 2025
92 checks passed
@aldanor
Copy link
Contributor Author

aldanor commented Jul 11, 2025

Thanks @BurntSushi and @konstin for your time reviewing and getting this through, really appreciated!

@zanieb zanieb mentioned this pull request Jul 14, 2025
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Jul 27, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [astral-sh/uv](https://github.com/astral-sh/uv) | minor | `0.7.19` -> `0.8.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 (astral-sh/uv)</summary>

### [`v0.8.3`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#083)

[Compare Source](astral-sh/uv@0.8.2...0.8.3)

##### Python

- Add CPython 3.14.0rc1

See the [`python-build-standalone` release notes](https://github.com/astral-sh/python-build-standalone/releases/tag/20250723) for more details.

##### Enhancements

- Allow non-standard entrypoint names in `uv_build` ([#&#8203;14867](astral-sh/uv#14867))
- Publish riscv64 wheels to PyPI ([#&#8203;14852](astral-sh/uv#14852))

##### Bug fixes

- Avoid writing redacted credentials to tool receipt ([#&#8203;14855](astral-sh/uv#14855))
- Respect `--with` versions over base environment versions ([#&#8203;14863](astral-sh/uv#14863))
- Respect credentials from all defined indexes ([#&#8203;14858](astral-sh/uv#14858))
- Fix missed stabilization of removal of registry entry during Python uninstall ([#&#8203;14859](astral-sh/uv#14859))
- Improve concurrency safety of Python downloads into cache ([#&#8203;14846](astral-sh/uv#14846))

##### Documentation

- Fix typos in `uv_build` reference documentation ([#&#8203;14853](astral-sh/uv#14853))
- Move the "Cargo" install method further down in docs ([#&#8203;14842](astral-sh/uv#14842))

### [`v0.8.2`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#082)

[Compare Source](astral-sh/uv@0.8.1...0.8.2)

##### Enhancements

- Add derivation chains for dependency errors ([#&#8203;14824](astral-sh/uv#14824))

##### Configuration

- Add `UV_INIT_BUILD_BACKEND` ([#&#8203;14821](astral-sh/uv#14821))

##### Bug fixes

- Avoid reading files in the environment bin that are not entrypoints ([#&#8203;14830](astral-sh/uv#14830))
- Avoid removing empty directories when constructing virtual environments ([#&#8203;14822](astral-sh/uv#14822))
- Preserve index URL priority order when writing to pyproject.toml ([#&#8203;14831](astral-sh/uv#14831))

##### Rust API

- Expose `tls_built_in_root_certs` for client ([#&#8203;14816](astral-sh/uv#14816))

##### Documentation

- Archive the 0.7.x changelog ([#&#8203;14819](astral-sh/uv#14819))

### [`v0.8.1`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#081)

[Compare Source](astral-sh/uv@0.8.0...0.8.1)

##### Enhancements

- Add support for `HF_TOKEN` ([#&#8203;14797](astral-sh/uv#14797))
- Allow `--config-settings-package` to apply configuration settings at the package level ([#&#8203;14573](astral-sh/uv#14573))
- Create (e.g.) `python3.13t` executables in `uv venv` ([#&#8203;14764](astral-sh/uv#14764))
- Disallow writing symlinks outside the source distribution target directory ([#&#8203;12259](astral-sh/uv#12259))
- Elide traceback when `python -m uv` in interrupted with Ctrl-C on Windows ([#&#8203;14715](astral-sh/uv#14715))
- Match `--bounds` formatting for `uv_build` bounds in `uv init` ([#&#8203;14731](astral-sh/uv#14731))
- Support `extras` and `dependency_groups` markers in PEP 508 grammar ([#&#8203;14753](astral-sh/uv#14753))
- Support `extras` and `dependency_groups` markers on `uv pip install` and `uv pip sync` ([#&#8203;14755](astral-sh/uv#14755))
- Add hint to use `uv self version` when `uv version` cannot find a project ([#&#8203;14738](astral-sh/uv#14738))
- Improve error reporting when removing Python versions from the Windows registry ([#&#8203;14722](astral-sh/uv#14722))
- Make warnings about masked `[tool.uv]` fields more precise ([#&#8203;14325](astral-sh/uv#14325))

##### Preview features

- Emit JSON output in `uv sync` with `--quiet` ([#&#8203;14810](astral-sh/uv#14810))

##### Bug fixes

- Allow removal of virtual environments with missing interpreters ([#&#8203;14812](astral-sh/uv#14812))
- Apply `Cache-Control` overrides to response, not request headers ([#&#8203;14736](astral-sh/uv#14736))
- Copy entry points into ephemeral environments to ensure layers are respected ([#&#8203;14790](astral-sh/uv#14790))
- Workaround Jupyter Lab application directory discovery in ephemeral environments ([#&#8203;14790](astral-sh/uv#14790))
- Enforce `requires-python` in `pylock.toml` ([#&#8203;14787](astral-sh/uv#14787))
- Fix kebab casing of `README` variants in build backend ([#&#8203;14762](astral-sh/uv#14762))
- Improve concurrency resilience of removing Python versions from the Windows registry ([#&#8203;14717](astral-sh/uv#14717))
- Retry HTTP requests on invalid data errors ([#&#8203;14703](astral-sh/uv#14703))
- Update virtual environment removal to delete `pyvenv.cfg` last ([#&#8203;14808](astral-sh/uv#14808))
- Error on unknown fields in `dependency-metadata` ([#&#8203;14801](astral-sh/uv#14801))

##### Documentation

- Recommend installing `setup-uv` after `setup-python` in Github Actions integration guide ([#&#8203;14741](astral-sh/uv#14741))
- Clarify which portions of `requires-python` behavior are consistent with pip ([#&#8203;14752](astral-sh/uv#14752))

### [`v0.8.0`](https://github.com/astral-sh/uv/blob/HEAD/CHANGELOG.md#080)

[Compare Source](astral-sh/uv@0.7.22...0.8.0)

Since we released uv [0.7.0](https://github.com/astral-sh/uv/releases/tag/0.7.0) in April, we've accumulated various changes that improve correctness and user experience, but could break some workflows. This release contains those changes; many have been marked as breaking out of an abundance of caution. We expect most users to be able to upgrade without making changes.

This release also includes the stabilization of a couple `uv python install` features, which have been available under preview since late last year.

##### Breaking changes

- **Install Python executables into a directory on the `PATH` ([#&#8203;14626](astral-sh/uv#14626

  `uv python install` now installs a versioned Python executable (e.g., `python3.13`) into a directory on the `PATH` (e.g., `~/.local/bin`) by default. This behavior has been available under the `--preview` flag since [Oct 2024](astral-sh/uv#8458). This change should not be breaking unless it shadows a Python executable elsewhere on the `PATH`.

  To install unversioned executables, i.e., `python3` and `python`, use the `--default` flag. The `--default` flag has also been in preview, but is not stabilized in this release.

  Note that these executables point to the base Python installation and only include the standard library. That means they will not include dependencies from your current project (use `uv run python` instead) and you cannot install packages into their environment (use `uvx --with <package> python` instead).

  As with tool installation, the target directory respects common variables like `XDG_BIN_HOME` and can be overridden with a `UV_PYTHON_BIN_DIR` variable.

  You can opt out of this behavior with `uv python install --no-bin` or `UV_PYTHON_INSTALL_BIN=0`.

  See the [documentation on installing Python executables](https://docs.astral.sh/uv/concepts/python-versions/#installing-python-executables) for more details.
- **Register Python versions with the Windows Registry ([#&#8203;14625](astral-sh/uv#14625

  `uv python install` now registers the installed Python version with the Windows Registry as specified by [PEP 514](https://peps.python.org/pep-0514/). This allows using uv installed Python versions via the `py` launcher. This behavior has been available under the `--preview` flag since [Jan 2025](astral-sh/uv#10634). This change should not be breaking, as using the uv Python versions with `py` requires explicit opt in.

  You can opt out of this behavior with `uv python install --no-registry` or `UV_PYTHON_INSTALL_REGISTRY=0`.
- **Prompt before removing an existing directory in `uv venv` ([#&#8203;14309](astral-sh/uv#14309

  Previously, `uv venv` would remove an existing virtual environment without confirmation. While this is consistent with the behavior of project commands (e.g., `uv sync`), it's surprising to users that are using imperative workflows (i.e., `uv pip`). Now, `uv venv` will prompt for confirmation before removing an existing virtual environment. **If not in an interactive context, uv will still remove the virtual environment for backwards compatibility. However, this behavior is likely to change in a future release.**

  The behavior for other commands (e.g., `uv sync`) is unchanged.

  You can opt out of this behavior by setting `UV_VENV_CLEAR=1` or passing the `--clear` flag.
- **Validate that discovered interpreters meet the Python preference ([#&#8203;7934](astral-sh/uv#7934

  uv allows opting out of its managed Python versions with the `--no-managed-python` and `python-preference` options.

  Previously, uv would not enforce this option for Python interpreters discovered on the `PATH`. For example, if a symlink to a managed Python interpreter was created, uv would allow it to be used even if `--no-managed-python` was provided. Now, uv ignores Python interpreters that do not match the Python preference *unless* they are in an active virtual environment or are explicitly requested, e.g., with `--python /path/to/python3.13`.

  Similarly, uv would previously not invalidate existing project environments if they did not match the Python preference. Now, uv will invalidate and recreate project environments when the Python preference changes.

  You can opt out of this behavior by providing the explicit path to the Python interpreter providing `--managed-python` / `--no-managed-python` matching the interpreter you want.
- **Install dependencies without build systems when they are `path` sources ([#&#8203;14413](astral-sh/uv#14413

  When working on a project, uv uses the [presence of a build system](https://docs.astral.sh/uv/concepts/projects/config/#build-systems) to determine if it should be built and installed into the environment. However, when a project is a dependency of another project, it can be surprising for the dependency to be missing from the environment.

  Previously, uv would not build and install dependencies with [`path` sources](https://docs.astral.sh/uv/concepts/projects/dependencies/#path) unless they declared a build system or set `tool.uv.package = true`. Now, dependencies with `path` sources are built and installed regardless of the presence of a build system. If a build system is not present, the `setuptools.build_meta:__legacy__ ` backend will be used (per [PEP 517](https://peps.python.org/pep-0517/#source-trees)).

  You can opt out of this behavior by setting `package = false` in the source declaration, e.g.:

  ```toml
  [tool.uv.sources]
  foo = { path = "./foo", package = false }
  ```

  Or, by setting `tool.uv.package = false` in the dependent `pyproject.toml`.

  See the documentation on [virtual dependencies](https://docs.astral.sh/uv/concepts/projects/dependencies/#virtual-dependencies) for details.
- **Install dependencies without build systems when they are workspace members ([#&#8203;14663](astral-sh/uv#14663

  As described above for dependencies with `path` sources, uv previously would not build and install workspace members that did not declare a build system. Now, uv will build and install workspace members that are a dependency of *another* workspace member regardless of the presence of a build system. The behavior is unchanged for workspace members that are not included in the `project.dependencies`, `project.optional-dependencies`, or `dependency-groups` tables of another workspace member.

  You can opt out of this behavior by setting `tool.uv.package = false` in the workspace member's `pyproject.toml`.

  See the documentation on [virtual dependencies](https://docs.astral.sh/uv/concepts/projects/dependencies/#virtual-dependencies) for details.
- **Bump `--python-platform linux` to `manylinux_2_28` ([#&#8203;14300](astral-sh/uv#14300

  uv allows performing [platform-specific resolution](https://docs.astral.sh/uv/concepts/resolution/#platform-specific-resolution) for explicit targets and provides short aliases, e.g., `linux`, for common targets.

  Previously, the default target for `--python-platform linux` was `manylinux_2_17`, which is compatible with most Linux distributions from 2014 or newer. We now default to `manylinux_2_28`, which is compatible with most Linux distributions from 2019 or newer.  This change follows the lead of other tools, such as `cibuildwheel`, which changed their default to `manylinux_2_28` in [Mar 2025](pypa/cibuildwheel#2330).

  This change only affects users requesting a specific target platform. Otherwise, uv detects the `manylinux` target from your local glibc version.

  You can opt out of this behavior by using `--python-platform x86_64-manylinux_2_17` instead.
- **Remove `uv version` fallback ([#&#8203;14161](astral-sh/uv#14161

  In [Apr 2025](astral-sh/uv#12349), uv changed the `uv version` command to an interface for viewing and updating the version of the current project. However, when outside a project, `uv version` would continue to display uv's version for backwards compatibility. Now, when used outside of a project, `uv version` will fail.

  You cannot opt out of this behavior. Use `uv self version` instead.
- **Require `--global` for removal of the global Python pin ([#&#8203;14169](astral-sh/uv#14169

  Previously, `uv python pin --rm` would allow you to remove the global Python pin without opt in. Now, uv requires the `--global` flag to remove the global Python pin.

  You cannot opt out of this behavior. Use the `--global` flag instead.
- **Support conflicting editable settings across groups ([#&#8203;14197](astral-sh/uv#14197

  Previously, uv would always treat a package as editable if any requirement requested it as editable. However, this prevented users from declaring `path` sources that toggled the `editable` setting across dependency groups. Now, uv allows declaring different `editable` values for conflicting groups. However, if a project includes a path dependency twice, once with `editable = true` and once without any editable annotation, those are now considered conflicting, and uv will exit with an error.

  You cannot opt out of this behavior. Use consistent `editable` settings or [mark groups as conflicting](https://docs.astral.sh/uv/concepts/projects/config/#conflicting-dependencies).
- **Make `uv_build` the default build backend in `uv init` ([#&#8203;14661](astral-sh/uv#14661

  The uv build backend (`uv_build`) was [stabilized in uv 0.7.19](https://github.com/astral-sh/uv/releases/tag/0.7.19). Now, it is the default build backend for `uv init --package` and `uv init --lib`. Previously, `hatchling` was the default build backend. A build backend is still not used without opt-in in `uv init`, but we expect to change this in a future release.

  You can opt out of this behavior with `uv init --build-backend hatchling`.
- **Set default `UV_TOOL_BIN_DIR` on Docker images ([#&#8203;13391](astral-sh/uv#13391

  Previously, `UV_TOOL_BIN_DIR` was not set in Docker images which meant that `uv tool install` did not install tools into a directory on the `PATH` without additional configuration. Now, `UV_TOOL_BIN_DIR` is set to `/usr/local/bin` in all Docker derived images.

  When the default image user is overridden (e.g. `USER <UID>`) with a less privileged user, this may cause `uv tool install` to fail.

  You can opt out of this behavior by setting an alternative `UV_TOOL_BIN_DIR`.
- **Update `--check` to return an exit code of 1 ([#&#8203;14167](astral-sh/uv#14167

  uv uses an exit code of 1 to indicate a "successful failure" and an exit code of 2 to indicate an "error".

  Previously, `uv lock --check` and `uv sync --check` would exit with a code of 2 when the lockfile or environment were outdated. Now, uv will exit with a code of 1.

  You cannot opt out of this behavior.
- **Use an ephemeral environment for `uv run --with` invocations ([#&#8203;14447](astral-sh/uv#14447

  When using `uv run --with`, uv layers the requirements requested using `--with` into another virtual environment and caches it. Previously, uv would invoke the Python interpreter in this layered environment. However, this allows poisoning the cached environment and introduces race conditions for concurrent invocations. Now, uv will layer *another* empty virtual environment on top of the cached environment and invoke the Python interpreter there. This should only cause breakage in cases where the environment is being inspected at runtime.

  You cannot opt out of this behavior.
- **Restructure the `uv venv` command output and exit codes ([#&#8203;14546](astral-sh/uv#14546

  Previously, uv used `miette` to format the `uv venv` output. However, this was inconsistent with most of the uv CLI. Now, the output is a little different and the exit code has switched from 1 to 2 for some error cases.

  You cannot opt out of this behavior.
- **Default to `--workspace` when adding subdirectories ([#&#8203;14529](astral-sh/uv#14529

  When using `uv add` to add a subdirectory in a workspace, uv now defaults to adding the target as a workspace member.

  You can opt out of this behavior by providing `--no-workspace`.
- **Add missing validations for disallowed `uv.toml` fields ([#&#8203;14322](astral-sh/uv#14322

  uv does not allow some settings in the `uv.toml`. Previously, some settings were silently ignored when present in the `uv.toml`. Now, uv will error.

  You cannot opt out of this behavior. Use `--no-config` or remove the invalid settings.

##### Configuration

- Add support for toggling Python bin and registry install options via env vars ([#&#8203;14662](astral-sh/uv#14662))

### [`v0.7.22`](https://github.com/astral-sh/uv/releases/tag/0.7.22)

[Compare Source](astral-sh/uv@0.7.21...0.7.22)

#### Release Notes

##### Python

- Upgrade GraalPy to 24.2.2

See the [GraalPy release notes](https://github.com/oracle/graalpython/releases/tag/graal-24.2.2) for more details.

##### Configuration

- Add `UV_COMPILE_BYTECODE_TIMEOUT` environment variable ([#&#8203;14369](astral-sh/uv#14369))
- Allow users to override index `cache-control` headers ([#&#8203;14620](astral-sh/uv#14620))
- Add `UV_LIBC` to override libc selection in multi-libc environment ([#&#8203;14646](astral-sh/uv#14646))

##### Bug fixes

- Fix `--all-arches` when paired with `--only-downloads` ([#&#8203;14629](astral-sh/uv#14629))
- Skip Windows Python interpreters that return a broken MSIX package code ([#&#8203;14636](astral-sh/uv#14636))
- Warn on invalid `uv.toml` when provided via direct path ([#&#8203;14653](astral-sh/uv#14653))
- Improve async signal safety in Windows exception handler ([#&#8203;14619](astral-sh/uv#14619))

##### Documentation

- Mention the `revision` in the lockfile versioning doc ([#&#8203;14634](astral-sh/uv#14634))
- Move "Conflicting dependencies" to the "Resolution" page ([#&#8203;14633](astral-sh/uv#14633))
- Rename "Dependency specifiers" section to exclude PEP 508 reference ([#&#8203;14631](astral-sh/uv#14631))
- Suggest `uv cache clean` prior to `--reinstall` ([#&#8203;14659](astral-sh/uv#14659))

##### Preview features

- Make preview Python registration on Windows non-fatal ([#&#8203;14614](astral-sh/uv#14614))
- Update preview installation of Python executables to be non-fatal ([#&#8203;14612](astral-sh/uv#14612))
- Add `uv python update-shell` ([#&#8203;14627](astral-sh/uv#14627))

#### Install uv 0.7.22

##### Install prebuilt binaries via shell script

```sh
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.7.22/uv-installer.sh | sh
```

##### Install prebuilt binaries via powershell script

```sh
powershell -ExecutionPolicy Bypass -c "irm https://github.com/astral-sh/uv/releases/download/0.7.22/uv-installer.ps1 | iex"
```

#### Download uv 0.7.22

|  File  | Platform | Checksum |
|--------|----------|----------|
| [uv-aarch64-apple-darwin.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-aarch64-apple-darwin.tar.gz) | Apple Silicon macOS | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-aarch64-apple-darwin.tar.gz.sha256) |
| [uv-x86\_64-apple-darwin.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-x86_64-apple-darwin.tar.gz) | Intel macOS | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-x86_64-apple-darwin.tar.gz.sha256) |
| [uv-aarch64-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-aarch64-pc-windows-msvc.zip) | ARM64 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-aarch64-pc-windows-msvc.zip.sha256) |
| [uv-i686-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-i686-pc-windows-msvc.zip) | x86 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-i686-pc-windows-msvc.zip.sha256) |
| [uv-x86\_64-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-x86_64-pc-windows-msvc.zip) | x64 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-x86_64-pc-windows-msvc.zip.sha256) |
| [uv-aarch64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-aarch64-unknown-linux-gnu.tar.gz) | ARM64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-aarch64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-i686-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-i686-unknown-linux-gnu.tar.gz) | x86 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-i686-unknown-linux-gnu.tar.gz.sha256) |
| [uv-powerpc64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-powerpc64-unknown-linux-gnu.tar.gz) | PPC64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-powerpc64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-powerpc64le-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-powerpc64le-unknown-linux-gnu.tar.gz) | PPC64LE Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-powerpc64le-unknown-linux-gnu.tar.gz.sha256) |
| [uv-riscv64gc-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-riscv64gc-unknown-linux-gnu.tar.gz) | RISCV Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-riscv64gc-unknown-linux-gnu.tar.gz.sha256) |
| [uv-s390x-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-s390x-unknown-linux-gnu.tar.gz) | S390x Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-s390x-unknown-linux-gnu.tar.gz.sha256) |
| [uv-x86\_64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-x86_64-unknown-linux-gnu.tar.gz) | x64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-x86_64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-armv7-unknown-linux-gnueabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-armv7-unknown-linux-gnueabihf.tar.gz) | ARMv7 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-armv7-unknown-linux-gnueabihf.tar.gz.sha256) |
| [uv-aarch64-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-aarch64-unknown-linux-musl.tar.gz) | ARM64 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-aarch64-unknown-linux-musl.tar.gz.sha256) |
| [uv-i686-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-i686-unknown-linux-musl.tar.gz) | x86 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-i686-unknown-linux-musl.tar.gz.sha256) |
| [uv-x86\_64-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-x86_64-unknown-linux-musl.tar.gz) | x64 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-x86_64-unknown-linux-musl.tar.gz.sha256) |
| [uv-arm-unknown-linux-musleabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-arm-unknown-linux-musleabihf.tar.gz) | ARMv6 MUSL Linux (Hardfloat) | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-arm-unknown-linux-musleabihf.tar.gz.sha256) |
| [uv-armv7-unknown-linux-musleabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-armv7-unknown-linux-musleabihf.tar.gz) | ARMv7 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.22/uv-armv7-unknown-linux-musleabihf.tar.gz.sha256) |

### [`v0.7.21`](https://github.com/astral-sh/uv/releases/tag/0.7.21)

[Compare Source](astral-sh/uv@0.7.20...0.7.21)

#### Release Notes

##### Python

- Restore the SQLite `fts4`, `fts5`, `rtree`, and `geopoly` extensions on macOS and Linux

See the [`python-build-standalone` release notes](https://github.com/astral-sh/python-build-standalone/releases/tag/20250712) for more details.

##### Enhancements

- Add `--python-platform` to `uv sync` ([#&#8203;14320](astral-sh/uv#14320))
- Support pre-releases in `uv version --bump` ([#&#8203;13578](astral-sh/uv#13578))
- Add `-w` shorthand for `--with` ([#&#8203;14530](astral-sh/uv#14530))
- Add an exception handler on Windows to display information on crash ([#&#8203;14582](astral-sh/uv#14582))
- Add hint when Python downloads are disabled ([#&#8203;14522](astral-sh/uv#14522))
- Add `UV_HTTP_RETRIES` to customize retry counts ([#&#8203;14544](astral-sh/uv#14544))
- Follow leaf symlinks matched by globs in `cache-key` ([#&#8203;13438](astral-sh/uv#13438))
- Support parent path components (`..`) in globs in `cache-key` ([#&#8203;13469](astral-sh/uv#13469))
- Improve `cache-key` performance ([#&#8203;13469](astral-sh/uv#13469))

##### Preview features

- Add `uv sync --output-format json` ([#&#8203;13689](astral-sh/uv#13689))

##### Bug fixes

- Do not re-resolve with a new Python version in `uv tool` if it is incompatible with `--python` ([#&#8203;14606](astral-sh/uv#14606))

##### Documentation

- Document how to nest dependency groups with `include-group` ([#&#8203;14539](astral-sh/uv#14539))
- Fix repeated word in Pyodide doc ([#&#8203;14554](astral-sh/uv#14554))
- Update CONTRIBUTING.md with instructions to format Markdown files via Docker ([#&#8203;14246](astral-sh/uv#14246))
- Fix version number for `setup-python` ([#&#8203;14533](astral-sh/uv#14533))

#### Install uv 0.7.21

##### Install prebuilt binaries via shell script

```sh
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.7.21/uv-installer.sh | sh
```

##### Install prebuilt binaries via powershell script

```sh
powershell -ExecutionPolicy Bypass -c "irm https://github.com/astral-sh/uv/releases/download/0.7.21/uv-installer.ps1 | iex"
```

#### Download uv 0.7.21

|  File  | Platform | Checksum |
|--------|----------|----------|
| [uv-aarch64-apple-darwin.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-aarch64-apple-darwin.tar.gz) | Apple Silicon macOS | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-aarch64-apple-darwin.tar.gz.sha256) |
| [uv-x86\_64-apple-darwin.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-x86_64-apple-darwin.tar.gz) | Intel macOS | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-x86_64-apple-darwin.tar.gz.sha256) |
| [uv-aarch64-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-aarch64-pc-windows-msvc.zip) | ARM64 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-aarch64-pc-windows-msvc.zip.sha256) |
| [uv-i686-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-i686-pc-windows-msvc.zip) | x86 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-i686-pc-windows-msvc.zip.sha256) |
| [uv-x86\_64-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-x86_64-pc-windows-msvc.zip) | x64 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-x86_64-pc-windows-msvc.zip.sha256) |
| [uv-aarch64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-aarch64-unknown-linux-gnu.tar.gz) | ARM64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-aarch64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-i686-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-i686-unknown-linux-gnu.tar.gz) | x86 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-i686-unknown-linux-gnu.tar.gz.sha256) |
| [uv-powerpc64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-powerpc64-unknown-linux-gnu.tar.gz) | PPC64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-powerpc64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-powerpc64le-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-powerpc64le-unknown-linux-gnu.tar.gz) | PPC64LE Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-powerpc64le-unknown-linux-gnu.tar.gz.sha256) |
| [uv-riscv64gc-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-riscv64gc-unknown-linux-gnu.tar.gz) | RISCV Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-riscv64gc-unknown-linux-gnu.tar.gz.sha256) |
| [uv-s390x-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-s390x-unknown-linux-gnu.tar.gz) | S390x Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-s390x-unknown-linux-gnu.tar.gz.sha256) |
| [uv-x86\_64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-x86_64-unknown-linux-gnu.tar.gz) | x64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-x86_64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-armv7-unknown-linux-gnueabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-armv7-unknown-linux-gnueabihf.tar.gz) | ARMv7 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-armv7-unknown-linux-gnueabihf.tar.gz.sha256) |
| [uv-aarch64-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-aarch64-unknown-linux-musl.tar.gz) | ARM64 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-aarch64-unknown-linux-musl.tar.gz.sha256) |
| [uv-i686-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-i686-unknown-linux-musl.tar.gz) | x86 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-i686-unknown-linux-musl.tar.gz.sha256) |
| [uv-x86\_64-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-x86_64-unknown-linux-musl.tar.gz) | x64 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-x86_64-unknown-linux-musl.tar.gz.sha256) |
| [uv-arm-unknown-linux-musleabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-arm-unknown-linux-musleabihf.tar.gz) | ARMv6 MUSL Linux (Hardfloat) | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-arm-unknown-linux-musleabihf.tar.gz.sha256) |
| [uv-armv7-unknown-linux-musleabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-armv7-unknown-linux-musleabihf.tar.gz) | ARMv7 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.21/uv-armv7-unknown-linux-musleabihf.tar.gz.sha256) |

### [`v0.7.20`](https://github.com/astral-sh/uv/releases/tag/0.7.20)

[Compare Source](astral-sh/uv@0.7.19...0.7.20)

#### Release Notes

##### Python

- Add Python 3.14.0b4
- Add zstd support to Python 3.14 on Unix (it already was available on Windows)
- Add PyPy 7.3.20 (for Python 3.11.13)

See the [PyPy](https://pypy.org/posts/2025/07/pypy-v7320-release.html) and [`python-build-standalone`](https://github.com/astral-sh/python-build-standalone/releases/tag/20250708) release notes for more details.

##### Enhancements

- Add `--workspace` flag to `uv add` ([#&#8203;14496](astral-sh/uv#14496))
- Add auto-detection for Intel GPUs ([#&#8203;14386](astral-sh/uv#14386))
- Drop trailing arguments when writing shebangs ([#&#8203;14519](astral-sh/uv#14519))
- Add debug message when skipping Python downloads ([#&#8203;14509](astral-sh/uv#14509))
- Add support for declaring multiple modules in namespace packages ([#&#8203;14460](astral-sh/uv#14460))

##### Bug fixes

- Revert normalization of trailing slashes on index URLs ([#&#8203;14511](astral-sh/uv#14511))
- Fix forced resolution with all extras in `uv version` ([#&#8203;14434](astral-sh/uv#14434))
- Fix handling of pre-releases in preferences ([#&#8203;14498](astral-sh/uv#14498))
- Remove transparent variants in `uv-extract` to enable retries ([#&#8203;14450](astral-sh/uv#14450))

##### Rust API

- Add method to get packages involved in a `NoSolutionError` ([#&#8203;14457](astral-sh/uv#14457))
- Make `ErrorTree` for `NoSolutionError` public ([#&#8203;14444](astral-sh/uv#14444))

##### Documentation

- Finish incomplete sentence in pip migration guide ([#&#8203;14432](astral-sh/uv#14432))
- Remove `cache-dependency-glob` examples for `setup-uv` ([#&#8203;14493](astral-sh/uv#14493))
- Remove `uv pip sync` suggestion with `pyproject.toml` ([#&#8203;14510](astral-sh/uv#14510))
- Update documentation for GitHub to use `setup-uv@v6` ([#&#8203;14490](astral-sh/uv#14490))

#### Install uv 0.7.20

##### Install prebuilt binaries via shell script

```sh
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.7.20/uv-installer.sh | sh
```

##### Install prebuilt binaries via powershell script

```sh
powershell -ExecutionPolicy Bypass -c "irm https://github.com/astral-sh/uv/releases/download/0.7.20/uv-installer.ps1 | iex"
```

#### Download uv 0.7.20

|  File  | Platform | Checksum |
|--------|----------|----------|
| [uv-aarch64-apple-darwin.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-aarch64-apple-darwin.tar.gz) | Apple Silicon macOS | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-aarch64-apple-darwin.tar.gz.sha256) |
| [uv-x86\_64-apple-darwin.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-x86_64-apple-darwin.tar.gz) | Intel macOS | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-x86_64-apple-darwin.tar.gz.sha256) |
| [uv-aarch64-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-aarch64-pc-windows-msvc.zip) | ARM64 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-aarch64-pc-windows-msvc.zip.sha256) |
| [uv-i686-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-i686-pc-windows-msvc.zip) | x86 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-i686-pc-windows-msvc.zip.sha256) |
| [uv-x86\_64-pc-windows-msvc.zip](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-x86_64-pc-windows-msvc.zip) | x64 Windows | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-x86_64-pc-windows-msvc.zip.sha256) |
| [uv-aarch64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-aarch64-unknown-linux-gnu.tar.gz) | ARM64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-aarch64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-i686-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-i686-unknown-linux-gnu.tar.gz) | x86 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-i686-unknown-linux-gnu.tar.gz.sha256) |
| [uv-powerpc64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-powerpc64-unknown-linux-gnu.tar.gz) | PPC64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-powerpc64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-powerpc64le-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-powerpc64le-unknown-linux-gnu.tar.gz) | PPC64LE Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-powerpc64le-unknown-linux-gnu.tar.gz.sha256) |
| [uv-riscv64gc-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-riscv64gc-unknown-linux-gnu.tar.gz) | RISCV Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-riscv64gc-unknown-linux-gnu.tar.gz.sha256) |
| [uv-s390x-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-s390x-unknown-linux-gnu.tar.gz) | S390x Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-s390x-unknown-linux-gnu.tar.gz.sha256) |
| [uv-x86\_64-unknown-linux-gnu.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-x86_64-unknown-linux-gnu.tar.gz) | x64 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-x86_64-unknown-linux-gnu.tar.gz.sha256) |
| [uv-armv7-unknown-linux-gnueabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-armv7-unknown-linux-gnueabihf.tar.gz) | ARMv7 Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-armv7-unknown-linux-gnueabihf.tar.gz.sha256) |
| [uv-aarch64-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-aarch64-unknown-linux-musl.tar.gz) | ARM64 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-aarch64-unknown-linux-musl.tar.gz.sha256) |
| [uv-i686-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-i686-unknown-linux-musl.tar.gz) | x86 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-i686-unknown-linux-musl.tar.gz.sha256) |
| [uv-x86\_64-unknown-linux-musl.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-x86_64-unknown-linux-musl.tar.gz) | x64 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-x86_64-unknown-linux-musl.tar.gz.sha256) |
| [uv-arm-unknown-linux-musleabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-arm-unknown-linux-musleabihf.tar.gz) | ARMv6 MUSL Linux (Hardfloat) | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-arm-unknown-linux-musleabihf.tar.gz.sha256) |
| [uv-armv7-unknown-linux-musleabihf.tar.gz](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-armv7-unknown-linux-musleabihf.tar.gz) | ARMv7 MUSL Linux | [checksum](https://github.com/astral-sh/uv/releases/download/0.7.20/uv-armv7-unknown-linux-musleabihf.tar.gz.sha256) |

</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:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90Il19-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants