Skip to content

Deduplicate all files in the wheel cache - #21327

Merged
charliermarsh merged 24 commits into
mainfrom
charlie/dirhash-all-files
Aug 31, 2026
Merged

Deduplicate all files in the wheel cache#21327
charliermarsh merged 24 commits into
mainfrom
charlie/dirhash-all-files

Conversation

@charliermarsh

@charliermarsh charliermarsh commented Aug 27, 2026

Copy link
Copy Markdown
Member

Summary

On main, we support content-addressed caching, but only at the wheel-level. That is, if you download the same wheel twice from different sources, they share a cache entry. But files within or across wheels are not deduplicated at all.

This PR adds deduplication at the file level: every file is now stored under its BLAKE3 hash in a files-v0 bucket. We hardlink these objects into their original locations in archive-v0, so the installation step doesn't change at all -- we're just deduping within the cache (and cache cleanup removes file objects when their hardlink count drops to one).

In the prior proposal (#19694), we included the following table:

File selection Additional savings Files hardlinked Distinct files-v0 objects
Executables and native libraries 275.7 MiB 3,336 3,088
Any payload file ≥ 10 MiB 235.0 MiB 80 78
Any payload file ≥ 1 MiB 279.7 MiB 373 349
Any payload file ≥ 100 KiB 353.4 MiB 2,830 2,458
Any payload file ≥ 10 KiB 475.7 MiB 23,767 18,722
Any payload file ≥ 1 KiB 537.4 MiB 95,156 66,422
All payload files 545.2 MiB 134,222 87,129

So we're saving 545.2 MiB on my local machine, or about 10% of the cache.

In return, the net effect seems to be something like a <4% slowdown for cold installs (and no effect on warm installs), which I think is probably worthwhile here.

@charliemarsh-oai

charliemarsh-oai commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

N.B. Slop comment for benchmark data.

We benchmarked the optimization in 85c3f7485 against the binary-only cache at a3f977ece, with --preview-features content-addressed-cache enabled on both. These are medians for the complete uv pip install process; positive changes mean slower.

Wheel Cache Parent median Optimized median Change (95% CI)
AnyIO 4.9.0 cold 90.4 ms 93.4 ms +3.39% (+2.87% to +3.86%)
AnyIO 4.9.0 warm 29.9 ms 30.3 ms +1.14% (-1.08% to +3.85%)
SymPy 1.14.0 cold 367.1 ms 381.6 ms +3.95% (+3.49% to +4.46%)
SymPy 1.14.0 warm 84.6 ms 84.0 ms -0.64% (-1.04% to -0.11%)
NumPy 2.2.6 cold 314.1 ms 326.5 ms +3.95% (+3.24% to +4.82%)
NumPy 2.2.6 warm 62.5 ms 62.7 ms +0.28% (-0.55% to +1.25%)
PyTorch 2.7.1+cpu cold 2982.6 ms 3072.9 ms +3.03% (+0.98% to +5.18%)
PyTorch 2.7.1+cpu warm 404.0 ms 403.1 ms -0.21% (-0.72% to +0.11%)

Before this optimization, the measured cold regressions were +4.02% for AnyIO 4.9.0, +19.41% for SymPy 1.14.0, +12.75% for NumPy 2.2.6, +15.30% for PyTorch 2.7.1+cpu.

All eight median regressions are below 5%. The PyTorch cold 95% interval still reaches 5.18%, so its upper bound is not below 5%. Every other upper bound is below 5%.

We retained all 1,860 measured installs, including outliers, and combined every confirmation run for this candidate. Cold measurements have 360 paired rounds for AnyIO, 120 each for SymPy and NumPy, and 90 for PyTorch; warm measurements have 60 paired rounds per wheel. Baseline/candidate order alternates, with three warmups per case. Intervals use a paired percentile bootstrap of the ratio of medians with 10,000 resamples.

These measurements ran on Linux/ext4, an AMD EPYC-Milan VM pinned to eight CPUs, with Python 3.12.13. Both binaries use Rust 1.98.0 and the same profiling profile (optimized, no LTO). Each sample installs one pinned local wheel offline, without dependencies or bytecode compilation, using hardlinks into a fresh virtual environment. Cold removes the entire uv cache; warm retains a primed cache. Setup and cleanup are untimed, and the wheel OS page cache is warm. No compilation ran during the benchmarks. These results do not cover other platforms or network-inclusive installs.

All-file deduplication, content/executable identities, the cache layout, complete archives, and copy fallbacks are preserved. Inode checks confirmed that every archived file shares its file-store object for all four wheels. The five targeted integration tests passed ten stress iterations (50 executions), including local and streamed wheels with one and four workers, RECORD handling, cache cleanup, and cross-filesystem installation. Formatting and Clippy with warnings denied also passed.

@codspeed-hq

codspeed-hq Bot commented Aug 27, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 25 untouched benchmarks
⏩ 12 skipped benchmarks1


Comparing charlie/dirhash-all-files (d12d2ad) with main (7c1d80e)

Open in CodSpeed

Footnotes

  1. 12 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@charliermarsh
charliermarsh marked this pull request as ready for review August 28, 2026 00:03
@charliermarsh
charliermarsh force-pushed the charlie/dirhash-binaries branch from a3f977e to 1f28fd5 Compare August 28, 2026 00:08
@charliermarsh
charliermarsh force-pushed the charlie/dirhash-all-files branch from 0a8edc0 to a108fab Compare August 28, 2026 00:08
@charliermarsh
charliermarsh changed the base branch from charlie/dirhash-binaries to main August 28, 2026 00:09
@charliermarsh charliermarsh added enhancement New feature or improvement to existing functionality preview Experimental behavior labels Aug 28, 2026
@astral-sh-bot

astral-sh-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

uv test inventory changes

This PR changes the tests when compared with the main base revision.

  • Added tests: 5
  • Removed tests: 0
  • Changed suites: 2
uv-extract: +1 / -0

Added:

  • uv-extract::dirhash::archive::tests::extracted_file_executable_status

Removed: none

uv::pip_install: +4 / -0

Added:

  • uv::pip_install::pip_install::all_files_except_record_use_archive_file_store
  • uv::pip_install::pip_install::binary_payload_copy_fallback_uses_archive_file_store
  • uv::pip_install::pip_install::binary_payloads_stay_in_archive_without_preview
  • uv::pip_install::pip_install::binary_payloads_use_archive_file_store

Removed: none

@zsol
zsol self-requested a review August 28, 2026 09:53
charliermarsh added a commit that referenced this pull request Aug 28, 2026
## Summary

When content hashing is enabled, we currently allocate and zero a new 64
KiB buffer for every file we copy and hash during streaming extraction.
This PR reuses one buffer across the wheel instead. For the PyTorch
wheel used in the benchmarks, that reduces buffer allocations for
hashing from 11,120 to one, while keeping the buffer size at 64 KiB per
active wheel.

The following measurements compare #21327 at
`a188b8e833aef3c3b4b60a32ed9fafe6ac74186a` with this optimization
applied on top, before moving the change onto `main`. They are not
measurements against `main`. The Linux benchmarks alternate base and
candidate, using pinned wheels served over local HTTP with
content-addressed caching enabled:

| Cold install | #21327 | #21327 + buffer reuse | Change |
| --- | ---: | ---: | ---: |
| AnyIO | 110 ms | 107 ms | -2.6% |
| SymPy | 845 ms | 775 ms | -8.3% |
| NumPy | 627 ms | 567 ms | -9.5% |
| PyTorch CPU | 6.50 s | 5.99 s | -7.8% |
| 14-package environment, concurrency 4 | 6.95 s | 6.47 s | -7.0% |

The individual results above use 16 paired rounds; the full environment
uses 12. AnyIO, SymPy, and NumPy were repeated after an initial 20-pair
run: the initial AnyIO timings were noisy, while the initial SymPy and
NumPy improvements were 7.8% and 6.9%. All original samples were
retained. Cached installs and local-wheel controls showed no consistent
change. Across the initial runs, repeats, and controls, we measured 672
installs, excluding warmups and cache priming.

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
@charliermarsh
charliermarsh force-pushed the charlie/dirhash-all-files branch from a188b8e to 694af80 Compare August 28, 2026 16:56
@charliermarsh

Copy link
Copy Markdown
Member Author

Okay, I experimented with a bunch of alternatives to try and make this more performant:

  • Store all files as non-executable, and then apply executable flags at install-time (Store cached wheel permissions in archive manifests #21334, Pipeline file cache publication for streamed wheels #21333). This would allow us to hardlink into files-v0 while we unzip. Unfortunately, it also means we have to copy every executable that we want to install, since hardlinks share a mode. Ultimately, this was a bit tradeoff -- it was slower!
  • Pre-fetching the central directory so that we can determine whether a given file is executable at extraction-time. (As-is, we only get the central directory after we've unzipped, so we don't know if a file is executable until after it's been extracted.) This also turned out to be slower because we have additional overhead from making more HTTP requests.

I also considered something like "guess whether a file is executable" during extraction (and then, if we're wrong, copy it and change the mode after unzipping). I guess this could end up being more performant, but I wasn't very happy with the heuristics.

Ultimately, I think what we have here is good.

@charliermarsh

Copy link
Copy Markdown
Member Author

If you bundle in #21340, I believe this is also faster than before (i.e., gains from #21340 outweigh the extra cost in this PR).

@charliermarsh
charliermarsh merged commit 15bf04f into main Aug 31, 2026
82 checks passed
@charliermarsh
charliermarsh deleted the charlie/dirhash-all-files branch August 31, 2026 13:28
charliermarsh added a commit that referenced this pull request Aug 31, 2026
## Summary

Follow-up to #21327.

Cache cleanup currently reads the hardlink count of every `files-v0`
object separately. On macOS, we can request names, file types, and link
counts in batches with `getattrlistbulk`, allocating paths only for
files with a single link.

Use this fast path for flat cache shards. We keep the existing walk on
other platforms, when bulk reads or required attributes aren't
available, and for nested directories. We don't follow symlinks, and
file removal still uses the existing storage accounting.

On macOS, with 87,129 distinct empty file objects across 256 shards and
two hardlinks per object, median full-command times across eight paired
warm-cache runs were:

| Command                        | Before |  After |
| ------------------------------ | -----: | -----: |
| `uv cache clean <package>`     | 386 ms | 105 ms |
| `uv cache clean <10 packages>` | 389 ms | 104 ms |
| `uv cache prune`               | 386 ms | 101 ms |

These runs retain every object, so they measure scan cost rather than
deletion throughput. The scan is still single-threaded.

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
luketainton pushed a commit to luketainton/repos_labmcp that referenced this pull request Aug 31, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [ghcr.io/astral-sh/uv](https://github.com/astral-sh/uv) | final | patch | `0.12.7` → `0.12.8` |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/51) for more information.

---

### Release Notes

<details>
<summary>astral-sh/uv (ghcr.io/astral-sh/uv)</summary>

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

Released on 2026-08-31.

##### Enhancements

- Warn about invalid tool directories and continue upgrading valid tools with `uv tool upgrade --all` ([#&#8203;21368](astral-sh/uv#21368))

##### Preview features

- Deduplicate identical files within and across cached wheels with the `content-addressed-cache` preview feature ([#&#8203;21327](astral-sh/uv#21327))
- Reduce allocations while extracting content-addressed wheels by reusing the hashing buffer across files ([#&#8203;21340](astral-sh/uv#21340))
- Speed up content-addressed cache cleanup on macOS by reading hard-link counts in bulk ([#&#8203;21344](astral-sh/uv#21344))

##### Performance

- Prevent concurrent uv processes from downloading and extracting the same remote wheel more than once ([#&#8203;21379](astral-sh/uv#21379))
- Speed up dependency graph construction from large lockfiles by indexing packages during traversal ([#&#8203;21373](astral-sh/uv#21373))
- Extend indexed lockfile traversal to exports, dependency trees, audits, and freshness checks ([#&#8203;21377](astral-sh/uv#21377))
- Speed up warm resolutions by reducing repeated marker interner work ([#&#8203;21300](astral-sh/uv#21300))

##### Bug fixes

- Do not trust hashes from direct URLs discovered only in wheel metadata when installing with `--require-hashes` ([#&#8203;21348](astral-sh/uv#21348))
- Use a compatible Azure Storage API version for anonymous and authenticated requests, allowing credential retries when public access is disabled ([#&#8203;21366](astral-sh/uv#21366))
- Redact Azure shared access signature (`sig`) query parameters from displayed URLs ([#&#8203;21360](astral-sh/uv#21360))
- Treat projects below one-level workspace member globs as standalone instead of aborting workspace discovery ([#&#8203;21341](astral-sh/uv#21341))

##### Other changes

- Update `astral-tokio-tar` to 0.7.0 and use effective sizes when tracking extracted hard links ([#&#8203;21346](astral-sh/uv#21346))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC41Mi4wIiwidXBkYXRlZEluVmVyIjoiNDQuNTIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsidHlwZS9kZXBlbmRlbmNpZXMiXX0=-->

Reviewed-on: https://git.tainton.uk/repos/labmcp/pulls/52
Co-authored-by: renovate[bot] <renovate-bot@git.tainton.uk>
jylenhof pushed a commit to jylenhof/mise-update-tool that referenced this pull request Sep 2, 2026
Automated mise tool upgrades from local config.

mise-managed tools:
- `action-validator`
- `actionlint`
- `aube`
- `editorconfig-checker`
- `ghalint`
- `pinact`
- `pipx:gh-action-pulse`
- `prek`
- `rumdl`
- `shellcheck`
- `shfmt`
- `tombi`
- `uv`
- `yamlfmt`
- `yamllint`
- `zizmor`

Command: `mise upgrade --bump --local action-validator actionlint aube editorconfig-checker ghalint pinact pipx:gh-action-pulse prek rumdl shellcheck shfmt tombi uv yamlfmt yamllint zizmor`

<details>
<summary>Version changelog (2 tools)</summary>

| Tool | Requested | Installed |
|------|-----------|-----------|
| `prek` | `0.5.0` → `0.5.1` | `0.5.0` → `0.5.1` |
| `uv` | `0.12.7` → `0.12.8` | `0.12.7` → `0.12.8` |

</details>

<details>
<summary>Release notes (2 tools)</summary>

<details>
<summary>prek: `0.5.0` → `0.5.1` (j178/prek)</summary>

### v0.5.1

## Release Notes

Released on 2026-09-01.

### Enhancements

- Add `--hide-status <passed|failed|skipped>` for hook reports ([#2644](j178/prek#2644))
- Add `prek init` for repository setup ([#2636](j178/prek#2636))
- Apply hook `env` during environment creation ([#2650](j178/prek#2650))
- Disable error snippets in `check-yaml` diagnostics ([#2664](j178/prek#2664))
- Show hooks excluded by skip selectors ([#2645](j178/prek#2645))
- Support Pixi for Conda environments ([#2667](j178/prek#2667))
- Support `cargo-binstall` for Rust CLI dependencies ([#2658](j178/prek#2658))
- Warn about unused keys in user settings ([#2665](j178/prek#2665))

### Bug fixes

- Reject unsupported YAML tags in check-yaml ([#2656](j178/prek#2656))

### Documentation

- Clarify local hook documentation ([#2640](j178/prek#2640))
- Clarify pre-commit command compatibility ([#2635](j178/prek#2635))
- Document automatic PR fixes with autofix.ci ([#2643](j178/prek#2643))
- Document check-yaml unsafe support ([#2632](j178/prek#2632))
- Improve setup and workflow documentation ([#2637](j178/prek#2637))

### Other changes

- Drop low-usage release targets ([#2651](j178/prek#2651))
- Generate a prek manifest JSON schema ([#2648](j178/prek#2648))

### Contributors

- @​j178

## Install prek 0.5.1

### Install prebuilt binaries via shell script

```sh
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/j178/prek/releases/download/v0.5.1/prek-installer.sh | sh
```

### Install prebuilt binaries via powershell script

```sh
powershell -ExecutionPolicy Bypass -c "irm https://github.com/j178/prek/releases/download… (truncated)

</details>
<details>
<summary>uv: `0.12.7` → `0.12.8` (astral-sh/uv)</summary>

### 0.12.8

## Release Notes

Released on 2026-08-31.

### Enhancements

- Warn about invalid tool directories and continue upgrading valid tools with `uv tool upgrade --all` ([#21368](astral-sh/uv#21368))

### Preview features

- Deduplicate identical files within and across cached wheels with the `content-addressed-cache` preview feature ([#21327](astral-sh/uv#21327))
- Reduce allocations while extracting content-addressed wheels by reusing the hashing buffer across files ([#21340](astral-sh/uv#21340))
- Speed up content-addressed cache cleanup on macOS by reading hard-link counts in bulk ([#21344](astral-sh/uv#21344))

### Performance

- Prevent concurrent uv processes from downloading and extracting the same remote wheel more than once ([#21379](astral-sh/uv#21379))
- Speed up dependency graph construction from large lockfiles by indexing packages during traversal ([#21373](astral-sh/uv#21373))
- Extend indexed lockfile traversal to exports, dependency trees, audits, and freshness checks ([#21377](astral-sh/uv#21377))
- Speed up warm resolutions by reducing repeated marker interner work ([#21300](astral-sh/uv#21300))

### Bug fixes

- Do not trust hashes from direct URLs discovered only in wheel metadata when installing with `--require-hashes` ([#21348](astral-sh/uv#21348))
- Use a compatible Azure Storage API version for anonymous and authenticated requests, allowing credential retries when public access is disabled ([#21366](astral-sh/uv#21366))
- Redact Azure shared access signature (`sig`) query parameters from displayed URLs ([#21360](astral-sh/uv#21360))
- Treat projects below one-level workspace member globs as standalone instead of aborting workspace discovery ([#21341](https://github.com/astra… (truncated)

</details>

</details>

Modified files:
- `.mise.toml`
jylenhof pushed a commit to jylenhof/mise-update-tool that referenced this pull request Sep 2, 2026
Automated mise tool upgrades from local config.

mise-managed tools:
- `action-validator`
- `actionlint`
- `aube`
- `editorconfig-checker`
- `ghalint`
- `pinact`
- `pipx:gh-action-pulse`
- `prek`
- `rumdl`
- `shellcheck`
- `shfmt`
- `tombi`
- `uv`
- `yamlfmt`
- `yamllint`
- `zizmor`

Command: `mise upgrade --bump --local action-validator actionlint aube editorconfig-checker ghalint pinact pipx:gh-action-pulse prek rumdl shellcheck shfmt tombi uv yamlfmt yamllint zizmor`

<details>
<summary>Version changelog (2 tools)</summary>

| Tool | Requested | Installed |
|------|-----------|-----------|
| `prek` | `0.5.0` → `0.5.1` | `0.5.0` → `0.5.1` |
| `uv` | `0.12.7` → `0.12.8` | `0.12.7` → `0.12.8` |

</details>

<details>
<summary>Release notes (2 tools)</summary>

<details>
<summary>prek: `0.5.0` → `0.5.1` (j178/prek)</summary>

### v0.5.1

## Release Notes

Released on 2026-09-01.

### Enhancements

- Add `--hide-status <passed|failed|skipped>` for hook reports ([#2644](j178/prek#2644))
- Add `prek init` for repository setup ([#2636](j178/prek#2636))
- Apply hook `env` during environment creation ([#2650](j178/prek#2650))
- Disable error snippets in `check-yaml` diagnostics ([#2664](j178/prek#2664))
- Show hooks excluded by skip selectors ([#2645](j178/prek#2645))
- Support Pixi for Conda environments ([#2667](j178/prek#2667))
- Support `cargo-binstall` for Rust CLI dependencies ([#2658](j178/prek#2658))
- Warn about unused keys in user settings ([#2665](j178/prek#2665))

### Bug fixes

- Reject unsupported YAML tags in check-yaml ([#2656](j178/prek#2656))

### Documentation

- Clarify local hook documentation ([#2640](j178/prek#2640))
- Clarify pre-commit command compatibility ([#2635](j178/prek#2635))
- Document automatic PR fixes with autofix.ci ([#2643](j178/prek#2643))
- Document check-yaml unsafe support ([#2632](j178/prek#2632))
- Improve setup and workflow documentation ([#2637](j178/prek#2637))

### Other changes

- Drop low-usage release targets ([#2651](j178/prek#2651))
- Generate a prek manifest JSON schema ([#2648](j178/prek#2648))

### Contributors

- @​j178

## Install prek 0.5.1

### Install prebuilt binaries via shell script

```sh
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/j178/prek/releases/download/v0.5.1/prek-installer.sh | sh
```

### Install prebuilt binaries via powershell script

```sh
powershell -ExecutionPolicy Bypass -c "irm https://github.com/j178/prek/releases/download… (truncated)

</details>
<details>
<summary>uv: `0.12.7` → `0.12.8` (astral-sh/uv)</summary>

### 0.12.8

## Release Notes

Released on 2026-08-31.

### Enhancements

- Warn about invalid tool directories and continue upgrading valid tools with `uv tool upgrade --all` ([#21368](astral-sh/uv#21368))

### Preview features

- Deduplicate identical files within and across cached wheels with the `content-addressed-cache` preview feature ([#21327](astral-sh/uv#21327))
- Reduce allocations while extracting content-addressed wheels by reusing the hashing buffer across files ([#21340](astral-sh/uv#21340))
- Speed up content-addressed cache cleanup on macOS by reading hard-link counts in bulk ([#21344](astral-sh/uv#21344))

### Performance

- Prevent concurrent uv processes from downloading and extracting the same remote wheel more than once ([#21379](astral-sh/uv#21379))
- Speed up dependency graph construction from large lockfiles by indexing packages during traversal ([#21373](astral-sh/uv#21373))
- Extend indexed lockfile traversal to exports, dependency trees, audits, and freshness checks ([#21377](astral-sh/uv#21377))
- Speed up warm resolutions by reducing repeated marker interner work ([#21300](astral-sh/uv#21300))

### Bug fixes

- Do not trust hashes from direct URLs discovered only in wheel metadata when installing with `--require-hashes` ([#21348](astral-sh/uv#21348))
- Use a compatible Azure Storage API version for anonymous and authenticated requests, allowing credential retries when public access is disabled ([#21366](astral-sh/uv#21366))
- Redact Azure shared access signature (`sig`) query parameters from displayed URLs ([#21360](astral-sh/uv#21360))
- Treat projects below one-level workspace member globs as standalone instead of aborting workspace discovery ([#21341](https://github.com/astra… (truncated)

</details>

</details>

Modified files:
- `.mise.toml`

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
hbjydev pushed a commit to hbjydev/phoebe that referenced this pull request Sep 3, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [uv](https://github.com/astral-sh/uv) | tools | patch | `0.12.7` → `0.12.9` |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/141) for more information.

---

### Release Notes

<details>
<summary>astral-sh/uv (uv)</summary>

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

[Compare Source](astral-sh/uv@0.12.8...0.12.9)

Released on 2026-09-01.

##### Python

- Add CPython 3.15.0rc2 ([#&#8203;21413](astral-sh/uv#21413), [#&#8203;21415](astral-sh/uv#21415))

##### Enhancements

- Add `--no-locked` and `--no-frozen` to disable lock modes enabled by `UV_LOCKED` and `UV_FROZEN` for a single invocation ([#&#8203;21408](astral-sh/uv#21408))
- Report the exact command-line lock-mode flag in warnings and errors ([#&#8203;21402](astral-sh/uv#21402))

##### Performance

- Speed up cold wheel installs by extracting each streaming ZIP archive in a single blocking task and reusing buffers across files ([#&#8203;21372](astral-sh/uv#21372))

##### Bug fixes

- Update `async_http_range_reader` to 0.11.1 to address a potential memory-safety issue when reading metadata ranges from untrusted wheels ([#&#8203;21401](astral-sh/uv#21401))
- Remove sensitive headers when redirects cross authentication realms, including same-host redirects that change URL schemes ([#&#8203;21382](astral-sh/uv#21382))
- Redact secrets in signed URLs from retry diagnostics, including nested request errors ([#&#8203;21381](astral-sh/uv#21381))
- Give `--locked`, `--frozen`, `--check`, and `--check-exists` precedence over conflicting `UV_LOCKED` and `UV_FROZEN` values ([#&#8203;21396](astral-sh/uv#21396))
- Prevent concurrent uv processes from redundantly extracting the same local or source-built wheel ([#&#8203;21400](astral-sh/uv#21400))

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

[Compare Source](astral-sh/uv@0.12.7...0.12.8)

Released on 2026-08-31.

##### Enhancements

- Warn about invalid tool directories and continue upgrading valid tools with `uv tool upgrade --all` ([#&#8203;21368](astral-sh/uv#21368))

##### Preview features

- Deduplicate identical files within and across cached wheels with the `content-addressed-cache` preview feature ([#&#8203;21327](astral-sh/uv#21327))
- Reduce allocations while extracting content-addressed wheels by reusing the hashing buffer across files ([#&#8203;21340](astral-sh/uv#21340))
- Speed up content-addressed cache cleanup on macOS by reading hard-link counts in bulk ([#&#8203;21344](astral-sh/uv#21344))

##### Performance

- Prevent concurrent uv processes from downloading and extracting the same remote wheel more than once ([#&#8203;21379](astral-sh/uv#21379))
- Speed up dependency graph construction from large lockfiles by indexing packages during traversal ([#&#8203;21373](astral-sh/uv#21373))
- Extend indexed lockfile traversal to exports, dependency trees, audits, and freshness checks ([#&#8203;21377](astral-sh/uv#21377))
- Speed up warm resolutions by reducing repeated marker interner work ([#&#8203;21300](astral-sh/uv#21300))

##### Bug fixes

- Do not trust hashes from direct URLs discovered only in wheel metadata when installing with `--require-hashes` ([#&#8203;21348](astral-sh/uv#21348))
- Use a compatible Azure Storage API version for anonymous and authenticated requests, allowing credential retries when public access is disabled ([#&#8203;21366](astral-sh/uv#21366))
- Redact Azure shared access signature (`sig`) query parameters from displayed URLs ([#&#8203;21360](astral-sh/uv#21360))
- Treat projects below one-level workspace member globs as standalone instead of aborting workspace discovery ([#&#8203;21341](astral-sh/uv#21341))

##### Other changes

- Update `astral-tokio-tar` to 0.7.0 and use effective sizes when tracking extracted hard links ([#&#8203;21346](astral-sh/uv#21346))

</details>

---

### Configuration

📅 **Schedule**: (in timezone Europe/London)

- 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 PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC41Mi4xIiwidXBkYXRlZEluVmVyIjoiNDQuNTYuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsicmVub3ZhdGUvZ2l0aHViLXJlbGVhc2UiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://forgejo.hayden.moe/hayden/phoebe/pulls/430
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or improvement to existing functionality preview Experimental behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants