Compare commits

...

21 commits
0.7.19 ... main

Author SHA1 Message Date
Zanie Blue
7e48292fac
Fix handling of pre-releases in preferences (#14498)
Some checks are pending
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
Closes https://github.com/astral-sh/uv/issues/14485

I tested this using the reproduction in the issue. It'd be nice to add
test coverage though.
2025-07-07 20:10:35 -05:00
github-actions[bot]
e31f556205
Sync latest Python releases (#14452)
Automated update for Python releases.

Co-authored-by: zanieb <2586601+zanieb@users.noreply.github.com>
2025-07-08 00:53:38 +00:00
Zanie Blue
dedced3265
Remove cache-dependency-glob examples for setup-uv (#14493)
Some checks are pending
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
See https://github.com/astral-sh/uv/pull/13163#discussion_r2063244551
2025-07-07 15:06:23 -05:00
theirix
5c6d76ca8b
Update documentation for GHA to use v6 (#14490)
## Summary

`astral-sh/setup-uv@v6` is the latest version of GitHub actions.

## Test Plan

Documentation update
2025-07-07 14:04:45 -05:00
Nils Koch
1d20530f2d
trim content of INSTALLER file (#14488)
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

We are using UV as a library and `installer()` returned `"pip\n"`. The
packages got installed by the pip package manager and not by UV. pip
seems to add a new line to the `INSTALLER` file and UV does not.

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

<!-- How was it tested? -->
2025-07-07 18:16:50 +02:00
renovate[bot]
ddb1577a93
Update Rust crate reqwest to v0.12.22 (#14475)
Some checks are pending
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [reqwest](https://redirect.github.com/seanmonstar/reqwest) |
workspace.dependencies | patch | `=0.12.15` -> `=0.12.22` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>seanmonstar/reqwest (reqwest)</summary>

###
[`v0.12.22`](https://redirect.github.com/seanmonstar/reqwest/blob/HEAD/CHANGELOG.md#v01222)

[Compare
Source](https://redirect.github.com/seanmonstar/reqwest/compare/v0.12.21...v0.12.22)

- Fix socks proxies when resolving IPv6 destinations.

###
[`v0.12.21`](https://redirect.github.com/seanmonstar/reqwest/blob/HEAD/CHANGELOG.md#v01221)

[Compare
Source](https://redirect.github.com/seanmonstar/reqwest/compare/v0.12.20...v0.12.21)

- Fix socks proxy to use `socks4a://` instead of `socks4h://`.
- Fix `Error::is_timeout()` to check for hyper and IO timeouts too.
- Fix request `Error` to again include URLs when possible.
- Fix socks connect error to include more context.
- (wasm) implement `Default` for `Body`.

###
[`v0.12.20`](https://redirect.github.com/seanmonstar/reqwest/blob/HEAD/CHANGELOG.md#v01220)

[Compare
Source](https://redirect.github.com/seanmonstar/reqwest/compare/v0.12.19...v0.12.20)

- Add `ClientBuilder::tcp_user_timeout(Duration)` option to set
`TCP_USER_TIMEOUT`.
- Fix proxy headers only using the first matched proxy.
- (wasm) Fix re-adding `Error::is_status()`.

###
[`v0.12.19`](https://redirect.github.com/seanmonstar/reqwest/blob/HEAD/CHANGELOG.md#v01219)

[Compare
Source](https://redirect.github.com/seanmonstar/reqwest/compare/v0.12.18...v0.12.19)

- Fix redirect that changes the method to GET should remove payload
headers.
- Fix redirect to only check the next scheme if the policy action is to
follow.
- (wasm) Fix compilation error if `cookies` feature is enabled (by the
way, it's a noop feature in wasm).

###
[`v0.12.18`](https://redirect.github.com/seanmonstar/reqwest/blob/HEAD/CHANGELOG.md#v01218)

[Compare
Source](https://redirect.github.com/seanmonstar/reqwest/compare/v0.12.17...v0.12.18)

- Fix compilation when `socks` enabled without TLS.

###
[`v0.12.17`](https://redirect.github.com/seanmonstar/reqwest/blob/HEAD/CHANGELOG.md#v01217)

[Compare
Source](https://redirect.github.com/seanmonstar/reqwest/compare/v0.12.16...v0.12.17)

- Fix compilation on macOS.

###
[`v0.12.16`](https://redirect.github.com/seanmonstar/reqwest/blob/HEAD/CHANGELOG.md#v01216)

[Compare
Source](https://redirect.github.com/seanmonstar/reqwest/compare/v0.12.15...v0.12.16)

- Add `ClientBuilder::http3_congestion_bbr()` to enable BBR congestion
control.
- Add `ClientBuilder::http3_send_grease()` to configure whether to send
use QUIC grease.
- Add `ClientBuilder::http3_max_field_section_size()` to configure the
maximum response headers.
- Add `ClientBuilder::tcp_keepalive_interval()` to configure TCP probe
interval.
- Add `ClientBuilder::tcp_keepalive_retries()` to configure TCP probe
count.
- Add `Proxy::headers()` to add extra headers that should be sent to a
proxy.
- Fix `redirect::Policy::limit()` which had an off-by-1 error, allowing
1 more redirect than specified.
- Fix HTTP/3 to support streaming request bodies.
- (wasm) Fix null bodies when calling `Response::bytes_stream()`.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
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 was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNy4yIiwidXBkYXRlZEluVmVyIjoiNDEuMTcuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

---

Closes #14243

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: konstin <konstin@mailbox.org>
2025-07-07 13:39:57 +02:00
John Mumm
d31e6ad7c7
Move fragment preservation test to directly test our redirect handling logic (#14480)
When [updating](https://github.com/astral-sh/uv/pull/14475) to the
latest `reqwest` version, our fragment propagation test broke. That test
was partially testing the `reqwest` behavior, so this PR moves the
fragment test to directly test our logic for constructing redirect
requests.
2025-07-07 12:51:21 +02:00
renovate[bot]
3a77b9cdd9
Update aws-actions/configure-aws-credentials digest to f503a18 (#14473)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| aws-actions/configure-aws-credentials | action | digest | `3d8cba3` ->
`f503a18` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
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 was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNy4yIiwidXBkYXRlZEluVmVyIjoiNDEuMTcuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 11:31:31 +02:00
renovate[bot]
1d027bd92a
Update pre-commit dependencies (#14474)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit)
| repository | patch | `v0.12.1` -> `v0.12.2` |
| [crate-ci/typos](https://redirect.github.com/crate-ci/typos) |
repository | minor | `v1.33.1` -> `v1.34.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>astral-sh/ruff-pre-commit (astral-sh/ruff-pre-commit)</summary>

###
[`v0.12.2`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.12.2)

[Compare
Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.12.1...v0.12.2)

See: https://github.com/astral-sh/ruff/releases/tag/0.12.2

</details>

<details>
<summary>crate-ci/typos (crate-ci/typos)</summary>

###
[`v1.34.0`](https://redirect.github.com/crate-ci/typos/releases/tag/v1.34.0)

[Compare
Source](https://redirect.github.com/crate-ci/typos/compare/v1.33.1...v1.34.0)

#### \[1.34.0] - 2025-06-30

##### Features

- Updated the dictionary with the [June
2025](https://redirect.github.com/crate-ci/typos/issues/1309) changes

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
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.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

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

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNy4yIiwidXBkYXRlZEluVmVyIjoiNDEuMTcuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 10:36:26 +02:00
renovate[bot]
bb738aeb44
Update Rust crate test-log to v0.2.18 (#14477)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [test-log](https://redirect.github.com/d-e-s-o/test-log) |
dev-dependencies | patch | `0.2.17` -> `0.2.18` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>d-e-s-o/test-log (test-log)</summary>

###
[`v0.2.18`](https://redirect.github.com/d-e-s-o/test-log/blob/HEAD/CHANGELOG.md#0218)

[Compare
Source](https://redirect.github.com/d-e-s-o/test-log/compare/v0.2.17...v0.2.18)

- Improved cooperation with other similar procedural macros to enable
  attribute stacking

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
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 was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNy4yIiwidXBkYXRlZEluVmVyIjoiNDEuMTcuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 10:04:50 +02:00
renovate[bot]
fc758bb755
Update Rust crate schemars to v1.0.4 (#14476)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [schemars](https://graham.cool/schemars/)
([source](https://redirect.github.com/GREsau/schemars)) |
workspace.dependencies | patch | `1.0.3` -> `1.0.4` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>GREsau/schemars (schemars)</summary>

###
[`v1.0.4`](https://redirect.github.com/GREsau/schemars/blob/HEAD/CHANGELOG.md#104---2025-07-06)

[Compare
Source](https://redirect.github.com/GREsau/schemars/compare/v1.0.3...v1.0.4)

##### Fixed

- Fix `JsonSchema` impl on
[atomic](https://doc.rust-lang.org/std/sync/atomic/) types being ignored
on non-nightly compilers due to a buggy `cfg` check
([https://github.com/GREsau/schemars/issues/453](https://redirect.github.com/GREsau/schemars/issues/453))
- Fix compatibility with minimal dependency versions, e.g. old(-ish)
versions of `syn`
([https://github.com/GREsau/schemars/issues/450](https://redirect.github.com/GREsau/schemars/issues/450))
- Fix derive for empty tuple variants
([https://github.com/GREsau/schemars/issues/455](https://redirect.github.com/GREsau/schemars/issues/455))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
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 was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNy4yIiwidXBkYXRlZEluVmVyIjoiNDEuMTcuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 10:00:52 +02:00
renovate[bot]
1308c85efe
Update Rust crate async-channel to v2.5.0 (#14478)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [async-channel](https://redirect.github.com/smol-rs/async-channel) |
workspace.dependencies | minor | `2.3.1` -> `2.5.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.

---

### Release Notes

<details>
<summary>smol-rs/async-channel (async-channel)</summary>

###
[`v2.5.0`](https://redirect.github.com/smol-rs/async-channel/blob/HEAD/CHANGELOG.md#Version-250)

[Compare
Source](https://redirect.github.com/smol-rs/async-channel/compare/v2.4.0...v2.5.0)

- Add `Sender::closed()`
([#&#8203;102](https://redirect.github.com/smol-rs/async-channel/issues/102))

###
[`v2.4.0`](https://redirect.github.com/smol-rs/async-channel/blob/HEAD/CHANGELOG.md#Version-240)

[Compare
Source](https://redirect.github.com/smol-rs/async-channel/compare/v2.3.1...v2.4.0)

- Add `Sender::same_channel()` and `Receiver::same_channel()`.
([#&#8203;98](https://redirect.github.com/smol-rs/async-channel/issues/98))
- Add `portable-atomic` feature to support platforms without atomics.
([#&#8203;106](https://redirect.github.com/smol-rs/async-channel/issues/106))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC),
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 was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/astral-sh/uv).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xNy4yIiwidXBkYXRlZEluVmVyIjoiNDEuMTcuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 09:58:05 +02:00
John Mumm
f609e1ddaf
Document that VerbatimUrl does not preserve original string after serialization (#14456)
Some checks failed
CI / check cache | macos aarch64 (push) Has been cancelled
CI / check system | python on debian (push) Has been cancelled
CI / check system | python on fedora (push) Has been cancelled
CI / check system | python on ubuntu (push) Has been cancelled
CI / check system | python on rocky linux 8 (push) Has been cancelled
CI / check system | python on rocky linux 9 (push) Has been cancelled
CI / check system | graalpy on ubuntu (push) Has been cancelled
CI / check system | pypy on ubuntu (push) Has been cancelled
CI / check system | python on macos aarch64 (push) Has been cancelled
CI / check system | homebrew python on macos aarch64 (push) Has been cancelled
CI / check system | python on macos x86-64 (push) Has been cancelled
CI / check system | python3.10 on windows x86-64 (push) Has been cancelled
CI / check system | python3.10 on windows x86 (push) Has been cancelled
CI / check system | python3.13 on windows x86-64 (push) Has been cancelled
CI / check system | x86-64 python3.13 on windows aarch64 (push) Has been cancelled
CI / check system | aarch64 python3.13 on windows aarch64 (push) Has been cancelled
CI / check system | windows registry (push) Has been cancelled
CI / check system | python3.12 via chocolatey (push) Has been cancelled
CI / check system | python3.9 via pyenv (push) Has been cancelled
CI / check system | python3.13 (push) Has been cancelled
CI / check system | conda3.11 on macos aarch64 (push) Has been cancelled
CI / check system | conda3.8 on macos aarch64 (push) Has been cancelled
CI / check system | conda3.11 on linux x86-64 (push) Has been cancelled
CI / check system | conda3.8 on linux x86-64 (push) Has been cancelled
CI / check system | conda3.11 on windows x86-64 (push) Has been cancelled
CI / check system | conda3.8 on windows x86-64 (push) Has been cancelled
CI / check system | amazonlinux (push) Has been cancelled
CI / check system | embedded python3.10 on windows x86-64 (push) Has been cancelled
CI / benchmarks | walltime aarch64 linux (push) Has been cancelled
CI / benchmarks | instrumented (push) Has been cancelled
This came up in
[discussion](https://github.com/astral-sh/uv/pull/14387#issuecomment-3032223670)
on #14387.
2025-07-04 22:42:56 +02:00
Tim de Jager
eaf517efd8
Add method to get packages involved in a NoSolutionError (#14457)
Some checks are pending
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

<!-- What's the purpose of the change? What does it do, and why? -->

In pixi we overlay the PyPI packages over the conda packages and we
sometimes need to figure out what PyPI packages are involved in the
no-solution error. We could parse the error message, but this is pretty
error-prone, so it would be good to get access to more information. A
lot of information in this module is private and should probably stay
this way, but package names are easy enough to expose. This would help
us a lot!

I collect into a HashSet to remove duplication, and did not want to
expose a rustc_hash datastructure directly, thats's why I've chosen to
expose as an iterator :)

Let me know if any changes need to be done, and thanks!

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
2025-07-04 18:08:23 +00:00
Charlie Marsh
e8bc3950ef
Remove transparent variants in uv-extract to enable retries (#14450)
Some checks are pending
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
## Summary

We think this is the culprit for the lack of retries in some settings
(e.g., Python downloads).

See: https://github.com/astral-sh/uv/issues/14425.
2025-07-03 23:32:07 +00:00
konsti
06af93fce7
Fix optional cfg gates (#14448)
Running `cargo clippy` in individual crates could raise warnings due to
unused imports as `Cow` is only used with `#[cfg(feature = "schemars")]`
2025-07-03 15:29:03 -05:00
Simon Sure
8afbd86f03
make ErrorTree for NoSolutionError externally accessible (#14444)
Some checks are pending
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
Hey, are you okay with exposing the `ErrorTree` for library consumers?

We have a use case that needs more information on conflicts. We need the
tree-structure of the conflict and be able to traverse it in particular.

Signed-off-by: Simon Sure <ssure@palantir.com>
2025-07-03 11:43:59 -05:00
konsti
a1cda6213c
Make "exit code" -> "exit status" a default filter (#14441)
Remove some test boilerplate.

Revival of https://github.com/astral-sh/uv/pull/14439 with main as base.
2025-07-03 13:50:40 +00:00
konsti
39cdfe9981
Add a test for --force-pep517 (#14310)
There was previously a gap in the test coverage in ensuring that
`--force-pep517` was respected.
2025-07-03 13:34:44 +00:00
Zanie Blue
85c0fc963b
Fix forced resolution with all extras in uv version (#14434)
Closes https://github.com/astral-sh/uv/issues/14433

Same as https://github.com/astral-sh/uv/pull/13380
2025-07-03 07:29:59 -05:00
Zanie Blue
c3f13d2505
Finish incomplete sentence in pip migration guide (#14432)
Some checks are pending
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
Fixes https://github.com/astral-sh/uv/pull/12382#discussion_r2181237729
2025-07-03 01:02:17 +00:00
41 changed files with 459 additions and 271 deletions

View file

@ -1585,7 +1585,7 @@ jobs:
run: chmod +x ./uv run: chmod +x ./uv
- name: "Configure AWS credentials" - name: "Configure AWS credentials"
uses: aws-actions/configure-aws-credentials@3d8cba388a057b13744d61818a337e40a119b1a7 uses: aws-actions/configure-aws-credentials@f503a1870408dcf2c35d5c2b8a68e69211042c7d
with: with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

View file

@ -12,7 +12,7 @@ repos:
- id: validate-pyproject - id: validate-pyproject
- repo: https://github.com/crate-ci/typos - repo: https://github.com/crate-ci/typos
rev: v1.33.1 rev: v1.34.0
hooks: hooks:
- id: typos - id: typos
@ -42,7 +42,7 @@ repos:
types_or: [yaml, json5] types_or: [yaml, json5]
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.1 rev: v0.12.2
hooks: hooks:
- id: ruff-format - id: ruff-format
- id: ruff - id: ruff

115
Cargo.lock generated
View file

@ -189,9 +189,9 @@ dependencies = [
[[package]] [[package]]
name = "async-channel" name = "async-channel"
version = "2.3.1" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [ dependencies = [
"concurrent-queue", "concurrent-queue",
"event-listener-strategy", "event-listener-strategy",
@ -1165,9 +1165,9 @@ dependencies = [
[[package]] [[package]]
name = "event-listener-strategy" name = "event-listener-strategy"
version = "0.5.3" version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [ dependencies = [
"event-listener", "event-listener",
"pin-project-lite", "pin-project-lite",
@ -1698,7 +1698,7 @@ dependencies = [
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
"tower-service", "tower-service",
"webpki-roots", "webpki-roots 0.26.8",
] ]
[[package]] [[package]]
@ -1707,6 +1707,7 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
dependencies = [ dependencies = [
"base64 0.22.1",
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -1714,7 +1715,9 @@ dependencies = [
"http", "http",
"http-body", "http-body",
"hyper", "hyper",
"ipnet",
"libc", "libc",
"percent-encoding",
"pin-project-lite", "pin-project-lite",
"socket2", "socket2",
"tokio", "tokio",
@ -1945,6 +1948,16 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]]
name = "iri-string"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
dependencies = [
"memchr",
"serde",
]
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.15" version = "0.4.15"
@ -3062,9 +3075,9 @@ dependencies = [
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.12.15" version = "0.12.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
dependencies = [ dependencies = [
"async-compression", "async-compression",
"base64 0.22.1", "base64 0.22.1",
@ -3079,18 +3092,14 @@ dependencies = [
"hyper", "hyper",
"hyper-rustls", "hyper-rustls",
"hyper-util", "hyper-util",
"ipnet",
"js-sys", "js-sys",
"log", "log",
"mime",
"mime_guess", "mime_guess",
"once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"quinn", "quinn",
"rustls", "rustls",
"rustls-native-certs", "rustls-native-certs",
"rustls-pemfile",
"rustls-pki-types", "rustls-pki-types",
"serde", "serde",
"serde_json", "serde_json",
@ -3098,17 +3107,16 @@ dependencies = [
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
"tokio-socks",
"tokio-util", "tokio-util",
"tower", "tower",
"tower-http",
"tower-service", "tower-service",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"wasm-streams", "wasm-streams",
"web-sys", "web-sys",
"webpki-roots", "webpki-roots 1.0.1",
"windows-registry 0.4.0",
] ]
[[package]] [[package]]
@ -3351,15 +3359,6 @@ dependencies = [
"security-framework", "security-framework",
] ]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.11.0" version = "1.11.0"
@ -3428,9 +3427,9 @@ dependencies = [
[[package]] [[package]]
name = "schemars" name = "schemars"
version = "1.0.3" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
dependencies = [ dependencies = [
"dyn-clone", "dyn-clone",
"ref-cast", "ref-cast",
@ -3442,9 +3441,9 @@ dependencies = [
[[package]] [[package]]
name = "schemars_derive" name = "schemars_derive"
version = "1.0.3" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b13ed22d6d49fe23712e068770b5c4df4a693a2b02eeff8e7ca3135627a24f6" checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3968,9 +3967,9 @@ dependencies = [
[[package]] [[package]]
name = "test-log" name = "test-log"
version = "0.2.17" version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7f46083d221181166e5b6f6b1e5f1d499f3a76888826e6cb1d057554157cd0f" checksum = "1e33b98a582ea0be1168eba097538ee8dd4bbe0f2b01b22ac92ea30054e5be7b"
dependencies = [ dependencies = [
"test-log-macros", "test-log-macros",
"tracing-subscriber", "tracing-subscriber",
@ -3978,9 +3977,9 @@ dependencies = [
[[package]] [[package]]
name = "test-log-macros" name = "test-log-macros"
version = "0.2.17" version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "888d0c3c6db53c0fdab160d2ed5e12ba745383d3e85813f2ea0f2b1475ab553f" checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4172,18 +4171,6 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "tokio-socks"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f"
dependencies = [
"either",
"futures-util",
"thiserror 1.0.69",
"tokio",
]
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.17" version = "0.1.17"
@ -4266,6 +4253,24 @@ dependencies = [
"tower-service", "tower-service",
] ]
[[package]]
name = "tower-http"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
dependencies = [
"bitflags 2.9.1",
"bytes",
"futures-util",
"http",
"http-body",
"iri-string",
"pin-project-lite",
"tower",
"tower-layer",
"tower-service",
]
[[package]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"
@ -5635,7 +5640,7 @@ dependencies = [
"uv-trampoline-builder", "uv-trampoline-builder",
"uv-warnings", "uv-warnings",
"which", "which",
"windows-registry 0.5.3", "windows-registry",
"windows-result 0.3.4", "windows-result 0.3.4",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
@ -5839,7 +5844,7 @@ dependencies = [
"tracing", "tracing",
"uv-fs", "uv-fs",
"uv-static", "uv-static",
"windows-registry 0.5.3", "windows-registry",
"windows-result 0.3.4", "windows-result 0.3.4",
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
@ -6221,6 +6226,15 @@ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
[[package]]
name = "webpki-roots"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.8" version = "0.1.8"
@ -6448,17 +6462,6 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-registry"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
dependencies = [
"windows-result 0.3.4",
"windows-strings 0.3.1",
"windows-targets 0.53.0",
]
[[package]] [[package]]
name = "windows-registry" name = "windows-registry"
version = "0.5.3" version = "0.5.3"

View file

@ -142,7 +142,7 @@ ref-cast = { version = "1.0.24" }
reflink-copy = { version = "0.1.19" } reflink-copy = { version = "0.1.19" }
regex = { version = "1.10.6" } regex = { version = "1.10.6" }
regex-automata = { version = "0.4.8", default-features = false, features = ["dfa-build", "dfa-search", "perf", "std", "syntax"] } regex-automata = { version = "0.4.8", default-features = false, features = ["dfa-build", "dfa-search", "perf", "std", "syntax"] }
reqwest = { version = "=0.12.15", default-features = false, features = ["json", "gzip", "deflate", "zstd", "stream", "rustls-tls", "rustls-tls-native-roots", "socks", "multipart", "http2", "blocking"] } reqwest = { version = "0.12.22", default-features = false, features = ["json", "gzip", "deflate", "zstd", "stream", "rustls-tls", "rustls-tls-native-roots", "socks", "multipart", "http2", "blocking"] }
reqwest-middleware = { git = "https://github.com/astral-sh/reqwest-middleware", rev = "ad8b9d332d1773fde8b4cd008486de5973e0a3f8", features = ["multipart"] } reqwest-middleware = { git = "https://github.com/astral-sh/reqwest-middleware", rev = "ad8b9d332d1773fde8b4cd008486de5973e0a3f8", features = ["multipart"] }
reqwest-retry = { git = "https://github.com/astral-sh/reqwest-middleware", rev = "ad8b9d332d1773fde8b4cd008486de5973e0a3f8" } reqwest-retry = { git = "https://github.com/astral-sh/reqwest-middleware", rev = "ad8b9d332d1773fde8b4cd008486de5973e0a3f8" }
rkyv = { version = "0.8.8", features = ["bytecheck"] } rkyv = { version = "0.8.8", features = ["bytecheck"] }

View file

@ -982,6 +982,45 @@ mod tests {
Ok(()) Ok(())
} }
#[tokio::test]
async fn test_redirect_preserves_fragment() -> Result<()> {
for status in &[301, 302, 303, 307, 308] {
let server = MockServer::start().await;
Mock::given(method("GET"))
.respond_with(
ResponseTemplate::new(*status)
.insert_header("location", format!("{}/redirect", server.uri())),
)
.mount(&server)
.await;
let request = Client::new()
.get(format!("{}#fragment", server.uri()))
.build()
.unwrap();
let response = Client::builder()
.redirect(reqwest::redirect::Policy::none())
.build()
.unwrap()
.execute(request.try_clone().unwrap())
.await
.unwrap();
let redirect_request =
request_into_redirect(request, &response, CrossOriginCredentialsPolicy::Secure)?
.unwrap();
assert!(
redirect_request
.url()
.fragment()
.is_some_and(|fragment| fragment == "fragment")
);
}
Ok(())
}
#[tokio::test] #[tokio::test]
async fn test_redirect_removes_authorization_header_on_cross_origin() -> Result<()> { async fn test_redirect_removes_authorization_header_on_cross_origin() -> Result<()> {
for status in &[301, 302, 303, 307, 308] { for status in &[301, 302, 303, 307, 308] {

View file

@ -1416,44 +1416,6 @@ mod tests {
Ok(()) Ok(())
} }
#[tokio::test]
async fn test_redirect_preserve_fragment() -> Result<(), Error> {
let redirect_server = MockServer::start().await;
// Configure the redirect server to respond with a 307 with a relative URL.
Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(307).insert_header("Location", "/foo".to_string()))
.mount(&redirect_server)
.await;
Mock::given(method("GET"))
.and(path_regex("/foo"))
.respond_with(ResponseTemplate::new(200))
.mount(&redirect_server)
.await;
let cache = Cache::temp()?;
let registry_client = RegistryClientBuilder::new(cache).build();
let client = registry_client.cached_client().uncached();
let mut url = DisplaySafeUrl::parse(&redirect_server.uri())?;
url.set_fragment(Some("fragment"));
assert_eq!(
client
.for_host(&url)
.get(Url::from(url.clone()))
.send()
.await?
.url()
.to_string(),
format!("{}/foo#fragment", redirect_server.uri()),
"Requests should preserve fragment"
);
Ok(())
}
#[test] #[test]
fn ignore_failing_files() { fn ignore_failing_files() {
// 1.7.7 has an invalid requires-python field (double comma), 1.7.8 is valid // 1.7.7 has an invalid requires-python field (double comma), 1.7.8 is valid

View file

@ -1,4 +1,6 @@
use std::{borrow::Cow, str::FromStr}; #[cfg(feature = "schemars")]
use std::borrow::Cow;
use std::str::FromStr;
use uv_pep508::PackageName; use uv_pep508::PackageName;

View file

@ -1,5 +1,6 @@
use std::str::FromStr; #[cfg(feature = "schemars")]
use std::{borrow::Cow, fmt::Formatter}; use std::borrow::Cow;
use std::{fmt::Formatter, str::FromStr};
use uv_pep440::{Version, VersionSpecifier, VersionSpecifiers, VersionSpecifiersParseError}; use uv_pep440::{Version, VersionSpecifier, VersionSpecifiers, VersionSpecifiersParseError};

View file

@ -1,5 +1,7 @@
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::{borrow::Cow, str::FromStr}; #[cfg(feature = "schemars")]
use std::borrow::Cow;
use std::str::FromStr;
use url::Url; use url::Url;
/// A host specification (wildcard, or host, with optional scheme and/or port) for which /// A host specification (wildcard, or host, with optional scheme and/or port) for which

View file

@ -365,7 +365,7 @@ impl InstalledDist {
pub fn installer(&self) -> Result<Option<String>, InstalledDistError> { pub fn installer(&self) -> Result<Option<String>, InstalledDistError> {
let path = self.install_path().join("INSTALLER"); let path = self.install_path().join("INSTALLER");
match fs::read_to_string(path) { match fs::read_to_string(path) {
Ok(installer) => Ok(Some(installer)), Ok(installer) => Ok(Some(installer.trim().to_owned())),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None), Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(err) => Err(err.into()), Err(err) => Err(err.into()),
} }

View file

@ -3,7 +3,9 @@
//! flags set. //! flags set.
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use std::{borrow::Cow, path::Path}; #[cfg(feature = "schemars")]
use std::borrow::Cow;
use std::path::Path;
use crate::{Index, IndexUrl}; use crate::{Index, IndexUrl};

View file

@ -1,4 +1,6 @@
use std::{borrow::Cow, ops::Deref}; #[cfg(feature = "schemars")]
use std::borrow::Cow;
use std::ops::Deref;
use http::StatusCode; use http::StatusCode;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;

View file

@ -2,11 +2,11 @@ use std::{ffi::OsString, path::PathBuf};
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error(transparent)] #[error("Failed to read from zip file")]
Zip(#[from] zip::result::ZipError), Zip(#[from] zip::result::ZipError),
#[error(transparent)] #[error("Failed to read from zip file")]
AsyncZip(#[from] async_zip::error::ZipError), AsyncZip(#[from] async_zip::error::ZipError),
#[error(transparent)] #[error("I/O operation failed during extraction")]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
#[error( #[error(
"The top-level of the archive must only contain a list directory, but it contains: {0:?}" "The top-level of the archive must only contain a list directory, but it contains: {0:?}"

View file

@ -18,11 +18,16 @@ use uv_redacted::DisplaySafeUrl;
use crate::Pep508Url; use crate::Pep508Url;
/// A wrapper around [`Url`] that preserves the original string. /// A wrapper around [`Url`] that preserves the original string.
///
/// The original string is not preserved after serialization/deserialization.
#[derive(Debug, Clone, Eq)] #[derive(Debug, Clone, Eq)]
pub struct VerbatimUrl { pub struct VerbatimUrl {
/// The parsed URL. /// The parsed URL.
url: DisplaySafeUrl, url: DisplaySafeUrl,
/// The URL as it was provided by the user. /// The URL as it was provided by the user.
///
/// Even if originally set, this will be [`None`] after
/// serialization/deserialization.
given: Option<ArcStr>, given: Option<ArcStr>,
} }

View file

@ -33135,6 +33135,102 @@
"sha256": null, "sha256": null,
"variant": "debug" "variant": "debug"
}, },
"pypy-3.11.13-darwin-aarch64-none": {
"name": "pypy",
"arch": {
"family": "aarch64",
"variant": null
},
"os": "darwin",
"libc": "none",
"major": 3,
"minor": 11,
"patch": 13,
"prerelease": "",
"url": "https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_arm64.tar.bz2",
"sha256": "84a48e09c97f57df62cc9f01b7a6d8c3e306b6270671d871aa8ab8c06945940d",
"variant": null
},
"pypy-3.11.13-darwin-x86_64-none": {
"name": "pypy",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "darwin",
"libc": "none",
"major": 3,
"minor": 11,
"patch": 13,
"prerelease": "",
"url": "https://downloads.python.org/pypy/pypy3.11-v7.3.20-macos_x86_64.tar.bz2",
"sha256": "bb3ae80cf5fca5044af2e42933e7692c7c5e76a828ce0eb6404a5d5da83b313c",
"variant": null
},
"pypy-3.11.13-linux-aarch64-gnu": {
"name": "pypy",
"arch": {
"family": "aarch64",
"variant": null
},
"os": "linux",
"libc": "gnu",
"major": 3,
"minor": 11,
"patch": 13,
"prerelease": "",
"url": "https://downloads.python.org/pypy/pypy3.11-v7.3.20-aarch64.tar.bz2",
"sha256": "9347fe691a07fd9df17a1b186554fb9d9e6210178ffef19520a579ce1f9eb741",
"variant": null
},
"pypy-3.11.13-linux-i686-gnu": {
"name": "pypy",
"arch": {
"family": "i686",
"variant": null
},
"os": "linux",
"libc": "gnu",
"major": 3,
"minor": 11,
"patch": 13,
"prerelease": "",
"url": "https://downloads.python.org/pypy/pypy3.11-v7.3.20-linux32.tar.bz2",
"sha256": "d08ce15dd61e9ace5e010b047104f0137110a258184e448ea8239472f10cf99b",
"variant": null
},
"pypy-3.11.13-linux-x86_64-gnu": {
"name": "pypy",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "linux",
"libc": "gnu",
"major": 3,
"minor": 11,
"patch": 13,
"prerelease": "",
"url": "https://downloads.python.org/pypy/pypy3.11-v7.3.20-linux64.tar.bz2",
"sha256": "1410db3a7ae47603e2b7cbfd7ff6390b891b2e041c9eb4f1599f333677bccb3e",
"variant": null
},
"pypy-3.11.13-windows-x86_64-none": {
"name": "pypy",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "windows",
"libc": "none",
"major": 3,
"minor": 11,
"patch": 13,
"prerelease": "",
"url": "https://downloads.python.org/pypy/pypy3.11-v7.3.20-win64.zip",
"sha256": "a8d36f6ceb1d9be6cf24a73b0ba103e7567e396b2f7a33426b05e4a06330755b",
"variant": null
},
"pypy-3.11.11-darwin-aarch64-none": { "pypy-3.11.11-darwin-aarch64-none": {
"name": "pypy", "name": "pypy",
"arch": { "arch": {

View file

@ -1,3 +1,4 @@
#[cfg(feature = "schemars")]
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops::Deref; use std::ops::Deref;

View file

@ -13,10 +13,9 @@ use uv_normalize::PackageName;
use uv_pep440::Version; use uv_pep440::Version;
use uv_types::InstalledPackagesProvider; use uv_types::InstalledPackagesProvider;
use crate::preferences::{Entry, Preferences}; use crate::preferences::{Entry, PreferenceSource, Preferences};
use crate::prerelease::{AllowPrerelease, PrereleaseStrategy}; use crate::prerelease::{AllowPrerelease, PrereleaseStrategy};
use crate::resolution_mode::ResolutionStrategy; use crate::resolution_mode::ResolutionStrategy;
use crate::universal_marker::UniversalMarker;
use crate::version_map::{VersionMap, VersionMapDistHandle}; use crate::version_map::{VersionMap, VersionMapDistHandle};
use crate::{Exclusions, Manifest, Options, ResolverEnvironment}; use crate::{Exclusions, Manifest, Options, ResolverEnvironment};
@ -188,7 +187,7 @@ impl CandidateSelector {
if index.is_some_and(|index| !entry.index().matches(index)) { if index.is_some_and(|index| !entry.index().matches(index)) {
return None; return None;
} }
Either::Left(std::iter::once((entry.marker(), entry.pin().version()))) Either::Left(std::iter::once((entry.pin().version(), entry.source())))
} }
[..] => { [..] => {
type Entries<'a> = SmallVec<[&'a Entry; 3]>; type Entries<'a> = SmallVec<[&'a Entry; 3]>;
@ -219,7 +218,7 @@ impl CandidateSelector {
Either::Right( Either::Right(
preferences preferences
.into_iter() .into_iter()
.map(|entry| (entry.marker(), entry.pin().version())), .map(|entry| (entry.pin().version(), entry.source())),
) )
} }
}; };
@ -238,7 +237,7 @@ impl CandidateSelector {
/// Return the first preference that satisfies the current range and is allowed. /// Return the first preference that satisfies the current range and is allowed.
fn get_preferred_from_iter<'a, InstalledPackages: InstalledPackagesProvider>( fn get_preferred_from_iter<'a, InstalledPackages: InstalledPackagesProvider>(
&'a self, &'a self,
preferences: impl Iterator<Item = (&'a UniversalMarker, &'a Version)>, preferences: impl Iterator<Item = (&'a Version, PreferenceSource)>,
package_name: &'a PackageName, package_name: &'a PackageName,
range: &Range<Version>, range: &Range<Version>,
version_maps: &'a [VersionMap], version_maps: &'a [VersionMap],
@ -246,7 +245,7 @@ impl CandidateSelector {
reinstall: bool, reinstall: bool,
env: &ResolverEnvironment, env: &ResolverEnvironment,
) -> Option<Candidate<'a>> { ) -> Option<Candidate<'a>> {
for (marker, version) in preferences { for (version, source) in preferences {
// Respect the version range for this requirement. // Respect the version range for this requirement.
if !range.contains(version) { if !range.contains(version) {
continue; continue;
@ -290,9 +289,14 @@ impl CandidateSelector {
let allow = match self.prerelease_strategy.allows(package_name, env) { let allow = match self.prerelease_strategy.allows(package_name, env) {
AllowPrerelease::Yes => true, AllowPrerelease::Yes => true,
AllowPrerelease::No => false, AllowPrerelease::No => false,
// If the pre-release is "global" (i.e., provided via a lockfile, rather than // If the pre-release was provided via an existing file, rather than from the
// a fork), accept it unless pre-releases are completely banned. // current solve, accept it unless pre-releases are completely banned.
AllowPrerelease::IfNecessary => marker.is_true(), AllowPrerelease::IfNecessary => match source {
PreferenceSource::Resolver => false,
PreferenceSource::Lock
| PreferenceSource::Environment
| PreferenceSource::RequirementsTxt => true,
},
}; };
if !allow { if !allow {
continue; continue;

View file

@ -3,6 +3,7 @@ use std::fmt::Formatter;
use std::sync::Arc; use std::sync::Arc;
use indexmap::IndexSet; use indexmap::IndexSet;
use itertools::Itertools;
use owo_colors::OwoColorize; use owo_colors::OwoColorize;
use pubgrub::{ use pubgrub::{
DefaultStringReporter, DerivationTree, Derived, External, Range, Ranges, Reporter, Term, DefaultStringReporter, DerivationTree, Derived, External, Range, Ranges, Reporter, Term,
@ -156,7 +157,7 @@ impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ResolveError {
} }
} }
pub(crate) type ErrorTree = DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>; pub type ErrorTree = DerivationTree<PubGrubPackage, Range<Version>, UnavailableReason>;
/// A wrapper around [`pubgrub::error::NoSolutionError`] that displays a resolution failure report. /// A wrapper around [`pubgrub::error::NoSolutionError`] that displays a resolution failure report.
pub struct NoSolutionError { pub struct NoSolutionError {
@ -367,6 +368,11 @@ impl NoSolutionError {
NoSolutionHeader::new(self.env.clone()) NoSolutionHeader::new(self.env.clone())
} }
/// Get the conflict derivation tree for external analysis
pub fn derivation_tree(&self) -> &ErrorTree {
&self.error
}
/// Hint at limiting the resolver environment if universal resolution failed for a target /// Hint at limiting the resolver environment if universal resolution failed for a target
/// that is not the current platform or not the current Python version. /// that is not the current platform or not the current Python version.
fn hint_disjoint_targets(&self, f: &mut Formatter) -> std::fmt::Result { fn hint_disjoint_targets(&self, f: &mut Formatter) -> std::fmt::Result {
@ -404,6 +410,15 @@ impl NoSolutionError {
} }
Ok(()) Ok(())
} }
/// Get the packages that are involved in this error.
pub fn packages(&self) -> impl Iterator<Item = &PackageName> {
self.error
.packages()
.into_iter()
.filter_map(|p| p.name())
.unique()
}
} }
impl std::fmt::Debug for NoSolutionError { impl std::fmt::Debug for NoSolutionError {

View file

@ -1,4 +1,6 @@
use std::{borrow::Cow, str::FromStr}; #[cfg(feature = "schemars")]
use std::borrow::Cow;
use std::str::FromStr;
use jiff::{Timestamp, ToSpan, tz::TimeZone}; use jiff::{Timestamp, ToSpan, tz::TimeZone};

View file

@ -1,5 +1,5 @@
pub use dependency_mode::DependencyMode; pub use dependency_mode::DependencyMode;
pub use error::{NoSolutionError, NoSolutionHeader, ResolveError, SentinelRange}; pub use error::{ErrorTree, NoSolutionError, NoSolutionHeader, ResolveError, SentinelRange};
pub use exclude_newer::ExcludeNewer; pub use exclude_newer::ExcludeNewer;
pub use exclusions::Exclusions; pub use exclusions::Exclusions;
pub use flat_index::{FlatDistributions, FlatIndex}; pub use flat_index::{FlatDistributions, FlatIndex};
@ -54,7 +54,7 @@ mod options;
mod pins; mod pins;
mod preferences; mod preferences;
mod prerelease; mod prerelease;
mod pubgrub; pub mod pubgrub;
mod python_requirement; mod python_requirement;
mod redirect; mod redirect;
mod resolution; mod resolution;

View file

@ -34,6 +34,8 @@ pub struct Preference {
/// is part of, otherwise `None`. /// is part of, otherwise `None`.
fork_markers: Vec<UniversalMarker>, fork_markers: Vec<UniversalMarker>,
hashes: HashDigests, hashes: HashDigests,
/// The source of the preference.
source: PreferenceSource,
} }
impl Preference { impl Preference {
@ -73,6 +75,7 @@ impl Preference {
.map(String::as_str) .map(String::as_str)
.map(HashDigest::from_str) .map(HashDigest::from_str)
.collect::<Result<_, _>>()?, .collect::<Result<_, _>>()?,
source: PreferenceSource::RequirementsTxt,
})) }))
} }
@ -91,6 +94,7 @@ impl Preference {
index: PreferenceIndex::from(package.index(install_path)?), index: PreferenceIndex::from(package.index(install_path)?),
fork_markers: package.fork_markers().to_vec(), fork_markers: package.fork_markers().to_vec(),
hashes: HashDigests::empty(), hashes: HashDigests::empty(),
source: PreferenceSource::Lock,
})) }))
} }
@ -112,6 +116,7 @@ impl Preference {
// `pylock.toml` doesn't have fork annotations. // `pylock.toml` doesn't have fork annotations.
fork_markers: vec![], fork_markers: vec![],
hashes: HashDigests::empty(), hashes: HashDigests::empty(),
source: PreferenceSource::Lock,
})) }))
} }
@ -127,6 +132,7 @@ impl Preference {
index: PreferenceIndex::Any, index: PreferenceIndex::Any,
fork_markers: vec![], fork_markers: vec![],
hashes: HashDigests::empty(), hashes: HashDigests::empty(),
source: PreferenceSource::Environment,
}) })
} }
@ -171,11 +177,24 @@ impl From<Option<IndexUrl>> for PreferenceIndex {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PreferenceSource {
/// The preference is from an installed package in the environment.
Environment,
/// The preference is from a `uv.ock` file.
Lock,
/// The preference is from a `requirements.txt` file.
RequirementsTxt,
/// The preference is from the current solve.
Resolver,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Entry { pub(crate) struct Entry {
marker: UniversalMarker, marker: UniversalMarker,
index: PreferenceIndex, index: PreferenceIndex,
pin: Pin, pin: Pin,
source: PreferenceSource,
} }
impl Entry { impl Entry {
@ -193,6 +212,11 @@ impl Entry {
pub(crate) fn pin(&self) -> &Pin { pub(crate) fn pin(&self) -> &Pin {
&self.pin &self.pin
} }
/// Return the source of the entry.
pub(crate) fn source(&self) -> PreferenceSource {
self.source
}
} }
/// A set of pinned packages that should be preserved during resolution, if possible. /// A set of pinned packages that should be preserved during resolution, if possible.
@ -245,6 +269,7 @@ impl Preferences {
version: preference.version, version: preference.version,
hashes: preference.hashes, hashes: preference.hashes,
}, },
source: preference.source,
}); });
} else { } else {
for fork_marker in preference.fork_markers { for fork_marker in preference.fork_markers {
@ -255,6 +280,7 @@ impl Preferences {
version: preference.version.clone(), version: preference.version.clone(),
hashes: preference.hashes.clone(), hashes: preference.hashes.clone(),
}, },
source: preference.source,
}); });
} }
} }
@ -270,11 +296,13 @@ impl Preferences {
index: Option<IndexUrl>, index: Option<IndexUrl>,
markers: UniversalMarker, markers: UniversalMarker,
pin: impl Into<Pin>, pin: impl Into<Pin>,
source: PreferenceSource,
) { ) {
self.0.entry(package_name).or_default().push(Entry { self.0.entry(package_name).or_default().push(Entry {
marker: markers, marker: markers,
index: PreferenceIndex::from(index), index: PreferenceIndex::from(index),
pin: pin.into(), pin: pin.into(),
source,
}); });
} }

View file

@ -1,6 +1,6 @@
pub(crate) use crate::pubgrub::dependencies::PubGrubDependency; pub(crate) use crate::pubgrub::dependencies::PubGrubDependency;
pub(crate) use crate::pubgrub::distribution::PubGrubDistribution; pub(crate) use crate::pubgrub::distribution::PubGrubDistribution;
pub(crate) use crate::pubgrub::package::{PubGrubPackage, PubGrubPackageInner, PubGrubPython}; pub use crate::pubgrub::package::{PubGrubPackage, PubGrubPackageInner, PubGrubPython};
pub(crate) use crate::pubgrub::priority::{PubGrubPriorities, PubGrubPriority, PubGrubTiebreaker}; pub(crate) use crate::pubgrub::priority::{PubGrubPriorities, PubGrubPriority, PubGrubTiebreaker};
pub(crate) use crate::pubgrub::report::PubGrubReportFormatter; pub(crate) use crate::pubgrub::report::PubGrubReportFormatter;

View file

@ -9,7 +9,7 @@ use crate::python_requirement::PythonRequirement;
/// [`Arc`] wrapper around [`PubGrubPackageInner`] to make cloning (inside PubGrub) cheap. /// [`Arc`] wrapper around [`PubGrubPackageInner`] to make cloning (inside PubGrub) cheap.
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) struct PubGrubPackage(Arc<PubGrubPackageInner>); pub struct PubGrubPackage(Arc<PubGrubPackageInner>);
impl Deref for PubGrubPackage { impl Deref for PubGrubPackage {
type Target = PubGrubPackageInner; type Target = PubGrubPackageInner;
@ -39,7 +39,7 @@ impl From<PubGrubPackageInner> for PubGrubPackage {
/// package (e.g., `black[colorama]`), and mark it as a dependency of the real package (e.g., /// package (e.g., `black[colorama]`), and mark it as a dependency of the real package (e.g.,
/// `black`). We then discard the virtual packages at the end of the resolution process. /// `black`). We then discard the virtual packages at the end of the resolution process.
#[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) enum PubGrubPackageInner { pub enum PubGrubPackageInner {
/// The root package, which is used to start the resolution process. /// The root package, which is used to start the resolution process.
Root(Option<PackageName>), Root(Option<PackageName>),
/// A Python version. /// A Python version.
@ -295,7 +295,7 @@ impl PubGrubPackage {
} }
#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)] #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Hash, Ord)]
pub(crate) enum PubGrubPython { pub enum PubGrubPython {
/// The Python version installed in the current environment. /// The Python version installed in the current environment.
Installed, Installed,
/// The Python version for which dependencies are being resolved. /// The Python version for which dependencies are being resolved.

View file

@ -7,7 +7,7 @@ use uv_platform_tags::{AbiTag, Tags};
/// The reason why a package or a version cannot be used. /// The reason why a package or a version cannot be used.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailableReason { pub enum UnavailableReason {
/// The entire package cannot be used. /// The entire package cannot be used.
Package(UnavailablePackage), Package(UnavailablePackage),
/// A single version cannot be used. /// A single version cannot be used.
@ -29,7 +29,7 @@ impl Display for UnavailableReason {
/// Most variant are from [`MetadataResponse`] without the error source, since we don't format /// Most variant are from [`MetadataResponse`] without the error source, since we don't format
/// the source and we want to merge unavailable messages across versions. /// the source and we want to merge unavailable messages across versions.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailableVersion { pub enum UnavailableVersion {
/// Version is incompatible because it has no usable distributions /// Version is incompatible because it has no usable distributions
IncompatibleDist(IncompatibleDist), IncompatibleDist(IncompatibleDist),
/// The wheel metadata was found, but could not be parsed. /// The wheel metadata was found, but could not be parsed.
@ -123,7 +123,7 @@ impl From<&MetadataUnavailable> for UnavailableVersion {
/// The package is unavailable and cannot be used. /// The package is unavailable and cannot be used.
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum UnavailablePackage { pub enum UnavailablePackage {
/// Index lookups were disabled (i.e., `--no-index`) and the package was not found in a flat index (i.e. from `--find-links`). /// Index lookups were disabled (i.e., `--no-index`) and the package was not found in a flat index (i.e. from `--find-links`).
NoIndex, NoIndex,
/// Network requests were disabled (i.e., `--offline`), and the package was not found in the cache. /// Network requests were disabled (i.e., `--offline`), and the package was not found in the cache.

View file

@ -47,7 +47,7 @@ use crate::fork_strategy::ForkStrategy;
use crate::fork_urls::ForkUrls; use crate::fork_urls::ForkUrls;
use crate::manifest::Manifest; use crate::manifest::Manifest;
use crate::pins::FilePins; use crate::pins::FilePins;
use crate::preferences::Preferences; use crate::preferences::{PreferenceSource, Preferences};
use crate::pubgrub::{ use crate::pubgrub::{
PubGrubDependency, PubGrubDistribution, PubGrubPackage, PubGrubPackageInner, PubGrubPriorities, PubGrubDependency, PubGrubDistribution, PubGrubPackage, PubGrubPackageInner, PubGrubPriorities,
PubGrubPython, PubGrubPython,
@ -447,6 +447,7 @@ impl<InstalledPackages: InstalledPackagesProvider> ResolverState<InstalledPackag
.try_universal_markers() .try_universal_markers()
.unwrap_or(UniversalMarker::TRUE), .unwrap_or(UniversalMarker::TRUE),
version.clone(), version.clone(),
PreferenceSource::Resolver,
); );
} }
} }

View file

@ -6,6 +6,7 @@
//! //!
//! Then lowers them into a dependency specification. //! Then lowers them into a dependency specification.
#[cfg(feature = "schemars")]
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Formatter; use std::fmt::Formatter;

View file

@ -933,7 +933,7 @@ impl ValidatedLock {
lock.prerelease_mode().cyan(), lock.prerelease_mode().cyan(),
options.prerelease_mode.cyan() options.prerelease_mode.cyan()
); );
return Ok(Self::Unusable(lock)); return Ok(Self::Preferable(lock));
} }
if lock.fork_strategy() != options.fork_strategy { if lock.fork_strategy() != options.fork_strategy {
let _ = writeln!( let _ = writeln!(

View file

@ -1730,7 +1730,7 @@ pub(crate) async fn resolve_names(
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum PreferenceSource<'lock> { pub(crate) enum PreferenceLocation<'lock> {
/// The preferences should be extracted from a lockfile. /// The preferences should be extracted from a lockfile.
Lock { Lock {
lock: &'lock Lock, lock: &'lock Lock,
@ -1745,7 +1745,7 @@ pub(crate) struct EnvironmentSpecification<'lock> {
/// The requirements to include in the environment. /// The requirements to include in the environment.
requirements: RequirementsSpecification, requirements: RequirementsSpecification,
/// The preferences to respect when resolving. /// The preferences to respect when resolving.
preferences: Option<PreferenceSource<'lock>>, preferences: Option<PreferenceLocation<'lock>>,
} }
impl From<RequirementsSpecification> for EnvironmentSpecification<'_> { impl From<RequirementsSpecification> for EnvironmentSpecification<'_> {
@ -1758,9 +1758,9 @@ impl From<RequirementsSpecification> for EnvironmentSpecification<'_> {
} }
impl<'lock> EnvironmentSpecification<'lock> { impl<'lock> EnvironmentSpecification<'lock> {
/// Set the [`PreferenceSource`] for the specification. /// Set the [`PreferenceLocation`] for the specification.
#[must_use] #[must_use]
pub(crate) fn with_preferences(self, preferences: PreferenceSource<'lock>) -> Self { pub(crate) fn with_preferences(self, preferences: PreferenceLocation<'lock>) -> Self {
Self { Self {
preferences: Some(preferences), preferences: Some(preferences),
..self ..self
@ -1869,7 +1869,7 @@ pub(crate) async fn resolve_environment(
// If an existing lockfile exists, build up a set of preferences. // If an existing lockfile exists, build up a set of preferences.
let preferences = match spec.preferences { let preferences = match spec.preferences {
Some(PreferenceSource::Lock { lock, install_path }) => { Some(PreferenceLocation::Lock { lock, install_path }) => {
let LockedRequirements { preferences, git } = let LockedRequirements { preferences, git } =
read_lock_requirements(lock, install_path, &upgrade)?; read_lock_requirements(lock, install_path, &upgrade)?;
@ -1881,7 +1881,7 @@ pub(crate) async fn resolve_environment(
preferences preferences
} }
Some(PreferenceSource::Entries(entries)) => entries, Some(PreferenceLocation::Entries(entries)) => entries,
None => vec![], None => vec![],
}; };

View file

@ -49,7 +49,7 @@ use crate::commands::project::install_target::InstallTarget;
use crate::commands::project::lock::LockMode; use crate::commands::project::lock::LockMode;
use crate::commands::project::lock_target::LockTarget; use crate::commands::project::lock_target::LockTarget;
use crate::commands::project::{ use crate::commands::project::{
EnvironmentSpecification, PreferenceSource, ProjectEnvironment, ProjectError, EnvironmentSpecification, PreferenceLocation, ProjectEnvironment, ProjectError,
ScriptEnvironment, ScriptInterpreter, UniversalState, WorkspacePython, ScriptEnvironment, ScriptInterpreter, UniversalState, WorkspacePython,
default_dependency_groups, script_specification, update_environment, default_dependency_groups, script_specification, update_environment,
validate_project_requires_python, validate_project_requires_python,
@ -958,10 +958,10 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
let spec = EnvironmentSpecification::from(spec).with_preferences( let spec = EnvironmentSpecification::from(spec).with_preferences(
if let Some((lock, install_path)) = base_lock.as_ref() { if let Some((lock, install_path)) = base_lock.as_ref() {
// If we have a lockfile, use the locked versions as preferences. // If we have a lockfile, use the locked versions as preferences.
PreferenceSource::Lock { lock, install_path } PreferenceLocation::Lock { lock, install_path }
} else { } else {
// Otherwise, extract preferences from the base environment. // Otherwise, extract preferences from the base environment.
PreferenceSource::Entries( PreferenceLocation::Entries(
base_site_packages base_site_packages
.iter() .iter()
.filter_map(Preference::from_installed) .filter_map(Preference::from_installed)

View file

@ -385,7 +385,7 @@ async fn lock_and_sync(
let default_groups = default_dependency_groups(project.pyproject_toml())?; let default_groups = default_dependency_groups(project.pyproject_toml())?;
let default_extras = DefaultExtras::default(); let default_extras = DefaultExtras::default();
let groups = DependencyGroups::default().with_defaults(default_groups); let groups = DependencyGroups::default().with_defaults(default_groups);
let extras = ExtrasSpecification::from_all_extras().with_defaults(default_extras); let extras = ExtrasSpecification::default().with_defaults(default_extras);
let install_options = InstallOptions::default(); let install_options = InstallOptions::default();
// Convert to an `AddTarget` by attaching the appropriate interpreter or environment. // Convert to an `AddTarget` by attaching the appropriate interpreter or environment.

View file

@ -15,7 +15,7 @@ fn build_basic() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -133,7 +133,7 @@ fn build_sdist() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -189,7 +189,7 @@ fn build_wheel() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -245,7 +245,7 @@ fn build_sdist_wheel() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -303,7 +303,7 @@ fn build_wheel_from_sdist() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -412,7 +412,7 @@ fn build_fail() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -488,7 +488,6 @@ fn build_workspace() -> Result<()> {
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([
(r"exit code: 1", "exit status: 1"),
(r"\\\.", ""), (r"\\\.", ""),
(r"\[project\]", "[PKG]"), (r"\[project\]", "[PKG]"),
(r"\[member\]", "[PKG]"), (r"\[member\]", "[PKG]"),
@ -694,7 +693,6 @@ fn build_all_with_failure() -> Result<()> {
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([
(r"exit code: 1", "exit status: 1"),
(r"\\\.", ""), (r"\\\.", ""),
(r"\[project\]", "[PKG]"), (r"\[project\]", "[PKG]"),
(r"\[member-\w+\]", "[PKG]"), (r"\[member-\w+\]", "[PKG]"),
@ -840,7 +838,7 @@ fn build_constraints() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -901,7 +899,7 @@ fn build_sha() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -1187,7 +1185,7 @@ fn build_tool_uv_sources() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let build = context.temp_dir.child("backend"); let build = context.temp_dir.child("backend");
@ -1337,7 +1335,6 @@ fn build_non_package() -> Result<()> {
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([
(r"exit code: 1", "exit status: 1"),
(r"\\\.", ""), (r"\\\.", ""),
(r"\[project\]", "[PKG]"), (r"\[project\]", "[PKG]"),
(r"\[member\]", "[PKG]"), (r"\[member\]", "[PKG]"),
@ -1930,7 +1927,7 @@ fn build_with_nonnormalized_name() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([(r"exit code: 1", "exit status: 1"), (r"\\\.", "")]) .chain([(r"\\\.", "")])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let project = context.temp_dir.child("project"); let project = context.temp_dir.child("project");
@ -1981,3 +1978,60 @@ fn build_with_nonnormalized_name() -> Result<()> {
Ok(()) Ok(())
} }
/// Check that `--force-pep517` is respected.
///
/// The error messages for a broken project are different for direct builds vs. PEP 517.
#[test]
fn force_pep517() -> Result<()> {
// We need to use a real `uv_build` package.
let context = TestContext::new("3.12").with_exclude_newer("2025-05-27T00:00:00Z");
context
.init()
.arg("--build-backend")
.arg("uv")
.assert()
.success();
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! {r#"
[project]
name = "project"
version = "1.0.0"
[tool.uv.build-backend]
module-name = "does_not_exist"
[build-system]
requires = ["uv_build>=0.5.15,<10000"]
build-backend = "uv_build"
"#})?;
uv_snapshot!(context.filters(), context.build().env("RUST_BACKTRACE", "0"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Building source distribution (uv build backend)...
× Failed to build `[TEMP_DIR]/`
Expected a Python module at: `src/does_not_exist/__init__.py`
");
uv_snapshot!(context.filters(), context.build().arg("--force-pep517").env("RUST_BACKTRACE", "0"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
Building source distribution...
Error: Missing module directory for `does_not_exist` in `src`. Found: `temp`
× Failed to build `[TEMP_DIR]/`
The build backend returned an error
Call to `uv_build.build_sdist` failed (exit status: 1)
hint: This usually indicates a problem with the package or the build environment.
");
Ok(())
}

View file

@ -517,6 +517,8 @@ impl TestContext {
if cfg!(windows) { if cfg!(windows) {
filters.push((" --link-mode <LINK_MODE>".to_string(), String::new())); filters.push((" --link-mode <LINK_MODE>".to_string(), String::new()));
filters.push((r#"link-mode = "copy"\n"#.to_string(), String::new())); filters.push((r#"link-mode = "copy"\n"#.to_string(), String::new()));
// Unix uses "exit status", Windows uses "exit code"
filters.push((r"exit code: ".to_string(), "exit status: ".to_string()));
} }
filters.extend( filters.extend(

View file

@ -7246,10 +7246,7 @@ fn fail_to_add_revert_project() -> Result<()> {
.child("setup.py") .child("setup.py")
.write_str("1/0")?; .write_str("1/0")?;
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.add().arg("./child"), @r#"
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.add().arg("./child"), @r#"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
@ -7351,10 +7348,7 @@ fn fail_to_edit_revert_project() -> Result<()> {
.child("setup.py") .child("setup.py")
.write_str("1/0")?; .write_str("1/0")?;
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.add().arg("./child"), @r#"
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.add().arg("./child"), @r#"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----

View file

@ -23617,10 +23617,7 @@ fn lock_derivation_chain_prod() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.lock(), @r###" uv_snapshot!(filters, context.lock(), @r###"
@ -23677,10 +23674,7 @@ fn lock_derivation_chain_extra() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.lock(), @r###" uv_snapshot!(filters, context.lock(), @r###"
@ -23739,10 +23733,7 @@ fn lock_derivation_chain_group() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.lock(), @r###" uv_snapshot!(filters, context.lock(), @r###"
@ -23812,10 +23803,7 @@ fn lock_derivation_chain_extended() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.lock(), @r###" uv_snapshot!(filters, context.lock(), @r###"

View file

@ -14679,10 +14679,7 @@ fn compile_derivation_chain() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.pip_compile().arg("pyproject.toml"), @r###" uv_snapshot!(filters, context.pip_compile().arg("pyproject.toml"), @r###"

View file

@ -342,10 +342,7 @@ dependencies = ["flask==1.0.x"]
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
requirements_txt.write_str("./path_dep")?; requirements_txt.write_str("./path_dep")?;
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.pip_install()
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.pip_install()
.arg("-r") .arg("-r")
.arg("requirements.txt"), @r###" .arg("requirements.txt"), @r###"
success: false success: false
@ -4930,10 +4927,7 @@ fn no_build_isolation() -> Result<()> {
requirements_in.write_str("anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz")?; requirements_in.write_str("anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz")?;
// We expect the build to fail, because `setuptools` is not installed. // We expect the build to fail, because `setuptools` is not installed.
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.pip_install()
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.pip_install()
.arg("-r") .arg("-r")
.arg("requirements.in") .arg("requirements.in")
.arg("--no-build-isolation"), @r###" .arg("--no-build-isolation"), @r###"
@ -5001,10 +4995,7 @@ fn respect_no_build_isolation_env_var() -> Result<()> {
requirements_in.write_str("anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz")?; requirements_in.write_str("anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz")?;
// We expect the build to fail, because `setuptools` is not installed. // We expect the build to fail, because `setuptools` is not installed.
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.pip_install()
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.pip_install()
.arg("-r") .arg("-r")
.arg("requirements.in") .arg("requirements.in")
.env(EnvVars::UV_NO_BUILD_ISOLATION, "yes"), @r###" .env(EnvVars::UV_NO_BUILD_ISOLATION, "yes"), @r###"
@ -8601,10 +8592,7 @@ fn install_build_isolation_package() -> Result<()> {
)?; )?;
// Running `uv pip install` should fail for iniconfig. // Running `uv pip install` should fail for iniconfig.
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.pip_install()
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.pip_install()
.arg("--no-build-isolation-package") .arg("--no-build-isolation-package")
.arg("iniconfig") .arg("iniconfig")
.arg(package.path()), @r###" .arg(package.path()), @r###"
@ -8931,10 +8919,7 @@ fn missing_top_level() {
fn sklearn() { fn sklearn() {
let context = TestContext::new("3.12"); let context = TestContext::new("3.12");
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.pip_install().arg("sklearn"), @r###"
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.pip_install().arg("sklearn"), @r###"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
@ -8984,10 +8969,7 @@ fn resolve_derivation_chain() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.pip_install() uv_snapshot!(filters, context.pip_install()

View file

@ -1122,10 +1122,7 @@ fn sync_build_isolation_package() -> Result<()> {
)?; )?;
// Running `uv sync` should fail for iniconfig. // Running `uv sync` should fail for iniconfig.
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.sync().arg("--no-build-isolation-package").arg("source-distribution"), @r###"
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(filters, context.sync().arg("--no-build-isolation-package").arg("source-distribution"), @r###"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
@ -1215,10 +1212,7 @@ fn sync_build_isolation_extra() -> Result<()> {
)?; )?;
// Running `uv sync` should fail for the `compile` extra. // Running `uv sync` should fail for the `compile` extra.
let filters = std::iter::once((r"exit code: 1", "exit status: 1")) uv_snapshot!(context.filters(), context.sync().arg("--extra").arg("compile"), @r###"
.chain(context.filters())
.collect::<Vec<_>>();
uv_snapshot!(&filters, context.sync().arg("--extra").arg("compile"), @r###"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
@ -1239,7 +1233,7 @@ fn sync_build_isolation_extra() -> Result<()> {
"###); "###);
// Running `uv sync` with `--all-extras` should also fail. // Running `uv sync` with `--all-extras` should also fail.
uv_snapshot!(&filters, context.sync().arg("--all-extras"), @r###" uv_snapshot!(context.filters(), context.sync().arg("--all-extras"), @r###"
success: false success: false
exit_code: 1 exit_code: 1
----- stdout ----- ----- stdout -----
@ -6985,10 +6979,7 @@ fn sync_derivation_chain() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.sync(), @r###" uv_snapshot!(filters, context.sync(), @r###"
@ -7051,10 +7042,7 @@ fn sync_derivation_chain_extra() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.sync().arg("--extra").arg("wsgi"), @r###" uv_snapshot!(filters, context.sync().arg("--extra").arg("wsgi"), @r###"
@ -7119,10 +7107,7 @@ fn sync_derivation_chain_group() -> Result<()> {
let filters = context let filters = context
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([(r"/.*/src", "/[TMP]/src")])
(r"exit code: 1", "exit status: 1"),
(r"/.*/src", "/[TMP]/src"),
])
.collect::<Vec<_>>(); .collect::<Vec<_>>();
uv_snapshot!(filters, context.sync().arg("--group").arg("wsgi"), @r###" uv_snapshot!(filters, context.sync().arg("--group").arg("wsgi"), @r###"

View file

@ -1682,7 +1682,6 @@ fn tool_install_uninstallable() {
.filters() .filters()
.into_iter() .into_iter()
.chain([ .chain([
(r"exit code: 1", "exit status: 1"),
(r"bdist\.[^/\\\s]+(-[^/\\\s]+)?", "bdist.linux-x86_64"), (r"bdist\.[^/\\\s]+(-[^/\\\s]+)?", "bdist.linux-x86_64"),
(r"\\\.", ""), (r"\\\.", ""),
(r"#+", "#"), (r"#+", "#"),

View file

@ -1958,3 +1958,57 @@ fn version_set_evil_constraints() -> Result<()> {
Ok(()) Ok(())
} }
/// Bump the version with conflicting extras, to ensure we're activating the correct subset of
/// extras during the resolve.
#[test]
fn version_extras() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "myproject"
version = "1.10.31"
requires-python = ">=3.12"
[project.optional-dependencies]
foo = ["requests"]
bar = ["httpx"]
baz = ["flask"]
[tool.uv]
conflicts = [[{"extra" = "foo"}, {"extra" = "bar"}]]
"#,
)?;
uv_snapshot!(context.filters(), context.version()
.arg("--bump").arg("patch"), @r"
success: true
exit_code: 0
----- stdout -----
myproject 1.10.31 => 1.10.32
----- stderr -----
Resolved 19 packages in [TIME]
Audited in [TIME]
");
// Sync an extra, we should not remove it.
context.sync().arg("--extra").arg("foo").assert().success();
uv_snapshot!(context.filters(), context.version()
.arg("--bump").arg("patch"), @r"
success: true
exit_code: 0
----- stdout -----
myproject 1.10.32 => 1.10.33
----- stderr -----
Resolved 19 packages in [TIME]
Audited in [TIME]
");
Ok(())
}

View file

@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
``` ```
It is considered best practice to pin to a specific uv version, e.g., with: It is considered best practice to pin to a specific uv version, e.g., with:
@ -44,7 +44,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
with: with:
# Install a specific version of uv. # Install a specific version of uv.
version: "0.7.19" version: "0.7.19"
@ -66,7 +66,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
- name: Set up Python - name: Set up Python
run: uv python install run: uv python install
@ -93,10 +93,10 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
- name: "Set up Python" - name: "Set up Python"
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version-file: ".python-version" python-version-file: ".python-version"
``` ```
@ -116,10 +116,10 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
- name: "Set up Python" - name: "Set up Python"
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version-file: "pyproject.toml" python-version-file: "pyproject.toml"
``` ```
@ -146,7 +146,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install uv and set the python version - name: Install uv and set the python version
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
``` ```
@ -187,7 +187,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install uv - name: Install uv
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
- name: Install the project - name: Install the project
run: uv sync --locked --all-extras --dev run: uv sync --locked --all-extras --dev
@ -212,44 +212,11 @@ persisting the cache:
```yaml title="example.yml" ```yaml title="example.yml"
- name: Enable caching - name: Enable caching
uses: astral-sh/setup-uv@v5 uses: astral-sh/setup-uv@v6
with: with:
enable-cache: true enable-cache: true
``` ```
You can configure the action to use a custom cache directory on the runner:
```yaml title="example.yml"
- name: Define a custom uv cache path
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-local-path: "/path/to/cache"
```
Or invalidate it when the lockfile changes:
```yaml title="example.yml"
- name: Define a cache dependency glob
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
```
Or when any requirements file changes:
```yaml title="example.yml"
- name: Define a cache dependency glob
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "requirements**.txt"
```
Note that `astral-sh/setup-uv` will automatically use a separate cache key for each host
architecture and platform.
Alternatively, you can manage the cache manually with the `actions/cache` action: Alternatively, you can manage the cache manually with the `actions/cache` action:
```yaml title="example.yml" ```yaml title="example.yml"

View file

@ -324,7 +324,7 @@ so multiple files are not needed to lock development dependencies.
The uv lockfile is always [universal](../../concepts/resolution.md#universal-resolution), so The uv lockfile is always [universal](../../concepts/resolution.md#universal-resolution), so
multiple files are not needed to multiple files are not needed to
[lock dependencies for each platform](#platform-specific-dependencies). This ensures that all [lock dependencies for each platform](#platform-specific-dependencies). This ensures that all
developers developers are using consistent, locked versions of dependencies regardless of their machine.
The uv lockfile also supports concepts like The uv lockfile also supports concepts like
[pinning packages to specific indexes](../../concepts/indexes.md#pinning-a-package-to-an-index), [pinning packages to specific indexes](../../concepts/indexes.md#pinning-a-package-to-an-index),