Closes#14262
## Description
Adds `UV_LIBC` environment variable and implements check within
`Libc::from_env` as recommended here:
https://github.com/astral-sh/uv/issues/14262#issuecomment-3014600313
Gave this a few passes to make sure I follow dev practices within uv as
best I am able. Feel free to call out anything that could be improved.
## Test Plan
Planned to simply run existing test suite. Open to adding more tests
once implementation is validated due to my limited Rust experience.
## Summary
We validate the `uv.toml` when it's discovered automatically, but not
when provided via `--config-file`. The same limitations exist, though --
I think the lack of enforcement is just an oversight.
Closes https://github.com/astral-sh/uv/issues/14650.
Part of #14296
This is the same as `uv tool update-shell` but handles the case where
the Python bin directory is configured to a different path.
```
❯ UV_PYTHON_BIN_DIR=/tmp/foo cargo run -q -- python install --preview 3.13.3
Installed Python 3.13.3 in 1.75s
+ cpython-3.13.3-macos-aarch64-none
warning: `/tmp/foo` is not on your PATH. To use installed Python executables, run `export PATH="/tmp/foo:$PATH"` or `uv python update-shell`.
❯ UV_PYTHON_BIN_DIR=/tmp/foo cargo run -q -- python update-shell
Created configuration file: /Users/zb/.zshenv
Restart your shell to apply changes
❯ cat /Users/zb/.zshenv
# uv
export PATH="/tmp/foo:$PATH"
❯ UV_TOOL_BIN_DIR=/tmp/bar cargo run -q -- tool update-shell
Updated configuration file: /Users/zb/.zshenv
Restart your shell to apply changes
❯ cat /Users/zb/.zshenv
# uv
export PATH="/tmp/foo:$PATH"
# uv
export PATH="/tmp/bar:$PATH"
```
Previously, if installation of executables into the bin directory failed
we'd with a non-zero code. However, if we make this behavior the default
we don't want it to be fatal. There's a `--bin` opt-in to _require_
successful executable installation and a `--no-bin` opt-out to silence
the warning / opt-out of installation entirely.
Part of https://github.com/astral-sh/uv/issues/14296 — we need this
before we can stabilize the behavior.
In #14614 we do the same for writing entries to the Windows registry.
## Summary
You can now override the cache control headers for the Simple API, file
downloads, or both:
```toml
[[tool.uv.index]]
name = "example"
url = "https://example.com/simple"
cache-control = { api = "max-age=600", files = "max-age=365000000, immutable" }
```
Closes https://github.com/astral-sh/uv/issues/10444.
## Summary
There's some inconsistent behaviour in handling symlinks when
`cache-key` is a glob or a file path. This PR attempts to address that.
- When cache-key is a path,
[`Path::metadata()`](https://doc.rust-lang.org/std/path/struct.Path.html#method.metadata)
is used to check if it's a file or not. According to the docs:
> This function will traverse symbolic links to query information about
the destination file.
So, if the target file is a symlink, it will be resolved and the
metadata will be queried for the underlying file.
- When cache-key is a glob, `globwalk` is used, specifically allowing
for symlinks:
```rust
.file_type(globwalk::FileType::FILE | globwalk::FileType::SYMLINK)
```
- However, without enabling link following, `DirEntry::metadata()` will
return an equivalent of `Path::symlink_metadata()` (and not
`Path::metadata()`), which will have a file type that looks like
```rust
FileType {
is_file: false,
is_dir: false,
is_symlink: true,
..
}
```
- Then, there's a check for `metadata.is_file()` which fails and
complains that the target entry "is a directory when file was expected".
- TLDR: glob cache-keys don't work with symlinks.
## Solutions
Option 1 (current PR): follow symlinks.
Option 2 (also doable): don't follow symlinks, but resolve the resulting
target entry manually in case its file type is a symlink. However, this
would be a little weird and unobvious in that we resolve files but not
directories for some reason. Also, symlinking directories is pretty
useful if you want to symlink directories of local dependencies that are
not under the project's path.
## Test Plan
This has been tested manually:
```rust
fn main() {
for follow_links in [false, true] {
let walker = globwalk::GlobWalkerBuilder::from_patterns(".", &["a/*"])
.file_type(globwalk::FileType::FILE | globwalk::FileType::SYMLINK)
.follow_links(follow_links)
.build()
.unwrap();
let entry = walker.into_iter().next().unwrap().unwrap();
dbg!(&entry);
dbg!(entry.file_type());
dbg!(entry.path_is_symlink());
dbg!(entry.path());
let meta = entry.metadata().unwrap();
dbg!(meta.is_file());
}
let path = std::path::PathBuf::from("./a/b");
dbg!(path.metadata().unwrap().file_type());
dbg!(path.symlink_metadata().unwrap().file_type());
}
```
Current behaviour (glob cache-key, don't follow links):
```
[src/main.rs:9:9] &entry = DirEntry("./a/b")
[src/main.rs:10:9] entry.file_type() = FileType {
is_file: false,
is_dir: false,
is_symlink: true,
..
}
[src/main.rs:11:9] entry.path_is_symlink() = true
[src/main.rs:12:9] entry.path() = "./a/b"
[src/main.rs:14:9] meta.is_file() = false
```
Glob cache-key, follow links:
```
[src/main.rs:9:9] &entry = DirEntry("./a/b")
[src/main.rs:10:9] entry.file_type() = FileType {
is_file: true,
is_dir: false,
is_symlink: false,
..
}
[src/main.rs:11:9] entry.path_is_symlink() = true
[src/main.rs:12:9] entry.path() = "./a/b"
[src/main.rs:14:9] meta.is_file() = true
```
Using `path.metadata()` for a non-glob cache key:
```
[src/main.rs:18:5] path.metadata().unwrap().file_type() = FileType {
is_file: true,
is_dir: false,
is_symlink: false,
..
}
[src/main.rs:19:5] path.symlink_metadata().unwrap().file_type() = FileType {
is_file: false,
is_dir: false,
is_symlink: true,
..
}
```
This is a continuation of the work in
* #12405
I have:
* moved to an architecture where the human output is derived from the
json structs to centralize more of the printing state/logic
* cleaned up some of the names/types
* added tests
* removed the restriction that this output is --dry-run only
I have not yet added package info, which was TBD in their design.
---------
Co-authored-by: x0rw <mahdi.svt5@gmail.com>
Co-authored-by: Zanie Blue <contact@zanie.dev>
Co-authored-by: John Mumm <jtfmumm@gmail.com>
We've seen a few cases of uv.exe exiting with an exception code as its
exit status and no user-visible output (#14563 in the field, and #13812
in CI). It seems that recent versions of Windows no longer show dialog
boxes on access violations (what UNIX calls segfaults) or similar
errors. Something is probably sent to Windows Error Reporting, and we
can maybe sign up to get the crashes from Microsoft, but the user
experience of seeing uv exit with no output is poor, both for end users
and during development. While it's possible to opt out of this behavior
or set up a debugger, this isn't the default configuration. (See
https://superuser.com/q/1246626 for some pointers.)
In order to get some output on a crash, we need to install our own
default handler for unhandled exceptions (or call all our code inside a
Structured Exception Handling __try/__catch block, which is complicated
on Rust). This is the moral equivalent of a segfault handler on Windows;
the kernel creates a new stack frame and passes arguments to it with
some processor state.
This commit adds a relatively simple exception handler that leans on
Rust's own backtrace implementation and also displays some minimal
information from the exception itself. This should be enough info to
communicate that something went wrong and let us collect enough
information to attempt to debug. There are also a handful of (non-Rust)
open-source libraries for this like Breakpad and Crashpad (both from
Google) and crashrpt.
The approach here, of using SetUnhandledExceptionFilter, seems to be the
standard one taken by other such libraries. Crashpad also seems to try
to use a newer mechanism for an out-of-tree DLL to report the crash:
https://issues.chromium.org/issues/42310037
If we have serious problems with memory corruption, it might be worth
adopting some third-party library that has already implemented this
approach. (In general, the docs of other crash reporting libraries are
worth skimming to understand how these things ought to work.)
Co-authored-by: samypr100 <3933065+samypr100@users.noreply.github.com>
## Summary
(Related PR: #13438 - would be nice to have it merged as well since it
touches on the same globwalker code)
There's a few issues with `cache-key` globs, which this PR attempts to
address:
- As of the current state, parent or absolute paths are not allowed,
which is not obvious and is not documented. E.g., cache-key paths of the
form `{file = "../dep/**"}` will be essentially ignored.
- Absolute glob patterns also don't work (funnily enough, there's logic
in `globwalk` itself that attempts to address it in
[`globwalk::glob_builder()`](8973fa2bc5/src/lib.rs (L415)),
which serves as inspiration to some parts of this PR).
- The reason for parent paths being ignored is the way globwalker is
currently being triggered in `uv-cache-info`: the base directory is
being walked over completely and each entry is then being matched to one
of the provided match patterns.
- This may also end up being very inefficient if you have a huge root
folder with thousands of files: if your match patterns are `a/b/*.rs`
and `a/c/*.py` then instead of walking over the root directory, you can
just walk over `a/b` and `a/c` and match the relevant patterns there.
- Why supporting parent paths may be important to the point of being a
blocker: in large codebases with python projects depending on other
local non-python projects (e.g. rust crates), cache-keys can be very
useful to track dependency on the source code of the latter (e.g.
`cache-keys = [{ file = "../../crates/some-dep/**" }]`.
- TLDR: parent/absolute cache-key globs don't work, glob walk can be
slow.
## Solution
- In this PR, user-provided glob patterns are first clustered
(LCP-style) into pattern groups with longest common path prefix; each of
these groups can then be walked over separately.
- Pattern groups do not overlap, so we would never walk over the same
directory twice (unless there's symlinks pointing to same folders).
- Paths are not canonicalized nor virtually normalized (which is
impossible on Unix without FS access), so the method is symlink-safe
(i.e. we don't treat `a/b/..` as `a`) and should work fine with #13438.
- Because of LCP logic, the minimal amount of directory space will be
traversed to cover all patterns.
- Absolute glob patterns will now work.
- Parent-relative glob patterns will now work.
- Glob walking will be more efficient in some cases.
## Possible improvements
- Efficiency can be further greatly improved if we limit max depth for
globwalk. Currently, a simple ".toml" will deep-traverse the whole
folder. Essentially, max depth can be always set to either N or
infinity. If a pattern at a pivot node contains `**`, we collect all
children nodes from the subtree into the same group and don't limit max
depth; otherwise, we set max depth to the length of the glob pattern.
This wouldn't change correctness though and can we done separately if
needed.
- If this is considered important enough, docs can be updated to
indicate that parent and absolute globs are supported (and symlinks are
resolved, if the relevant PR is algo merged in).
## Test Plan
- Glob splitting and clustering tests are included in the PR.
- Relative and absolute glob cache-keys were tested in an actual
codebase.
<!--
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
This is a small quality of life feature that adds a shorthand (`-w`) to
the `--with` flag for minimizing keystrokes.
Pretty minor, but I didn't see any conflicts with `-w` and thought this
could be a nice place for it.
```bash
# proposed addition (short)
uvx -w numpy ipython
# original (long)
uvx --with numpy ipython
```
## Test Plan
Added testing already in the P.R. - just copied over tests from the
`--with` flag
<!-- How was it tested? -->
<!--
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
The `version_extras` test added in
85c0fc963b needs to connect to PyPI. This
PR conditionalizes it on the `pypi` extra so that people running the
tests offline don’t have to skip that test explicitly.
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
<!-- How was it tested? -->
I already ran `cargo test` in the git checkout to confirm I didn’t
somehow introduce a syntax error. I am also applying this PR as a patch
to [the `uv` package in Fedora](https://src.fedoraproject.org/rpms/uv),
which runs tests offline with the `pypi` feature disabled.
Follow-up to https://github.com/astral-sh/uv/pull/14509 to provide the
_reason_ downloads are disabled and surface it as a hint rather than a
debug log.
e.g.,
```
❯ cargo run -q -- run --no-managed-python -p 3.13.4 python
error: No interpreter found for Python 3.13.4 in virtual environments or search path
hint: A managed Python download is available for Python 3.13.4, but the Python preference is set to 'only system'
```
This adds `alpha`, `beta`, `rc`, `stable`, `post`, and `dev` modes to
`uv version --bump`.
The components that `--bump` accepts are ordered as follows:
major > minor > patch > stable > alpha > beta > rc > post > dev
Bumping a component "clears" all lesser component (`alpha`, `beta`, and
`rc` all overwrite each other):
* `--bump minor` on `1.2.3a4.post5.dev6` => `1.3.0`
* `--bump alpha` on `1.2.3a4.post5.dev6` => `1.2.3a5`
* `--bump dev ` on `1.2.3a4.post5.dev6` => `1.2.3a4.post5.dev7`
In addition, `--bump` can now be repeated. The primary motivation of
this is "bump stable version and also enter a prerelease", but it
technically lets you express other things if you want them:
* `--bump patch --bump alpha` on `1.2.3` => `1.2.4a1` ("bump patch
version and go to alpha 1")
* `--bump minor --bump patch` on `1.2.3` => `1.3.1` ("bump minor version
and got to patch 1")
* `--bump minor --bump minor` on `1.2.3` => `1.4.0` ("bump minor version
twice")
The `--bump` flags are sorted by their priority, so that you don't need
to remember the priority yourself. This ordering is the only "useful"
one that preserves every `--bump` you passed, so there's no concern
about loss of expressiveness. For instance `--bump minor --bump major`
would just be `--bump major` if we didn't sort, as the major bump clears
the minor version. The ordering of `beta` after `alpha` means `--bump
alpha --bump beta` will just result in beta 1; this is the one case
where a bump request will effectively get overwritten.
The `stable` mode "bumps to the next stable release", clearing the pre
(`alpha`, `beta`, `rc`), `dev`, and `post` components from a version
(`1.2.3a4.post5.dev6` => `1.2.3`). The choice to clear `post` here is a
bit odd, in that `1.2.3.post4` => `1.2.3` is actually a version
decrease, but I think this gives a more intuitive model (as preserving
`post5` in the previous example is definitely wrong), and also
post-releases are extremely obscure so probably no one will notice. In
the cases where this behaviour isn't useful, you probably wanted to pass
`--bump patch` or something anyway which *should* definitely clear the
`post5` (putting it another way: the only cases where `--bump stable`
has dubious behaviour is when you wanted it to do a noop, which, is a
command you could have just not written at all).
In all cases we preserve the "epoch" and "local" components of a
version, so the `7!` and `+local` in `7!1.2.3+local` will never be
modified by `--bump` (you can use the raw version set mode if you want
to touch those). The preservation of `local` is another slightly odd
choice, but it's a really obscure feature (so again it mostly won't come
up) and when it's used it seems to mostly be used for referring to
variant releases, in which case preserving it tends to be correct.
Fixes#13223
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
<!--
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
remove redundant words in comment
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
<!-- How was it tested? -->
Signed-off-by: jingchanglu <jingchanglu@outlook.com>
Support multiple root modules in namespace packages by enumerating them:
```toml
[tool.uv.build-backend]
module-name = ["foo", "bar"]
```
This allows applications with multiple root packages without migrating
to workspaces. Since those are regular module names (we iterate over
them an process each one like a single module names), it allows
combining dotted (namespace) names and regular names. It also
technically allows combining regular and stub modules, even though this
is even less recommends.
We don't recommend this structure (please use a workspace instead, or
structure everything in one root module), but it reduces the number of
cases that need `namespace = true`.
Fixes#14435Fixes#14438
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
This PR intends to enable `--torch-backend=auto` to detect Intel GPUs
automatically:
- On Linux, detection is performed using the `lspci` command via
`Display controller` id.
- On Windows, ~~detection is done via a `powershell` query to
`Win32_VideoController`~~. Skip support for now—revisit once a better
solution is available.
Currently, Intel GPUs (XPU) do not rely on specific driver or toolkit
versions to distribute different PyTorch wheels.
## Test Plan
<!-- How was it tested? -->
On Linux:

~~On Windows:
~~
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Reverts:
- #14349
- #14346
- #14245
Retains the test cases. Includes a `find-links` test case.
Supersedes
- https://github.com/astral-sh/uv/pull/14387
- https://github.com/astral-sh/uv/pull/14503
We originally got a report at
https://github.com/astral-sh/uv/issues/13707 that inclusion of a
trailing slash on an index URL was causing lockfile churn despite having
no semantic meaning and resolved the issue by adding normalization that
stripped trailing slashes at parse time.
We then discovered that, while there are not semantic differences for
trailing slashes on Simple API index URLs, there are differences for
some flat (or find links) indexes. As reported in
https://github.com/astral-sh/uv/issues/14367, the change in
https://github.com/astral-sh/uv/pull/14245 caused a regression for at
least one user.
We attempted to fix the regression via a few approaches.
https://github.com/astral-sh/uv/pull/14387 attempted to differentiate
between Simple API and flat index URL parsing, but failed to account for
the `Deserialize` implementation, which always assumed Simple API-style
index URLs and incorrectly trimmed trailing slashes in various cases
where we deserialized the `IndexUrl` type from a file. I attempted to
resolve this by performing a larger refactor, but it ended up being
quite painful. In particular, the `Index` type was a blocker — we don't
know the `IndexUrl` variant until we've parsed the `IndexFormat` and
having a multi-stage deserializer is not appealing but adding a new
intermediate type (i.e., `RawIndex`) is painful due to the pervasiveness
of `Index`. Given that we've regressed behavior here and there's not a
straight-forward fix, we're reverting the normalization entirely.
https://github.com/astral-sh/uv/pull/14503 attempted to perform
normalization at compare-time, but that means we'd fail to invalidate
the lockfile when the a trailing slash was added or removed and given
that a trailing slash has semantic meaning for a find-links URL... we'd
have another correctness problem.
After this revert, we'll retain all index URLs verbatim. The downside to
this approach is that we'll be adding a bunch of trailing slashes back
to lockfiles that we previously normalized out, and we'll be reverting
our fix for users with inconsistent trailing slashes on their index
URLs. Users affected by the original motivating issue should use
consistent trailing slashes on their URLs, as they do frequently have
semantic meaning. We may want to revisit normalization and type aware
index URL parsing as part of a larger change.
Closes https://github.com/astral-sh/uv/issues/14367
<!--
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? -->
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.
<!--
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>
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>
The uv build backend has gone through some feedback cycles, we expect no
more major configuration changes, and we're ready to take the next step:
The uv build backend in stable.
This PR stabilizes:
* Using `uv_build` as build backend
* The documentation of the uv build backend
* The direct build fast path, where uv doesn't use PEP 517 if you're
using `uv_build` in a compatible version.
* `uv build --list`, which is limited to `uv_build`.
It does not:
* Make `uv_build` the default on `uv init`
* Make `--package` the default on `uv init`
## Summary
If we fail to acquire a lock on an environment, uv shouldn't fail; we
should just warn. In some cases, users run uv with read-only permissions
for their projects, etc.
For now, I kept any locks acquired _in the cache_ as hard failures,
since we always need write-access to the cache.
Closes https://github.com/astral-sh/uv/issues/14411.
## Summary
The idea here is that if a user runs `uv pip compile --universal`, we
should ignore the patch version on the current interpreter. I think this
makes sense... `--universal` tries to resolve for all future versions,
so it seems a bit odd that we'd start at the _current_ patch version.
Closes https://github.com/astral-sh/uv/issues/14397.
Clap does not perform global validation, so flag that are declared as
overriding can be set at the same time:
https://github.com/clap-rs/clap/issues/6049. This would previously cause
a panic. We work around this by choosing the yes-value always and
writing a warning.
An alternative would be erroring when both are set, but it's unclear to
me if this may break things we want to support. (`UV_OFFLINE=1 cargo run
-q pip --no-offline install tqdm --no-cache` is already banned).
Fixes https://github.com/astral-sh/uv/pull/14299
**Test Plan**
```
$ cargo run -q pip --offline install --no-offline tqdm --no-cache
warning: Boolean flags on different levels are not correctly supported (https://github.com/clap-rs/clap/issues/6049)
× No solution found when resolving dependencies:
╰─▶ Because tqdm was not found in the cache and you require tqdm, we can conclude that your requirements are unsatisfiable.
hint: Packages were unavailable because the network was disabled. When the network is disabled, registry packages may only be read from the cache.
```
## Summary
The basic idea here is that we can (should) reuse a build environment
across resolution (`prepare_metadata_for_build_wheel`) and installation.
This also happens to solve the build-PyTorch-from-source problem, since
we use a consistent build environment between the invocations.
Since `SourceDistributionBuilder` is stateless, we instead store the
builds on `BuildContext`, and we key them by various properties: the
underlying interpreter, the configuration settings, etc. This just
ensures that if we build the same package twice within a process, we
don't accidentally reuse an incompatible build (virtual) environment.
(Note that still drop build environments at the end of the command, and
don't attempt to reuse them across processes.)
Closes#14269.
If/when we see https://github.com/astral-sh/uv/issues/14171 again, this
should clarify whether our retry logic was skipped (i.e. a transient
error wasn't correctly identified as transient), or whether we exhausted
our retries. Previously, if you ran a local example fileserver as in
https://github.com/astral-sh/uv/issues/14171#issuecomment-3014580701 and
then you tried to install Python from it, you'd get:
```
$ export UV_TEST_NO_CLI_PROGRESS=1
$ uv python install 3.8.20 --mirror http://localhost:8000 2>&1 | cat
error: Failed to install cpython-3.8.20-linux-x86_64-gnu
Caused by: Failed to extract archive: cpython-3.8.20-20241002-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz
Caused by: failed to unpack `/home/jacko/.local/share/uv/python/.temp/.tmpS4sHHZ/python/lib/libpython3.8.so.1.0`
Caused by: failed to unpack `python/lib/libpython3.8.so.1.0` into `/home/jacko/.local/share/uv/python/.temp/.tmpS4sHHZ/python/lib/libpython3.8.so.1.0`
Caused by: error decoding response body
Caused by: request or response body error
Caused by: error reading a body from connection
Caused by: Connection reset by peer (os error 104)
```
With this change you get:
```
error: Failed to install cpython-3.8.20-linux-x86_64-gnu
Caused by: Request failed after 3 retries
Caused by: Failed to extract archive: cpython-3.8.20-20241002-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz
Caused by: failed to unpack `/home/jacko/.local/share/uv/python/.temp/.tmp4Ia24w/python/lib/libpython3.8.so.1.0`
Caused by: failed to unpack `python/lib/libpython3.8.so.1.0` into `/home/jacko/.local/share/uv/python/.temp/.tmp4Ia24w/python/lib/libpython3.8.so.1.0`
Caused by: error decoding response body
Caused by: request or response body error
Caused by: error reading a body from connection
Caused by: Connection reset by peer (os error 104)
```
At the same time, I'm updating the way we handle the retry count to
avoid nested retry loops exceeding the intended number of attempts, as I
mentioned at
https://github.com/astral-sh/uv/issues/14069#issuecomment-3020634281.
It's not clear to me whether we actually want this part of the change,
and I need feedback here.
## Summary
This PR ensures that we avoid cleaning up build directories until the
end of a resolve-and-install cycle. It's not bulletproof (since we could
still run into issues with `uv lock` followed by `uv sync` whereby a
build directory gets cleaned up that's still referenced in the `build`
artifacts), but it at least gets PyTorch building without error with `uv
pip install .`, which is a case that's been reported several times.
Closes https://github.com/astral-sh/uv/issues/14269.
The marker display code assumes that all versions are normalized, in
that all trailing zeroes are stripped. This is not the case for
tilde-equals and equals-star versions, where the trailing zeroes (before
the `.*`) are semantically relevant. This would cause path
dependent-behavior where we would get a different marker string
depending on whether a version with or without a trailing zero was added
to the cache first.
To handle both equals-star and tilde-equals when converting
`python_version` to `python_full_version` markers, we have to merge the
version normalization (i.e. trimming the trailing zeroes) and the
conversion both to `python_full_version` and to `Ranges`, while special
casing equals-star and tilde-equals.
To avoid churn in lockfiles, we only trim in the conversion to `Ranges`
for markers, but keep using untrimmed versions for requires-python.
(Note that this behavior is technically also path dependent, as versions
with and without trailing zeroes have the same Hash and Eq. E.q.,
`requires-python == ">= 3.10.0"` and `requires-python == ">= 3.10"` in
the same workspace could lead to either value in `uv.lock`, and which
one it is could change if we make unrelated (performance) changes.
Always trimming however definitely changes lockfiles, a churn I wouldn't
do outside another breaking or lockfile-changing change.) Nevertheless,
there is a change for users who have `requires-python = "~= 3.12.0"` in
their `pyproject.toml`, as this now hits the correct normalization path.
Fixes#14231Fixes#14270
## Summary
There's a good example of the downside of using verbatim URLs here:
https://github.com/astral-sh/uv/pull/14197#discussion_r2163599625 (we
show two relative paths that point to the same directory, but it's not
clear from the error message).
The diff:
```
2 2 │ ----- stdout -----
3 3 │
4 4 │ ----- stderr -----
5 5 │ error: Requirements contain conflicting URLs for package `library` in all marker environments:
6 │-- ../../library
7 │-- ./library
6 │+- file://[TEMP_DIR]/library
7 │+- file://[TEMP_DIR]/library (editable)
```
## Summary
<!-- What's the purpose of the change? What does it do, and why? -->
As explained in the [`codspeed-rust` v3 release
notes](https://github.com/CodSpeedHQ/codspeed-rust/releases/tag/v3.0.0),
the `v3` of the compatibility layers is now required to work with the
latest version(`v3`) of `cargo-codspeed`.
and prefer emulated x64 windows in its stead.
This is preparatory work for shipping support for uv downloading and
installing aarch64 (arm64) windows Pythons. We've [had builds for this
platform ready for a
while](https://github.com/astral-sh/python-build-standalone/pull/387),
but have held back on shipping them due to a fundamental problem:
**The Python packaging ecosystem does not have strong support for
aarch64 windows**, e.g., not many projects build aarch64 wheels yet. The
net effect of this is that, if we handed you an aarch64 python
interpreter on windows, you would have to build a lot more sdists, and
there's a high chance you will simply fail to build that sdist and be
sad.
Yes unfortunately, in this case a non-native Python interpreter simply
*works better* than the native one... in terms of working at all, today.
Of course, if the native interpreter works for your project, it should
presumably have better performance and platform compatibility.
We do not want to stand in the way of progress, as ideally this
situation is a temporary state of affairs as the ecosystem grows to
support aarch64 windows. To enable progress, on aarch64 Windows builds
of uv:
* We will still use a native python interpreter, e.g., if it's at the
front of your `PATH` or the only installed version.
* If we are choosing between equally good interpreters that differ in
architecture, x64 will be preferred.
* If the aarch64 version is newer, we will prefer the aarch64 one.
* We will emit a diagnostic on installation, and show the python request
to pass to uv to force aarch64 windows to be used.
* Will be shipping [aarch64 Windows Python
downloads](https://github.com/astral-sh/python-build-standalone/pull/387)
* Will probably add some kind of global override setting/env-var to
disable this behaviour.
* Will be shipping this behaviour in
[astral-sh/setup-uv](https://github.com/astral-sh/setup-uv)
We're coordinating with Microsoft, GitHub (for the `setup-python`
action), and the CPython team (for the `python.org` installers), to
ensure we're aligned on this default and the timing of toggling to
prefer native distributions in the future.
See discussion in
- https://github.com/astral-sh/uv/issues/12906
---
This is an alternative to
* #13719
which uses sorting rather than filtering, as discussed in
* #13721
This fixes an obscure cache collision in Python interpreter queries,
which we believe to be the root cause of CI flakes we've been seeing
where a project environment is invalidated and recreated.
This work follows from the logs in [this CI
run](https://github.com/astral-sh/uv/actions/runs/15934322410/job/44950599993?pr=14326)
which captured one of the flakes with tracing enabled. There, we can see
that the project environment is invalidated because the Python
interpreter in the environment has a different version than expected:
```
DEBUG Checking for Python environment at `.venv`
TRACE Cached interpreter info for Python 3.12.9, skipping probing: .venv/bin/python3
DEBUG The interpreter in the project environment has different version (3.12.9) than it was created with (3.9.21)
```
(this message is updated to reflect #14329)
The flow is roughly:
- We create an environment with 3.12.9
- We query the environment, and cache the interpreter version for
`.venv/bin/python`
- We create an environment for 3.9.12, replacing the existing one
- We query the environment, and read the cached information
The Python cache entries are keyed by the absolute path to the
interpreter, and rely on the modification time (ctime, nsec resolution)
of the canonicalized path to determine if the cache entry should be
invalidated. The key is a hex representation of a u64 sea hasher output
— which is very unlikely to collide.
After an audit of the Python query caching logic, we determined that the
most likely cause of a collision in cache entries is that the
modification times of underlying interpreters are identical. This seems
pretty feasible, especially if the file system does not support
nanosecond precision — though it appears that the GitHub runners do
support it.
The fix here is to include the canonicalized path in the cache key,
which ensures we're looking at the modification time of the _same_
underlying interpreter.
This will "invalidate" all existing interpreter cache entries but that's
not a big deal.
This should also have the effect of reducing cache churn for
interpreters in virtual environments. Now, when you change Python
versions, we won't invalidate the previous cache entry so if you change
_back_ to the old version we can re-use our cached information.
It's a bit speculative, since we don't have a deterministic reproduction
in CI, but this is the strongest candidate given the logs and should
increase correctness regardless.
Closes https://github.com/astral-sh/uv/issues/14160
Closes https://github.com/astral-sh/uv/issues/13744
Closes https://github.com/astral-sh/uv/issues/13745
Once it's confirmed the flakes are resolved, we should revert
- https://github.com/astral-sh/uv/pull/14275
- #13817
## Summary
In #14245, we started normalizing index URLs by dropping the trailing
slash in the lockfile. We added tests to ensure that this didn't cause
existing lockfiles to be invalidated, but we missed one of the
constructors (specifically, the path that's used with
`tool.uv.sources`).
Motivated by some code duplication highlighted in
https://github.com/astral-sh/uv/pull/14201, I noticed we weren't taking
advantage of the existing implementation for casting to a str here.
Unfortunately, we do need a special case for CPython still.
Close#7426
## Summary
Picking up on #8284, I noticed that the `requires_python` object already
has its specifiers canonicalized in the `intersection` method, meaning
`~=3.12` is converted to `>=3.12, <4`. To fix this, we check and warn in
`intersection`.
## Test Plan
Used the same tests from #8284.
## Summary
When the user provides a requirement like `==2.4.*`, we desugar that to
`>=2.4.dev0,<2.5.dev0`. These bounds then appear in error messages, and
worse, they also trick the error message reporter into thinking that the
user asked for a pre-release.
This PR adds logic to convert to the more-concise `==2.4.*`
representation when possible. We could probably do a similar thing for
the compatible release operator (`~=`).
Closes https://github.com/astral-sh/uv/issues/14177.
Co-authored-by: Zanie Blue <contact@zanie.dev>
Python `bin` installations installed with `uv python install --default
--preview` (no version specified) were not being installed as
upgradeable. Instead each link was pointed at the highest patch version
for a minor version. This change ensures that these preview default
installations are also treated as upgradeable.
The PR includes some updates to the related tests. First, it checks the
default install without specified version case. Second, since it's
adding more read link checks, it creates a new `read_link` helper method
to consolidate repeated logic and replace instances of
`#[cfg(unix/windows)` with `if cfg!(unix/windows)`.
Fixes#14247
This PR updates `IndexUrl` parsing to normalize non-file URLs by
removing trailing slashes. It also normalizes registry source URLs when
using them to validate the lockfile.
Prior to this change, when writing an index URL to the lockfile, uv
would use a trailing slash if present in the provided URL and no
trailing slash otherwise. This can cause surprising behavior. For
example, `uv lock --locked` will fail when a package is added with an
`--index` value without a trailing slash and then `uv lock --locked` is
run with a `pyproject.toml` version of the index URL that contains a
trailing slash. This PR fixes this and adds a test for the scenario.
It might be safe to normalize file URLs in the same way, but since
slashes have a well-defined meaning in the context of files and
directories, I chose not to normalize them here.
Closes#13707.
uv currently ignores URL-encoded credentials in a redirect location.
This PR adds a check for these credentials to the redirect handling
logic. If found, they are moved to the Authorization header in the
redirect request.
Closes#11097
Previously we were using the XDG data dir to avoid symlinks, but there's no
particular guarantee that that's not going to be a symlink too. Using the
canonicalized temp dir by default is also slightly nicer for a couple reasons:
It's sometimes faster (an in-memory tempfs on e.g. Arch), and it makes
overriding `$TMPDIR` or `%TMP%` sufficient to control where tests put temp
files, without needing to override `UV_INTERNAL__TEST_DIR` too.
There was a regression introduced in #13954 on Windows where creating a
venv behaved as if there was a minor version link even if none existed.
This PR adds a check to fix this.
Closes#14249.
We do not currently support passing index names to `--index` for
installing packages. However, we do accept relative paths that can look
like index names. This PR adds the requirement that `--index` values
must be disambiguated with a prefix (`./` or `../` on Unix and Windows
or `.\\` or `..\\` on Windows). For now, if an ambiguous value is
provided, uv will warn that this will not be supported in the future.
Currently, if you provide an index name like `--index test` when there
is no `test` directory, uv will error with a `Directory not found...`
error. That's not very informative if you thought index names were
supported. The new warning makes the context clearer.
Closes#13921
In workspaces with multiple packages, you usually don't want to include
shared files such as the license repeatedly. Instead, we reading from
symlinked files. This would be supported if we had used std's `is_file`
and read methods, but walkdir's `is_file` does not consider symlinked
files as files.
See https://github.com/astral-sh/uv/issues/3957#issuecomment-2994675003
<!--
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
Update [schemars
0.9.0](https://github.com/GREsau/schemars/releases/tag/v0.9.0)
There are differences in the generated JSON Schema and I will [contact
the author](https://github.com/GREsau/schemars/issues/407).
## Test Plan
---------
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
This flakes often and we don't really need it to be monitored
continuously. We can always revive it from Git later.
Closes https://github.com/astral-sh/uv/issues/13952.
Don't log that we resolved a reference through the GitHub fast path if
we didn't use GitHub at all but used the cached revision. This avoids
stating that the fast path works when it's blocked due to unrelated
reasons (e.g. rate limits).
@oconnor663 discovered that executing `3.10.8` on Arch Linux ran into an
error loading `libcrypt.so.1`. This caused uv to install the latest
patch version on `uv venv` operations during upgrade tests, which
undermined their purpose (since they are checking that if you first
install `3.10.8` and then upgrade, virtual environments are
transparently upgraded). This PR updates the test to use `3.10.17`
instead to avoid this issue.
#13954 introduced an unnecessary slow-down to Python uninstall by
calling `installations.find_all()` to discover remaining installations
after an uninstall. Instead, we can filter all initial installations
against those in `uninstalled`.
As part of this change, I've updated `uninstalled` from a `Vec` to an
`IndexSet` in order to do efficient lookups in the filter. This required
a change I call out below to how we were retrieving them for messaging.
We were checking whether a path was an executable in a virtual
environment or the base directory of a virtual environment in multiple
places in the codebase. This PR consolidates this logic into one place.
Closes#13947.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| [mimalloc](https://redirect.github.com/purpleprotocol/mimalloc_rust) |
dependencies | patch | `0.1.46` -> `0.1.47` |
---
> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.
---
### Release Notes
<details>
<summary>purpleprotocol/mimalloc_rust (mimalloc)</summary>
###
[`v0.1.47`](https://redirect.github.com/purpleprotocol/mimalloc_rust/releases/tag/v0.1.47):
Version 0.1.47
[Compare
Source](https://redirect.github.com/purpleprotocol/mimalloc_rust/compare/v0.1.46...v0.1.47)
##### Changes
- Mimalloc `v2.2.4`
</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:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
## Summary
Allows `--torch-backend=auto` to detect AMD GPUs. The approach is fairly
well-documented inline, but I opted for `rocm_agent_enumerator` over
(e.g.) `rocminfo` since it seems to be the recommended approach for
scripting:
https://rocm.docs.amd.com/projects/rocminfo/en/latest/how-to/use-rocm-agent-enumerator.html.
Closes https://github.com/astral-sh/uv/issues/14086.
## Test Plan
```
root@rocm-jupyter-gpu-mi300x1-192gb-devcloud-atl1:~# ./uv-linux-libc-11fb582c5c046bae09766ceddd276dcc5bb41218/uv pip install torch --torch-backend=auto
Resolved 11 packages in 251ms
Prepared 2 packages in 6ms
Installed 11 packages in 257ms
+ filelock==3.18.0
+ fsspec==2025.5.1
+ jinja2==3.1.6
+ markupsafe==3.0.2
+ mpmath==1.3.0
+ networkx==3.5
+ pytorch-triton-rocm==3.3.1
+ setuptools==80.9.0
+ sympy==1.14.0
+ torch==2.7.1+rocm6.3
+ typing-extensions==4.14.0
```
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
I was looking into `uv tool` not supporting version files, and noticed
this implementation was confusing and skipped handling like a tracing
log if `--no-config` excludes selection a file. I've refactored it in
preparation for the next change.
> NOTE: The PRs that were merged into this feature branch have all been
independently reviewed. But it's also useful to see all of the changes
in their final form. I've added comments to significant changes
throughout the PR to aid discussion.
This PR introduces transparent Python version upgrades to uv, allowing
for a smoother experience when upgrading to new patch versions.
Previously, upgrading Python patch versions required manual updates to
each virtual environment. Now, virtual environments can transparently
upgrade to newer patch versions.
Due to significant changes in how uv installs and executes managed
Python executables, this functionality is initially available behind a
`--preview` flag. Once an installation has been made upgradeable through
`--preview`, subsequent operations (like `uv venv -p 3.10` or patch
upgrades) will work without requiring the flag again. This is
accomplished by checking for the existence of a minor version symlink
directory (or junction on Windows).
### Features
* New `uv python upgrade` command to upgrade installed Python versions
to the latest available patch release:
```
# Upgrade specific minor version
uv python upgrade 3.12 --preview
# Upgrade all installed minor versions
uv python upgrade --preview
```
* Transparent upgrades also occur when installing newer patch versions:
```
uv python install 3.10.8 --preview
# Automatically upgrades existing 3.10 environments
uv python install 3.10.18
```
* Support for transparently upgradeable Python `bin` installations via
`--preview` flag
```
uv python install 3.13 --preview
# Automatically upgrades the `bin` installation if there is a newer patch version available
uv python upgrade 3.13 --preview
```
* Virtual environments can still be tied to a patch version if desired
(ignoring patch upgrades):
```
uv venv -p 3.10.8
```
### Implementation
Transparent upgrades are implemented using:
* Minor version symlink directories (Unix) or junctions (Windows)
* On Windows, trampolines simulate paths with junctions
* Symlink directory naming follows Python build standalone format: e.g.,
`cpython-3.10-macos-aarch64-none`
* Upgrades are scoped to the minor version key (as represented in the
naming format: implementation-minor version+variant-os-arch-libc)
* If the context does not provide a patch version request and the
interpreter is from a managed CPython installation, the `Interpreter`
used by `uv python run` will use the full symlink directory executable
path when available, enabling transparently upgradeable environments
created with the `venv` module (`uv run python -m venv`)
New types:
* `PythonMinorVersionLink`: in a sense, the core type for this PR, this
is a representation of a minor version symlink directory (or junction on
Windows) that points to the highest installed managed CPython patch
version for a minor version key.
* `PythonInstallationMinorVersionKey`: provides a view into a
`PythonInstallationKey` that excludes the patch and prerelease. This is
used for grouping installations by minor version key (e.g., to find the
highest available patch installation for that minor version key) and for
minor version directory naming.
### Compatibility
* Supports virtual environments created with:
* `uv venv`
* `uv run python -m venv` (using managed Python that was installed or
upgraded with `--preview`)
* Virtual environments created within these environments
* Existing virtual environments from before these changes continue to
work but aren't transparently upgradeable without being recreated
* Supports both standard Python (`python3.10`) and freethreaded Python
(`python3.10t`)
* Support for transparently upgrades is currently only available for
managed CPython installations
Closes#7287Closes#7325Closes#7892Closes#9031Closes#12977
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
This PR is a combination of #12920 and #13754. Prior to these changes,
following a redirect when searching indexes would bypass our
authentication middleware. This PR updates uv to support propagating
credentials through our middleware on same-origin redirects and to
support netrc credentials for both same- and cross-origin redirects. It
does not handle the case described in #11097 where the redirect location
itself includes credentials (e.g.,
`https://user:pass@redirect-location.com`). That will be addressed in
follow-up work.
This includes unit tests for the new redirect logic and integration
tests for credential propagation. The automated external registries test
is also passing for AWS CodeArtifact, Azure Artifacts, GCP Artifact
Registry, JFrog Artifactory, GitLab, Cloudsmith, and Gemfury.
Close#13922
## Summary
Add a warning if the directory given by the `--index` argument is empty.
## Test Plan
Added test case `add_index_empty_directory` in `edit.rs`
When working on support for reading global Python pins in tool
operations, I noticed that we weren't using the canonicalized Python
request in receipts — we were using the raw string provided by the user.
Since we'll need to compare these values, we should be using the
canonicalized string.
The `Tool` and `ToolReceipt` types have been updated to hold a
`PythonRequest` instead of a `String`, and `Serialize` was implemented
for `PythonRequest` so canonicalization can happen at the edge instead
of being the caller's responsibility.
Fix `uv run -p 3.7` by not using a walrus operator. Python 3.7 isn't
really supported anymore, but there's no reason to break interpreter
discovery for it.
When using `uv lock --upgrade-package=python` after changing
`requires-python`, it was possible to get into a state where the fork
markers produced corresponded to the empty set. This in turn resulted in
an empty lock file.
There was already some infrastructure in place that I think was perhaps
intended to handle this. In particular, `Lock::check_marker_coverage`
checks whether the fork markers have some overlap with the supported
environments (including the `requires-python`). But there were two
problems with this.
First is that in lock validation, this marker coverage check came
_after_ a path that returned `Preferable` (meaning that the fork markers
should be kept) when `--upgrade-package` was used. Second is that the
marker coverage check used the `requires-python` in the lock file and
_not_ the `requires-python` in the now updated `pyproject.toml`.
We attempt to solve this conundrum by slightly re-arranging lock file
validation and by explicitly checking whether the *new*
`requires-python` is disjoint from the fork markers in the lock file. If
it is, then we return `Versions` from lock file validation (indicating
that the fork markers should be dropped).
Fixes#13951
We always ignore the `clippy::struct_excessive_bools` rule and formerly
annotated this at the function level. This PR specifies the allow in
`workspace.lints.clippy` in `Cargo.toml`.
Using a companion change in the middleware
(https://github.com/TrueLayer/reqwest-middleware/pull/235, forked&tagged
pending review), we can check and show retries for HTTP status core
errors, to consistently report retries again.
We fix two cases:
* Show retries for status code errors for cache client requests
* Show retries for status code errors for Python download requests
Not handled:
* Show previous retries when a distribution download fails mid-streaming
* Perform retries when a distribution download fails mid-streaming
* Show previous retries when a Python download fails mid-streaming
* Perform retries when a Python download fails mid-streaming
(or legacy tool.uv.workspace).
This cleaves out a dedicated SourcedDependencyGroups type based on
RequiresDist but with only the DependencyGroup handling implemented.
This allows `uv pip` to read `dependency-groups` from pyproject.tomls
that only have that table defined, per PEP 735, and as implemented by
`pip`.
However we want our implementation to respect various uv features when
they're available:
* `tool.uv.sources`
* `tool.uv.index`
* `tool.uv.dependency-groups.mygroup.requires-python` (#13735)
As such we want to opportunistically detect "as much as possible" while
doing as little as possible when things are missing. The issue with the
old RequiresDist path was that it fundamentally wanted to build the
package, and if `[project]` was missing it would try to desperately run
setuptools on the pyproject.toml to try to find metadata and make a hash
of things.
At the same time, the old code also put in a lot of effort to try to
pretend that `uv pip` dependency-groups worked like `uv`
dependency-groups with defaults and non-only semantics, only to separate
them back out again. By explicitly separating them out, we confidently
get the expected behaviour.
Note that dependency-group support is still included in RequiresDist, as
some `uv` paths still use it. It's unclear to me if those paths want
this same treatment -- for now I conclude no.
Fixes#13138
This allows you to specify requires-python on individual dependency-groups,
with the intended usecase being "oh my dev-dependencies have a higher
requires-python than my actual project".
This includes a large driveby move of the RequiresPython type to
uv-distribution-types to allow us to generate the appropriate markers at
this point in the code. It also migrates RequiresPython from
pubgrub::Range to version_ranges::Ranges, and makes several pub(crate)
items pub, as it's no longer defined in uv_resolver.
Fixes#11606
In the case where we have platform information in a Python request, we
should filter managed Python distributions by it prior to querying them.
Closes https://github.com/astral-sh/uv/issues/13935
---------
Co-authored-by: Aria Desires <aria.desires@gmail.com>
Often, HTTP requests don't fail due to server errors, but from spurious
network errors such as connection resets. reqwest surfaces these as
`io::Error`, and we have to handle their retrying separately.
Companion PR: https://github.com/LukeMathWalker/wiremock-rs/pull/159
Unlike regular packages, specifying all `__init__.py` directories for a
namespace package would be very verbose There is e.g.
https://github.com/python-poetry/poetry/tree/main/src/poetry, which has
18 modules, or https://github.com/googleapis/api-common-protos which is
inconsistently nested. For both the Google Cloud SDK, there are both
packages with a single module and those with complex structures, with
many having multiple modules due to versioning through `<module>_v1`
versioning. The Azure SDK seems to use one module per package (it's not
explicitly documented but seems to follow from the process in
https://azure.github.io/azure-sdk/python_design.html#azure-sdk-distribution-packages
and
ccb0e03a3d/doc/dev/packaging.md).
For simplicity with complex projects, we add a `namespace = true` switch
which disabled checking for an `__init__.py`. We only check that there's
no `<module_root>/<module_name>/__init__.py` and otherwise add the whole
`<module_root>/<module_name>` folder. This comes at the cost of
`namespace = true` effectively creating an opt-out from our usual checks
that allows creating an almost entirely arbitrary package.
For simple projects with only a single module, the module name can be
dotted to point to the target module, so the build still gets checked:
```toml
[tool.uv.build-backend]
module-name = "poetry.core"
```
## Alternatives
### Declare all packages
We could make `module-name` a list and allow or require declaring all
packages:
```toml
[tool.uv.build-backend]
module-name = ["cloud_sdk.service.storage", "cloud_sdk.service.storage_v1", "cloud_sdk.billing.storage"]
```
Or for Poetry:
```toml
[tool.uv.build-backend]
module-name = [
"poetry.config",
"poetry.console",
"poetry.inspection",
"poetry.installation",
"poetry.json",
"poetry.layouts",
"poetry.masonry",
"poetry.mixology",
"poetry.packages",
"poetry.plugins",
"poetry.publishing",
"poetry.puzzle",
"poetry.pyproject",
"poetry.repositories",
"poetry.toml",
"poetry.utils",
"poetry.vcs",
"poetry.version"
]
```
### Support multiple namespaces
We could also allow namespace packages with multiple root level module:
```toml
[tool.uv.build-backend]
module-name = ["cloud_sdk.my_ext", "local_sdk.my_ext"]
```
For lack of use cases, we delegate this to creating a workspace with one
package per module.
## Implementation
Due to the more complex options for the module name, I'm moving
verification on deserialization later, dropping the source span we'd get
from serde. We also don't show similarly named directories anymore.
---------
Co-authored-by: Andrew Gallant <andrew@astral.sh>
Investigating #13744
I tried reproducing here by running the test in a loop, but could not. I
presume it's an interaction with other tests.
This drops the snapshot, but I think it's worth it to try to examine the
flake?
Use TTY detection to determine when we should forward SIGINT instead of
counting signals, which can lead to various problems where multiple
SIGINTs are sent to a child after the first signal. Counting does not
make sense in interactive situations that do not exit on interrupt,
e.g., the Python REPL.
Closes https://github.com/astral-sh/uv/issues/13919
Closes https://github.com/astral-sh/uv/issues/12108
As another follow-up in the vein of
https://github.com/astral-sh/uv/pull/13944, I noticed `uv python pin`
doesn't download Python versions, which is a bit weird because we'll
warn it's not found.
See https://github.com/astral-sh/uv/issues/13935#issuecomment-2957300516
where we fail to write a pin file because we encounter an unusable
interpreter. This is actually a special case where `MissingPython` is
not raised because we want to show why we failed to find a usable
interpreter, which is useful in commands where you _need_ an interpreter
to use, but here we don't actually need it. Here, we just log the
failure and move on.
Related https://github.com/astral-sh/uv/pull/13936
Add basic tests for error messages on retryable network errors.
This test mod is intended to grow to ensure that we handle retryable
errors correctly and that we show the appropriate error message if we
failed after retrying.
The starter tests show some common cases we've seen download errors in:
simple and find links indexes, file downloads and Python installs.
For `io::Error` fault injection to test the reqwest `Err` path besides
the HTTP status code `Ok` path, see
https://github.com/LukeMathWalker/wiremock-rs/issues/149.
As per #13874, passing a relative URL like `test` to `--index` for `uv
add` causes unexpected behavior if the directory does not exist. The
non-existent index is effectively ignored and uv falls back to PyPI. If
a package is found there, the spurious index is then written to
`pyproject.toml`. This doesn't happen for `--default-index` since
resolution will fail without fallback to PyPI.
This PR adds a validation step for indexes provided on the command line.
If a directory does not exist, uv will fail with an error.
Closes#13874
For the case where there was no matching wheel on sync, we previously
added a note about which wheels are available vs. on which platform you
are on. We extend this error message to link directly towards
`tool.uv.required-environments`, which otherwise has a discovery
problem.
On Linux (Setting `tool.uv.required-environments` doesn't help here
either, but it's a clear example):
```
[project]
name = "debug"
version = "0.1.0"
requires-python = "==3.10.*"
dependencies = ["tensorflow-macos>=2.13.1"]
```
```
Resolved 41 packages in 24ms
error: Distribution `tensorflow-macos==2.16.2 @ registry+https://pypi.org/simple` can't be installed because it doesn't have a source distribution or wheel for the current platform
hint: You're on Linux (`manylinux_2_39_x86_64`), but there are no wheels for the current platform, consider configuring `tool.uv.required-environments`.
hint: `tensorflow-macos` (v2.16.2) only has wheels for the following platform: `macosx_12_0_arm64`.
```

---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
Users are not (yet) properly familiar with the concept of universal
resolution and its implication that we need to resolve for all possible
platforms and Python versions. Some projects only target a specific
platform or Python version, and users experience resolution errors due
to failures for other platforms. Indicated by the number of questions we
get about it, `tool.uv.environments` for restricting environments is not
well discoverable.
We add a special hint when resolution failed on a fork disjoint with the
current environment, hinting the user to constrain `requires-python` and
`tool.uv.environments` respectively.
The hint has false positives for cases where the resolution failed on a
different platform, but equally fails on the current platform, in cases
where the non-current fork was tried earlier. Given that conflicts can
be based on `requires-python`, afaik we can't parse whether the current
platform would also be affected from the derivation tree.
Two cases not covered by this are build errors as well as install errors
that need `tool.uv.required-environments`.
- Define all list elements using `-`: it used to be a mix of `*` and
`-`. `-` is what Prettier linter formats it to by default.
- Removed unnecessary blank line between 2 list elements. Other elements
were stitched together without blank lines in between.
- Only the first list element started in sentence case (capital letter
first) - I made all start like so.
Surprisingly, we weren't locking during `uv sync` so far, so running `uv
sync` in parallel could cause errors in filesystem races.
I've also added locks to `uv add` and `uv remove` which concurrently
modify `pyproject.toml`. These locks only apply after we determined the
interpreter, so they don't actually prevent computing the same thing
twice when running `uv add` in parallel.
All other subcommands that I checked were already locking (with no claim
to exhaustiveness)
Fixes#12751
# Test Plan
I don't have CI-sized reproducer for this.
```toml
[project]
name = "debug"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"boto3>=1.38.30",
"fastapi>=0.115.12",
"numba>=0.61.2",
"polars>=1.30.0",
"protobuf>=6.31.1",
"pyarrow>=20.0.0",
"pydantic>=2.11.5",
"requests>=2.32.3",
"urllib3>=2.4.0",
"scikit-learn>=1.6.1",
"jupyter>=1.1.1",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
```
```
rm -rf .venv && parallel -n0 "uv sync -q" ::: {1..10}
```
## Summary
I think `GlobDirFilter::match_directory` should return `true` if it
failed to construct a DFA
to force a full directory traversal. Returning `false` means that the
build backend excludes all files which
is unlikely what we want in that situation.
## Summary
When trying out `uv export --no-editable --format pylock.toml` the
exported contents would still retain `editable = true` regardless.
## Test Plan
Added additional test. Tested locally on few projects where I was
previously using `uv export --no-editable --format requirements.txt` to
ensure the output aligns.
Extends https://github.com/astral-sh/uv/pull/13841 — I'll drop that
commit later after that pull request merges but it's small.
I find the split into a "Configuration" section awkward and don't think
it's helping us. Everything moved into the "Concepts" section, except
the "Environment variables" page which definitely belongs in the
reference and the "Installer" page which is fairly niche and seems
better in the reference.
Before / After
<img
src="https://github.com/user-attachments/assets/80d8304b-17da-4900-a5f4-c3ccac96fcc5"
width="400">
## Summary
Switch the `sync_dry_run` test to use Python 3.9 instead of Python 3.8.
I didn't previously catch it since it was marked as `python_managed`.
## Test Plan
`cargo test --no-default-features --features git,pypi,python` without
Python 3.8 installed.
## Summary
Modify `requirements_txt_ssh_git_username` test to pass `-o
UserKnownHostsFile=/dev/null` to OpenSSH, in order to prevent it from
trying to write into the known-hosts in user's home directory. To add
insult to the injury, OpenSSH ignores `HOME` and writes into the actual
home when it is explicitly overridden for the purpose of testing.
## Test Plan
`cargo test --no-default-features --features=git,pypi,python` in an
environment where the user can't write to `pw_home` (but can to
`${HOME}`). Previously the test would fail:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Snapshot Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Snapshot: requirements_txt_ssh_git_username
Source: crates/uv/tests/it/export.rs:1252
────────────────────────────────────────────────────────────────────────────────
Expression: snapshot
────────────────────────────────────────────────────────────────────────────────
-old snapshot
+new results
────────────┬───────────────────────────────────────────────────────────────────
7 7 │ ├─▶ failed to clone into: [PATH]
8 8 │ ├─▶ failed to fetch branch, tag, or commit `d780faf0ac91257d4d5a4f0c5a0e4509608c0071`
9 9 │ ╰─▶ process didn't exit successfully: [GIT_COMMAND_ERROR]
10 10 │ --- stderr
11 │+ Could not create directory '/var/lib/portage/home/.ssh' (Permission denied).
12 │+ Failed to add the host to the list of known hosts (/var/lib/portage/home/.ssh/known_hosts).
11 13 │ Load key "[TEMP_DIR]/fake_deploy_key": [ERROR]
12 14 │ git@github.com: Permission denied (publickey).
13 15 │ fatal: Could not read from remote repository.
14 16 │
────────────┴───────────────────────────────────────────────────────────────────
```
---------
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
When missing an operator for version parsing, it would give an error
that was hard to know how to fix if you were not familiar with version
specifiers / PEP-440:
```
Unexpected end of version specifier, expected operator
```
Now, it will attempt to provide a more useful hint if it can parse the
version from the remaining scanner:
```
Unexpected end of version specifier, expected operator (did you mean "==3.12"?)
```
## Test Plan
Unit tests in `version_specifier.rs` were added/updated for the
following cases:
- `test_parse_specifier_missing_operator_error`
- `test_parse_specifier_missing_operator_invalid_version_error`
- `test_invalid_word`
- `test_invalid_specifier`
- `error_message_version_specifiers_parse_error`
A test in `edit.rs` for failing to parse the `pyproject.toml` when using
`add` was also included to match the request in the original issue:
- `add_invalid_requires_python`
I didn't add cases where no version specifier is provided because it
seemed like it doesn't get to the point of parsing in that case, so it
should not happen.
## Reference
Fixes#13126
---------
Co-authored-by: Jacob Woliver <jacob@jmw.sh>
Co-authored-by: konstin <konstin@mailbox.org>
This includes some initial work on adding Pyodide support (issue
#12729). It is enough to get
```
uv pip compile -p /path/to/pyodide --extra-index-url file:/path/to/simple-index
```
to work which should already be quite useful.
## Test Plan
* added a unit test for `pyodide_platform`
* integration tested manually with:
```
cargo run pip install \
-p /home/rchatham/Documents/programming/tmp/pyodide-venv-test/.pyodide-xbuildenv-0.29.3/0.27.4/xbuildenv/pyodide-root/dist/python \
--extra-index-url file:/home/rchatham/Documents/programming/tmp/pyodide-venv-test/.pyodide-xbuildenv-0.29.3/0.27.4/xbuildenv/pyodide-root/package_index \
--index-strategy unsafe-best-match --target blah --no-build \
numpy pydantic
```
---------
Co-authored-by: konsti <konstin@mailbox.org>
Co-authored-by: Zanie Blue <contact@zanie.dev>
This updates the `Debug` implementation of `DisplaySafeUrl` to display
the git username in a case like `git+ssh://git@github.com/org/repo`. It
also factors out the git username check into a shared function and adds
related unit tests to `DisplaySafeUrl`.
`trace!`-log the locations of data directories used by a wheel. These
paths are queried from sysconfig, i.e. they are mostly Python
interpreter defined instead of being computed by uv.
Fixes#13795 specifically by not redacting the username convention `git`
for SSH URLs, though we should never write stars in `uv export`
generally (#13791).
We've been using a number of different winapi crates. This PR removes
winsafe in favor of the official windows-* crates, so all of uv's own
winapi calls go through the official windows-* crates.
---------
Co-authored-by: Aria Desires <aria.desires@gmail.com>
## Summary
Implemented as suggested in #13761
eg.
```
$ uv tool install 'harlequin[postgres]'
$ uv tool list --show-extras
harlequin v2.1.2 [extras: postgres]
- harlequin
```
## Test Plan
Added a new test with the argument along with the others from the `uv
tool list` cli.
## Summary
Update `lock::lock_requires_python` not to require Python 3.8.
Fixes#13676
## Test Plan
`cargo test --no-default-features --features git,pypi,python` after
removing Python 3.8.
<!--
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? -->
Use the existing `pypi` feature to conditionalize a number of tests that
attempt to access https://pypi.org and/or
https://files.pythonhosted.org. See
https://github.com/astral-sh/uv/issues/8970#issuecomment-2466794088.
There is no reason to believe that these are *all* of the tests that
need to be conditionalized on the `pypi` feature, but this should be a
solid step in the right direction.
## Test Plan
<!-- How was it tested? -->
This allows me to build and run the integration tests in [Fedora’s `uv`
package](https://src.fedoraproject.org/rpms/uv) without having to
manually skip tests that try to access PyPI. I confirmed that this
appears to accomplish that goal.
Otherwise, this should be tested by building and running the tests as
usual. As mentioned in
https://github.com/astral-sh/uv/issues/8970#issuecomment-2516181501, a
more complete solution would include CI tests that confirm these
features are working as intended. I’m not in a position to offer that.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| [tempfile](https://stebalien.com/projects/tempfile-rs/)
([source](https://redirect.github.com/Stebalien/tempfile)) |
workspace.dependencies | minor | `3.19.1` -> `3.20.0` |
---
> [!WARNING]
> Some dependencies could not be looked up. Check the Dependency
Dashboard for more information.
---
### Release Notes
<details>
<summary>Stebalien/tempfile (tempfile)</summary>
###
[`v3.20.0`](https://redirect.github.com/Stebalien/tempfile/blob/HEAD/CHANGELOG.md#3200)
[Compare
Source](https://redirect.github.com/Stebalien/tempfile/compare/v3.19.1...v3.20.0)
This release mostly unifies the behavior/capabilities around "keeping"
temporary files:
- Rename `Builder::keep(bool)` (via deprecation) to
`Builder::disable_cleanup(bool)` to make it clear that behaves
differently from `NamedTempFile::keep()`. The former disables automatic
cleanup while the latter *consumes* the `NamedTempFile` object entirely
and unsets the "temporary file" attribute (on Windows).
- Rename `TempDir::into_path` (via deprecation) to `TempDir::keep` to
mirror `NamedTempFile::keep`.
- Add `TempDir::disable_cleanup`, `NamedTempFile::disable_cleanup`, and
`TempPath::disable_cleanup` making it possible to disable automatic
cleanup in-place *after* creating a temporary file/directory (equivalent
to calling `Builder::disable_cleanup` before creating the
file/directory).
Additionally, it adds a few spooled temporary file features:
- Add `SpooledTempFile::into_file` for turning a `SpooledTempFile` into
a regular unnamed temporary file, writing it to the backing storage
("rolling" it) if it was still stored in-memory.
- Add `spooled_tempfile_in` and `SpooledTempFile::new_in` methods for
creating spooled temporary files in a specific directory. This makes it
possible to choose the backing device for your spooled temporary file
which is rather important on Linux where the default temporary directory
is likely backed by memory (defeating the entire point of having a
spooled temporary file).
Finally, this release improves documentation, especially the top-level
documentation explaining which temporary file type to use.
**BREAKING** for those with `deny(warnings)`:
- `Builder::keep` deprecated in favor of `Builder::disable_cleanup`.
- `TempDir::into_path` is deprecated in favor of `TempDir::keep`.
**BREAKING**:
</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:eyJjcmVhdGVkSW5WZXIiOiI0MC4zMy42IiwidXBkYXRlZEluVmVyIjoiNDAuMzMuNiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW50ZXJuYWwiXX0=-->
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
Update a few more tests to avoid using Python 3.8 unnecessarily. From
what I can see, neither of these really need that specific Python
version, so just update them to use 3.9 instead.
Bug #13676
## Test Plan
Uninstall Python 3.8 and run: `cargo test --no-default-features
--features git,pypi,python`
One test failure remains.
Previously if you wanted to run e.g. PyPy via `uvx`, you had to spell it
like `uvx -p pypy python`. Now we reuse some of the
`PythonRequest::parse` machinery to handle the executable, so all of the
following examples work:
- `uvx python3.8`
- `uvx 'python>3.7,<3.9'`
- `uvx --from python3.8 python` (or e.g. `bash`)
- `uvx pypy38`
- `uvx graalpy@38`
The `python` (and on Windows only, `pythonw`) special cases are
retained, which normally aren't allowed values of `-p`/`--python`.
Closes https://github.com/astral-sh/uv/issues/13536.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
Co-authored-by: konsti <konstin@mailbox.org>
Replacement for https://github.com/astral-sh/uv/pull/13474 (clobbered by
a changed base)
Once these are explicitly installed, they should be discoverable and
usable. Currently, they are not.
In https://github.com/astral-sh/uv/pull/13721#issuecomment-2920530601 I
presumed that all the installation problems in
https://github.com/astral-sh/uv/pull/13722 were solved by
https://github.com/astral-sh/uv/pull/13709 but they were not because we
don't differentiate between implicit and explicit architecture requests
so a request for `aarch64` is considered satisfied by an existing
`x86-64` installation even if the user explicitly requested that
architecture.
Now, we track if it was explicit or implicit, requiring an exact match
in the former case, and a `supports` in the latter.
We considered doing this for other items in the request, like the
operating system but we don't have a `supports()` concept there. It
might make sense for libc in the future.
Resolves
https://github.com/astral-sh/uv/pull/13474#discussion_r2112586405
This kind of dynamic ordering freaks me out a little, but I think it's
probably the best solution and is static at compile-time.
Currently, we're just sorting by the stringified representation! which
is just convenient for reproducibility, but we rely on these orderings
for prioritization in discovery.
Previously you could get a confusing error message like this:
```
$ docker run ghcr.io/astral-sh/uv run python
error: Could not read ELF interpreter from any of the following paths: /bin/sh, /usr/bin/env, /bin/dash, /bin/ls
```
Now the error message is better:
```
error: Failed to discover managed Python installations
Caused by: Failed to determine the libc used on the current platform
Caused by: Failed to find any common binaries to determine libc from: /bin/sh, /usr/bin/env, /bin/dash, /bin/ls
```
See https://github.com/astral-sh/uv/issues/8635.
---------
Co-authored-by: konsti <konstin@mailbox.org>
Co-authored-by: Zanie Blue <contact@zanie.dev>
<!--
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
Related to https://github.com/astral-sh/uv/issues/6801.
Currently on Windows, uv itself will always creates a console window,
even though the window could be empty if `uv run --gui-script` is used.
This is due to it using the [default `console` window
subsystem](https://rust-lang.github.io/rfcs/1665-windows-subsystem.html).
This PR introduces a wrapper `uvw` that, similar to the existing `uvx`,
invokes `uv` with the
[`CREATE_NO_WINDOW`](https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags#:~:text=CREATE_NO_WINDOW)
process creation flag on Windows, which creates child process without
console window.
Note that this PR does not alter any behaviors regarding `run --script`
and `run --gui-script`.
## Test Plan
Built and tested locally by doing something like `uvw run test.py`.
Currently, it is not possible to set a custom Python downloads JSON on
Windows, as Windows paths can be valid URLs.
```rust
use url::Url;
fn main() {
dbg!(Url::parse(r"C:\Users\Ferris\download.json"));
}
```
Tested by https://github.com/astral-sh/uv/pull/13585 (where it is
currently failing CI).
By default, uv uses only a lower bound in `uv add`, which avoids
dependency conflicts due to upper bounds. With this PR, this cna be
changed by setting a different bound kind. The bound kind can be
configured in `uv.toml`, as a user preference, in `pyproject.toml`, as a
project preference, or on the CLI, when adding a specific project.
We add two options that add an upper bound on the constraint, one for
SemVer (`>=1.2.3,<2.0.0`, dubbed "major", modeled after the SemVer
caret) and another one for dependencies that make breaking changes in
minor version (`>=1.2.3,<1.3.0`, dubbed "minor", modeled after the
SemVer tilde). Intuitively, the major option bumps the most significant
version component, while the minor option bumps the second most
significant version component. There is also an exact bounds option
(`==1.2.3`), though generally we recommend setting a wider bound and
using the lockfile for pinning.
Versions can have leading zeroes, such as `0.1` or `0.0.1`. For a single
leading 0, we shift the the meaning of major and minor similar to cargo.
For two or more leading zeroes, the difference between major and minor
becomes inapplicable, instead both bump the most significant component:
- major: `0.1` -> `>=0.1,<0.2`
- major: `0.0.1` -> `>=0.0.1,<0.0.2`
- major: `0.0.1.1` -> `>=0.0.1.1,<0.0.2.0`
- major: `0.0.0.1` -> `>=0.0.0.1,<0.0.0.2`
- minor: `0.1` -> `>=0.1,<0.1.1`
- minor: `0.0.1` -> `>=0.0.1,<0.0.2`
- minor: `0.0.1.1` -> `>=0.0.1.1,<0.0.2.0`
- minor: `0.0.0.1` -> `>=0.0.0.1,<0.0.0.2`
For a consistent appearance, we try to preserve the number of components
in the upper bound. For example, adding a version `2.17` with the major
option is stored as `>=2.17,<3.0`. If a version uses three components
and is greater than 0, both bounds will also use three components
(SemVer versions always have three components). Of the top 100 PyPI
packages, 8 use a non-three-component version (docutils, idna, pycparser
and soupsieve with two components, packaging, pytz and tzdata with two
component, CalVer and trove-classifiers with four component CalVer).
Example `pyproject.toml` files with the top 100 packages: [`--bounds
major`](https://gist.github.com/konstin/0aaffa9ea53c4834c22759e8865409f4)
and [`--bounds
minor`](https://gist.github.com/konstin/e77f5e990a7efe8a3c8a97c5c5b76964).
While many projects follow version scheme that roughly or directly
matches the major or minor options, these compatibility ranges are
usually not applicable for the also popular CalVer versioning.
For pre-release versions, there are two framings we could take: One is
that pre-releases generally make no guarantees about compatibility
between them and are used to introduce breaking changes, so we should
pin them exactly. In many cases however, pre-release specifiers are used
because a project needs a bugfix or a feature that hasn't made it into a
stable release, or because a project is compatible with the next version
before a final version for that release is published. In those cases,
compatibility with other packages that depend on the same library is
more important, so the desired bound is the same as it would be for the
stable release, except with the lower bound lowered to include
pre-release.
The names of the bounds and the name of the flag is up for bikeshedding.
Currently, the option is call `tool.uv.bounds`, but we could also move
it under `tool.uv.edit.bounds`, where it would be the first/only entry.
Fixes#6783
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
This PR makes a few performance improvements:
1. Reduces the need to unpack and repack a `_GLibCVersion` tuple
2. Reduces the doubled call to `_is_compatible(arch, glibc_version)`
3. Moves the assignment of the `tag` variable directly into the yield,
reducing memory allocation in case this is never used when
`_is_compatible(arch, glibc_version)` is false
4. Combines the check of the `glibc_version` being in
`_LEGACY_MANYLINUX_MAP` and the assignment to the variable together. I'm
not sure if this is actually better, but using the assignment expression
reduces this from 4 lines to 2
## Test Plan
I upstreamed these changes in
https://github.com/pypa/packaging/pull/869. Otherwise, I'm pretty
confident this is a minor change that works the same
By default, Rust does not support safe cast from `&U` to `&T` for
`#[repr(transparent)] T(U)` even if the newtype opts in. The dtolnay
ref-cast crate fills this gap, allowing to remove `DisplaySafeUrlRef`.
This PR implements a few review follow-ups from #13560. In particular,
it
* Makes `DisplaySafeUrlRef` implement `Copy` so that it can be passed by
value.
* Updates `to_string_with_credentials` methods with
`displayable_with_credentials`, returning an `impl Display` instead of
`String` for greater flexibility.
* Updates the `DisplaySafeUrl` and `DisplaySafeUrlRef` `Debug`
implementations to match the underlying `Url` `Debug` implementation
(with the exception that credentials are masked).
* Replaces an unnecessary `DisplaySafeUrl::from(Url::from_file_path`
with `DisplaySafeUrl::from_file_path`
See https://github.com/astral-sh/uv/issues/13676
Python 3.8 is EOL, and we should avoid using it for tests unless we're
covering 3.8-specific behaviors.
In some cases, the Python version we require for the test is arbitrary,
for which I've added a new constant (set to 3.12). In other cases, we
require an older Python version for compatibility with various packages,
like `setuptools`. For those, we can _probably_ switch to a newer
version but it'd be more invasive as we'd need to change our constraints
or `exclude-newer`.
Adds a feature flag for cases where we still need to use the EOL
version.
There's a lot of usage in packse scenarios too, I'll update those
separately because the diff will be large.
<!--
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? -->
This change allows for `uv python install --reinstall` to include
pre-releases when reinstalling. It does this by explicitly allowing
pre-releases to be included within `PythonRequest::Any` if the user does
not specify a version to reinstall.
Fixes#13582
## Test Plan
<!-- How was it tested? -->
```bash
uv python install 3.14 3.13 3.10
uv python install --no-config --reinstall
```
## Summary
Right now, if a workspace member is first created by way of being a dev
dependency on another member, we end up duplicating it in the graph.
Instead, we should create all the roots upfront; all subsequent node
creations are robust to existing nodes.
Closes
https://github.com/astral-sh/uv/issues/13673#issuecomment-2912196406.
## Summary
Resolves https://github.com/astral-sh/uv/issues/13580.
`uv self update --offline` should fail and exit early because
self-updating requires network connection.
## Test Plan
A snapshot test is added.
---------
Co-authored-by: Aria Desires <aria.desires@gmail.com>
Co-authored-by: konsti <konstin@mailbox.org>
## Summary
This should reduce the number of filesystem operations fairly
dramatically:
- Only query actual symlinks.
- Don't recurse into package bodies (huge).
- Only traverse once (rather than twice).
Closes https://github.com/astral-sh/uv/issues/13667.
Prior to this PR, there were numerous places where uv would leak
credentials in logs. We had a way to mask credentials by calling methods
or a recently-added `redact_url` function, but this was not secure by
default. There were a number of other types (like `GitUrl`) that would
leak credentials on display.
This PR adds a `DisplaySafeUrl` newtype to prevent leaking credentials
when logging by default. It takes a maximalist approach, replacing the
use of `Url` almost everywhere. This includes when first parsing config
files, when storing URLs in types like `GitUrl`, and also when storing
URLs in types that in practice will never contain credentials (like
`DirectorySourceUrl`). The idea is to make it easy for developers to do
the right thing and for the compiler to support this (and to minimize
ever having to manually convert back and forth). Displaying credentials
now requires an active step. Note that despite this maximalist approach,
the use of the newtype should be zero cost.
One conspicuous place this PR does not use `DisplaySafeUrl` is in the
`uv-auth` crate. That would require new clones since there are calls to
`request.url()` that return a `&Url`. One option would have been to make
`DisplaySafeUrl` wrap a `Cow`, but this would lead to lifetime
annotations all over the codebase. I've created a separate PR based on
this one (#13576) that updates `uv-auth` to use `DisplaySafeUrl` with
one new clone. We can discuss the tradeoffs there.
Most of this PR just replaces `Url` with `DisplaySafeUrl`. The core is
`uv_redacted/lib.rs`, where the newtype is implemented. To make it
easier to review the rest, here are some points of note:
* `DisplaySafeUrl` has a `Display` implementation that masks
credentials. Currently, it will still display the username when there is
both a username and password. If we think is the wrong choice, it can
now be changed in one place.
* `DisplaySafeUrl` has a `remove_credentials()` method and also a
`.to_string_with_credentials()` method. This allows us to use it in a
variety of scenarios.
* `IndexUrl::redacted()` was renamed to
`IndexUrl::removed_credentials()` to make it clearer that we are not
masking.
* We convert from a `DisplaySafeUrl` to a `Url` when calling `reqwest`
methods like `.get()` and `.head()`.
* We convert from a `DisplaySafeUrl` to a `Url` when creating a
`uv_auth::Index`. That is because, as mentioned above, I will be
updating the `uv_auth` crate to use this newtype in a separate PR.
* A number of tests (e.g., in `pip_install.rs`) that formerly used
filters to mask tokens in the test output no longer need those filters
since tokens in URLs are now masked automatically.
* The one place we are still knowingly writing credentials to
`pyproject.toml` is when a URL with credentials is passed to `uv add`
with `--raw`. Since displaying credentials is no longer automatic, I
have added a `to_string_with_credentials()` method to the `Pep508Url`
trait. This is used when `--raw` is passed. Adding it to that trait is a
bit weird, but it's the simplest way to achieve the goal. I'm open to
suggestions on how to improve this, but note that because of the way
we're using generic bounds, it's not as simple as just creating a
separate trait for that method.
PackageMetadata, for whatever reason, does not have a mirrored Wire type
so it was easy to not realize that it contains markers that need to be
complexified.
Fixes#13614
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
We were previously rendering messages for the info level, carrying
overhead in pubgrub which using `log::info!`. We avoid this by only
configuring `LevelFilter::INFO` if the durations layer exists.
I've confirmed that the default `Subscriber::max_level_hint` goes from
`INFO` to `OFF` and the profile skips `Incompatibility::display`.
## Summary
Closes#13612
We check if the git error message includes `not a git repository` to
figure out if the path isn't a Git repo or if Git's broken. This PR sets
`LC_ALL=C` when invoking `git rev-parse --is-inside-work-tree` so that
the error message doesn’t change based on the user’s locale settings.
There is a runtime issue with some of these builds
Here is the testing I've seen (❌ has bug, ✅ works):
* uv version (pbs version)
* ❌ uv 0.7.7 (pbs 20250521)
* ❌ uv 0.7.6 (pbs 20250517)
* ✅ uv 0.7.5 (pbs 20250409)
* os
* ❌ linux
* ✅ windows
* arch
* ❌ x86_64
* ✅ aarch64
* python version
* ❌ 3.12
* ❌ 3.13
* ❌ 3.14
Fixes#13610
Stubs packages are different in that their name ends with `-stubs`,
their module is `<module name>-stubs` (with a dash, not the generally
legal underscore) and their modules contain a `__init__.pyi` instead of
an `__init__.py`
(https://typing.python.org/en/latest/spec/distributing.html#stub-only-packages).
We add support in the uv build backend by detecting the `-stubs` suffix.
Fixes#13546
---------
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| [markdown](https://redirect.github.com/wooorm/markdown-rs) |
dependencies | major | `0.3.0` -> `1.0.0` |
---
### Release Notes
<details>
<summary>wooorm/markdown-rs (markdown)</summary>
###
[`v1.0.0`](https://redirect.github.com/wooorm/markdown-rs/releases/tag/1.0.0)
💯
Nothing changed since the last alpha.
It’s just that: this crate’s now being used a bunch and working well, so
it’s time to be stable!
</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4yNTcuMyIsInVwZGF0ZWRJblZlciI6IjM5LjI1Ny4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
Fix the two version json tests to account for the possibility that uv
was built outside a git checkout (e.g. from an unpacked git archive) and
therefore does not have the commit info available. This approach uses
separate snapshots for the two cases, as suggested in discussion of pull
request #13251.
Fixes#13212
## Test Plan
1. `cargo test` in a git clone.
2. `cargo clean`, moved `.git` away, `cargo test` again.
---------
Co-authored-by: Aria Desires <aria.desires@gmail.com>
This adopts the logic from `uv remove` for locking and syncing, as the
scope of the changes made are ultimately similar. Unlike `uv remove`
there is no support for modifying PEP723 scripts, as these are not
versioned.
In doing this the `version` command gains a truckload of args for
configuring lock/sync behaviour. Presumably most of these are passed via
settings or env files, and not of particular concern.
The most interesting additions are:
* `--frozen`: makes `uv version` work ~exactly as it did before this PR
* `--locked`: errors if the lockfile is out of date
* `--no-sync`: updates the lockfile, but doesn't run the equivalent of
`uv sync`
* `--package name`: a convenience for referring to a package in the
workspace
Note that the existing `--dry-run` flag effectively implies `--frozen` for sets and bumps.
Fixes#13254Fixes#13548
In platform discovery we're parsing the output of the ELF interpreter,
e.g., `/lib64/ld-linux-x86-64.so.2`. This file is ld, not ldd, which was
incorrectly named in the code.
An alternative is naming everything ELF interpreter instead of ld.so.
## Summary
This test started failing, and it fails at least back to v0.6, so I
don't think it's on our end. I'm wondering if all the wheels here were
yanked? They're visible in the lockfile, but not on PyPI:
https://pypi.org/project/av/9.2.0/#files. So to get this passing, let's
just unpin it.
Edit: Ahh, ok. It looks like the project ran out of space, so they
removed wheels for all the older versions:
https://github.com/PyAV-Org/PyAV/issues/1879.
## Summary
Without the `git` feature, it fails with:
```
error: Failed to initialize Git repository at `/home/mgorny/.local/share/uv/tests/.tmp01wGGK/temp/preserve_executable_bit`
stdout:
stderr: error: `git` operations are not allowed — are you missing a cfg for the `git` feature?
```
## Test Plan
cargo test --features python --profile=fast-build --no-default-features
## Summary
Using "all extras" in `uv remove` will cause errors for projects with
conflicting extras. Now that we have a concept of "default extras", it
seems better to respect those defaults like we do for dependency groups.
Closes https://github.com/astral-sh/uv/issues/12770.
## Summary
<!-- What's the purpose of the change? What does it do, and why? -->
I've compared all the activator scripts here with the original ones in
https://github.com/pypa/virtualenv/tree/main/src/virtualenv/activation
and only the bash/POSIX script here was yielding a VIRTUAL_ENV_PROMPT
value with parenthesis and a trailing space, which should be part of the
shell prompt (PS1 for bash/POSIX) but not of the VIRTUAL_ENV_PROMPT
value itself. This fixes that small inconsistency. Fixes#13456
This reverts commit 0ec2d4e434
## Test Plan
<!-- How was it tested? -->
I didn't test this locally.
Rustfmt introduces a lot of formatting changes in the 2024 edition. To
not break everything all at once, we split out the set of formatting
changes compatible with both the 2021 and 2024 edition by first
formatting with the 2024 style, and then again with the currently used
2021 style.
Notable changes are the formatting of derive macro attributes and lines
with overly long strings and adding trailing semicolons after statements
consistently.
We may run on case-sensitive file systems (Linux, generally) or on
case-insensitive file systems (Windows, generally), while modules in
Python may be lower or upper case. For robustness over filesystem
casing, we require an explicit module name for modules with upper cases.
Fixes#13419
Love this tooling! Small adjustment to help on error messaging 🙏
<!--
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? -->
The previous message was missing the word **“like,”** which made it read
a tad awkwardly.
This PR inserts the missing word so the error reads naturally:
**After:**
```
$ uvx ch1.py
error: It looks like you tried to run a Python script at `ch1.py`, which is not supported by `uvx`
hint: Use `uv run ch1.py` instead
```
**Before:**
```
$ uvx ch1.py
error: It looks you tried to run a Python script at `ch1.py`, which is not supported by `uvx`
hint: Use `uv run ch1.py` instead
```
## Test Plan
<!-- How was it tested? -->
- `cargo run -- uvx examples/ch1.py` shows the updated message (see
“After” above).
- `cargo test` passes.
Unlike OS APIs, glob inclusion checks don't work when there are relative
path elements such as `./`. We normalize the path before using it for
the glob.
Fixes#13407
We were not correctly falling back to cached realm credentials when an
index URL was provided with only a username. This came up in a [later
comment](https://github.com/astral-sh/uv/issues/13443#issuecomment-2881115301)
on #13443 where credentials in a pip extra index in `uv.toml` were being
ignored when the same URL (but with only a username) was used at the
command line for `--extra-index-url`. I've added a test to catch this
case.
Closes#13443
## Summary
Related to https://github.com/astral-sh/uv/issues/12492
This change makes all progress bars vertically aligned. This is still a
WIP and so is not complete, in the current design I store `max_len` in
`BarState` and update it on every `on_request_start`, however this is
problematic since order matters, and if the largest name is not sent
first, the alignment is not complete. To mitigate this we'd probably
have to update all previous bars by "iterating" through the `bars` field
in `BarState` and update all request bars.
Below is an image of what happens when the largest name
(`nvidia-cusparselt-cu12`) is not the first (in this case, it was the
second to last).

## Test Plan
There are currently no tests, and I'm not sure how to design them since
from what I gather the `uv_snapshot` facilities record the final output,
not the intermediate stages.
---------
Co-authored-by: konstin <konstin@mailbox.org>
This PR redacts credentials in displayed URLs.
It mostly relies on a `redacted_url` function (and where possible
`IndexUrl::redacted`). This is a quick way to prevent leaked credentials
but it's prone to programmer error when adding new trace statements. A
better follow-on would use a `RedactedUrl` type with the appropriate
`Display` implementation. This would allow us to still extract
credentials from the URL while displaying it securely. On the plus side,
the sites where the `redacted_url` function are used serve as easy
signposts for where to use the new type in a future PR.
Closes#1714.
## Summary
We mapped both `.tgz` and `.tar.gz` to the same enum variant; later,
though, we made the assumption that a file marked with that variant
ended with exactly `.tar.gz`. Instead, we need to preserve the
originating suffix.
Closes https://github.com/astral-sh/uv/issues/13372.
## Summary
If a set of wheel tags includes a dot, this code is treating the part
_after_ the dot as an extension, and thereby failing to detect that the
entry is a symlink to an archive (and thereby removing the archive).
This is all an optimization, so this code just makes it a little
targeted: we skip specific known extensions, rather than anything with
any extension.
Closes https://github.com/astral-sh/uv/issues/13270.
This follows on from #13334 to fix another case.
<!--
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
If a dependency group contained any `{ include-group = "..." }` entries,
the sort detection would bail out. The root cause of the problem was
gating the sort detection behind `deps.iter().all(Value::is_str)`.
A public code search reveals that keeping include-groups at the top is
by far the most common, but keeping them at the bottom isn't uncommon.
In both of these cases, uv will now preserve the convention that is in
use.
Unless I've missed it, I don't think uv supports `uv add`ing an
include-group, and so that wasn't tested here.
## Test Plan
cargo test
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This also omits bounds on constraints, and is useful for that. This
retains `--raw-sources` as an alias. I've had this on my mind for a
while, but https://github.com/astral-sh/uv/pull/12946 reminded me of it
again.
## Summary
Closes#13253
## Test Plan
```sh
❯ cat pyproject.toml | rg required
required-version = ">=0.7.3, <0.8"
❯ cargo run -q --features self-update --manifest-path ~/uv/Cargo.toml add black
error: Required uv version `>=0.7.3, <0.8` does not match the running version `0.7.2`.
hint: Update `uv` by running `uv self update`.
❯ cat pyproject.toml | rg required
required-version = ">=0.7.3"
❯ cargo run -q --features self-update --manifest-path ~/uv/Cargo.toml add black
error: Required uv version `>=0.7.3` does not match the running version `0.7.2`.
hint: Update `uv` by running `uv self update`.
❯ cat pyproject.toml | rg required
required-version = "<0.7"
❯ cargo run -q --features self-update --manifest-path ~/uv/Cargo.toml add black
error: Required uv version `<0.7` does not match the running version `0.7.2`.
❯ cat pyproject.toml | rg required
required-version = ">=0.4,<0.7"
❯ cargo run -q --features self-update --manifest-path ~/uv/Cargo.toml add black
error: Required uv version `>=0.4, <0.7` does not match the running version `0.7.2`.
```
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
The prior implementation only looks for dependencies which are sorted by
name then specifier.
I knew uv was meant to preserve sorted dependencies, but it never seemed
to work for me.
I've always used the "sort lines" feature of PyCharm/Sublime to sort
these lists, and I guess I'm not the only one. In such a case,
`flask-wtf>=1.2.1` is sorted before `flask>=3.0.2`.
After digging into the code I realised what was happening, hence this
merge request.
Maybe there's a tool I'm not aware of that people are using to sort
dependencies "properly", or are doing it by hand, but I think this is
worth supporting.
Relevant issues: https://github.com/astral-sh/uv/issues/9076,
https://github.com/astral-sh/uv/issues/10738
## Test Plan
`cargo test`
e.g., these are misleading cruft in the error message at
https://github.com/astral-sh/uv/pull/12168#discussion_r2078204601
```
❯ uv python find /foo/bar
error: No interpreter found for path `/foo/bar` in virtual environments, managed installations, or search path
❯ cargo run -q -- python find /foo/bar
error: No interpreter found at path `/foo/bar`
```
When removing a Python interpreter underneath an existing venv, uv
currently shows a not found error:
```
error: Failed to inspect Python interpreter from active virtual environment at `.venv/bin/python3`
Caused by: Python interpreter not found at `/home/konsti/projects/uv/.venv/bin/python3`
```
This is unintuitive, as the file for the Python interpreter does exist,
it is a broken symlink that needs to be replaced with `uv venv`.
I've been encountering those occasionally, and I expect users that
switch between versions a lot will, too, especially when they also use
pyenv or a similar Python manager.
The new error hints at this solution:
```
error: Failed to inspect Python interpreter from active virtual environment at `.venv/bin/python3`
Caused by: Broken symlink at `.venv/bin/python3`, was the underlying Python interpreter removed?
hint: To recreate the virtual environment, run `uv venv`
```
PEP 639 does not allow any characters that aren't in either their
limited glob syntax or the alphanumeric Unicode characters. This means
there's no way to express a glob such as `**/@test` for the excludes.
We extend the glob syntax from PEP 639 by introducing backslash escapes,
which can escape all characters but path separators (forward and
backwards slashes) to be parsed verbatim.
This means we have two glob parsers: The strict PEP 639 parser for
`project.license-files`, and our extended parser for `tool.uv`, with a
slight difference if you need to use special characters, to both adhere
to PEP 639 and to support cases such as #13280.
Fixes#13280
We have test coverage for this elsewhere, but managed Python versions
are a distinct case because we know the _full_ version before querying
the interpreter (whereas, when we find them on the `PATH`, we usually
only know `X.y` from the file name).
This pre-filter logic now matches our subsequent logic at
060be9cef1/crates/uv-python/src/discovery.rs (L2146-L2149)060be9cef1
shows the snapshot change.
## Summary
Add a `--show-extras` argument to the `uv tool list` cli, to show which
extra dependencies were installed with the tool.
i.e.
```bash
$ uv tool install fastapi --with requests --with typer==0.14
```
```bash
$ uv tool list --show-extras
fastapi v0.115.12 [extras: requests, typer==0.14]
- fastapi
```
## Test Plan
Added a new test function based on the others in the same file, with the
other arguments tested with the new argument as well.
The goal of this PR is to support reproducible builds and best-effort
platform-independent builds. Previously, while the build backend would
build the same source dist and wheel on the same machine, they would
look different across different operating systems. This PR fixes the
platform-dependent walk dir order by sorting and removes
platform-specific permissions from the source dist that had caused those
differences.
The reproducibility goal does not extend to platform-dependent
filesystem features, such as permissions and links, especially in
interaction with Git. Since most users share code across platforms
through Git, we're focusing on cross-platform behavior under Git. One of
those caveats is intentional: If a file, such as a bash script, has an
executable bit, we preserve it. This means that E.g. builds of Git
checkout of a repository with an executable shell script in the sources
will have different archives on Unix and Windows. Another relevant case
are symlinks: By default, Git on Windows replaces symlinks with a file
that contains the path to the target file
(https://stackoverflow.com/q/5917249/3549270). (This example comes from
Cargo, where it means that the package archive is different on Windows
when symlinking license from the repository root to a workspace package)
Best reviewed commit-by-commit
## Summary
This adds GraalPy download metadata so that `uv python install graalpy`
works. See https://github.com/astral-sh/uv/issues/13114
## Test Plan
The existing integration test was changed to test this functionality.
In #13302, there was an IO error without context. This error seems to be
caused by a symlink error. Switching as symlinking to `fs_err` ensures
these errors will carry context in the future.
## Summary
We now show a user-visible warning if we're using a "stale" virtual
environment due to `--no-sync`. I'd also be fine erroring here.
Closes https://github.com/astral-sh/uv/issues/13235.
Add configuration documentation for the build backend and make it the
preview default.
The build backend should generally work with default configuration
unless you want specific features such as flat layout or module
renaming, there is only a dedicated configuration, but no concept or
guide page for the build backend. Once the build backend is stable, we
can update the guide documentation to explain that uv defaults to its
own build backend, but other build backends are also supported.
The uv build backend becomes the default in preview, giving it more
exposure from users and preparing it to make it the default proper. The
current documentation retains warnings that the build backend is in
preview.
To see current uses of `uv_build` on GitHub:
https://github.com/search?q=path%3A**%2Fpyproject.toml+uv_build%3E%3D0&type=code
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
If a script has some requirements, and you provide `--with`, we
currently ignore any constraints from those requirements. We might want
to treat them as hard constraints in the future. For now, though, we
just treat them as preferences -- so we _prefer_ those versions, but
don't require them to match and still run the `--with` resolution in
isolation.
Closes https://github.com/astral-sh/uv/issues/13173.
See commentary at
https://github.com/astral-sh/uv/issues/9828#issuecomment-2537542100
regarding the limitations and future upstream changes needed.
```
❯ cargo build --features self-update
Compiling uv v0.5.8 (/Users/zb/workspace/uv/crates/uv)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.28s
❯ cp ./target/debug/uv ~/.cargo/bin
❯ uv self update --dry-run
info: Checking for updates...
Nothing to do. You're on the latest version of uv (v0.5.8)
❯ uv self update --dry-run 0.5.7
info: Checking for updates...
Would update uv from v0.5.8 to v0.5.7
❯ vi ~/.config/uv/uv-receipt.json # Edit the receipt to think its on an older version
❯ uv self update --dry-run
info: Checking for updates...
Would update uv from v0.5.8 to the latest version
```
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
Part of https://github.com/astral-sh/uv/issues/13173, but doesn't close
the issue. This just respects preferences if your script uses a
lockfile, since we already support that for locked _projects_.
#5577 fixed a bug on macos due to dynamically linking lzma/xz through
static linking. In #7686, this feature was moved to the performance
category.
This PR moves the `xz2/static` back to the general default features,
and, inspired by https://github.com/Homebrew/homebrew-core/pull/222211,
it structures and documents the feature flags cleaner.
We need to take care that this feature does not accidentally disable
features we want.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
The only thing that changed for #12175 relevant to the existing
downloads is the order of nesting, so we're checking all nested IO
errors instead of only the first one.
See #13238
## Test Plan
This is an educated guess based on what happens if I turn off the
network during a download.
```
Downloading cpython-3.13.3-linux-x86_64-gnu (download) (20.3MiB)
TRACE Considering retry of error: ExtractError("cpython-3.13.3-20250409-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz", Io(Custom { kind: Other, error: TarError { desc: "failed to unpack `/home/konsti/.local/share/uv/python/.temp/.tmpe3AIvt/python/lib/libpython3.13.so.1.0`", io: Custom { kind: Other, error: TarError { desc: "failed to unpack `python/lib/libpython3.13.so.1.0` into `/home/konsti/.local/share/uv/python/.temp/.tmpe3AIvt/python/lib/libpython3.13.so.1.0`", io: Custom { kind: Other, error: reqwest::Error { kind: Decode, source: reqwest::Error { kind: Body, source: TimedOut } } } } } } }))
TRACE Cannot retry IO error: not one of `ConnectionReset` or `UnexpectedEof`
TRACE Cannot retry IO error: not one of `ConnectionReset` or `UnexpectedEof`
TRACE Cannot retry error: not an IO error
error: Failed to install cpython-3.13.3-linux-x86_64-gnu
Caused by: Failed to extract archive: cpython-3.13.3-20250409-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz
Caused by: failed to unpack `/home/konsti/.local/share/uv/python/.temp/.tmpe3AIvt/python/lib/libpython3.13.so.1.0`
Caused by: failed to unpack `python/lib/libpython3.13.so.1.0` into `/home/konsti/.local/share/uv/python/.temp/.tmpe3AIvt/python/lib/libpython3.13.so.1.0`
Caused by: error decoding response body
Caused by: request or response body error
Caused by: operation timed out
```
<!--
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? -->
Fixes#11970.
## Test Plan
<!-- How was it tested? -->
Ran `cargo nextest`
There is a new idna version of testpypi. While we don't select that
version due to the exclude-newer cutoff, the version is still available
to pubgrub as an incompatible choice, changing the error message on
conflicts.
## Summary
Part of #12838. Allow users to configure `python-downloads-json-url` in
`uv.toml` and not just from env.
I followed similar PR #8695, so same as there it's also available in the
CLI (I think maybe it's better not to be configurable from the CLI, but
since the mirror parameters are, I think it's better to do the same)
## Test Plan
<!-- How was it tested? -->
## Summary
In #10939 I added the generated
`crates/uv-python/src/download-metadata-minified.json` file which is a
minified version of `crates/uv-python/download-metadata.json`.
The main reason for this PR is to avoid bloating the git objects as this
is a single-line file.
As a bonus, I also filtered the embed json to include only the versions
for the compiled target. Which should improve the binary size and
performance by a bit.
## Test Plan
<!-- How was it tested? -->
Previously, we were using the wrong `Workspace` discovery and would
report the version of the workspace root, which would iterate up from
the `--project` directory and return the workspace root (with or without
a project in the root). Instead, we need `ProjectWorkspace` discovery
that returns the closest project.
This fixes `uv version --project <path>` where `<path>` belongs to a
workspace member.
Fixes#13213
I think this regressed in https://github.com/astral-sh/uv/pull/13027 — I
misunderstood what versions could be represented in the `pyvenv.cfg` (I
assumed they _never_ included pre-release components).
Closes#13233
uv’s default index strategy was designed with dependency confusion
attacks in mind. [According to the
docs](https://docs.astral.sh/uv/configuration/indexes/#searching-across-multiple-indexes),
“if a package exists on an internal index, it should always be installed
from the internal index, and never from PyPI”. Unfortunately, this is
not true in the case where authentication fails on that internal index.
In that case, uv will simply try the next index (even on the
`first-index` strategy). This means that uv is not secure by default in
this common scenario.
This PR causes uv to stop searching for a package if it encounters an
authentication failure at an index. It is possible to opt out of this
behavior for an index with a new `pyproject.toml` option
`ignore-error-codes`. For example:
```
[[tool.uv.index]]
name = "my-index"
url = "<index-url>"
ignore-error-codes = [401, 403]
```
This will also enable users to handle idiosyncratic registries in a more
fine-grained way. For example, PyTorch registries return a 403 when a
package is not found. In this PR, we special-case PyTorch registries to
ignore 403s, but users can use `ignore-error-codes` to handle similar
behaviors if they encounter them on internal registries.
Depends on #12651Closes#9429Closes#12362
If you pass a TOML file to `uv pip install` that isn't recognized, we
should just reject it instead of assuming `requirements.txt`. I just
don't see a real case where it's better to let the command proceed.
This is a reimplementation of #7248 with a new CLI interface.
The old `uv version` is now `uv self version` (also it has gained a
`--short` flag for parity).
The new `uv version` is now an interface for getting/setting the project
version.
To give a modicum of support for migration, if `uv version` is run and
we fail to find/read a `pyproject.toml` we will fallback to `uv self
version`. `uv version --project .` prevents this fallback from being
allowed.
The new API of `uv version` is as follows:
* pass nothing to read the project version
* pass a version to set the project version
* `--bump major|minor|patch` to semver-bump the project version
* `--dry-run` to show the result but not apply it
* `--short` to have the final printout contain only the final version
* `--output-format json` to get the final printout as json
```
$ uv version
myfast 0.1.0
$ uv version --bump major --dry-run
myfast 0.1.0 => 1.0.0
$ uv version 1.2.3 --dry-run
myfast 0.1.0 => 1.2.3
$ uv version 1.2.3
myfast 0.1.0 => 1.2.3
$ uv version --short
1.2.3
$ uv version --output-format json
{
"package_name": "myfast",
"version": "1.2.3",
"commit_info": null
}
```
Fixes#6298
Some registries (like Azure Artifact) can require you to authenticate
separately for every package URL if you do not authenticate for the
/simple endpoint. These changes make the auth middleware aware of index
URL endpoints and attempts to fetch keyring credentials for such an
index URL when making a request to any URL it's a prefix of.
The current uv behavior is to cache credentials either at the request
URL or realm level. But with these changes, we also need to cache
credentials at the index level. Note that when uv does not detect an
index URL for a request URL, it will continue to apply the old behavior.
Addresses part of #4056Closes#4583Closes#11236Closes#11391Closes#11507
Closes https://github.com/astral-sh/uv/issues/7804
Includes a few small minor changes to the messaging, but the primary
change is that in, e.g., `uvx foo`, if the `foo` package does not
provide the `foo` executable we will no longer execute an arbitrary
`foo` executable if present on the `PATH`. This prevents confusing and
surprising behavior, such as the user reported where they did `uv tool
install foobar` (which provides `foo`) then `uvx foo` (which does not
provide `foo`) later falls back to the executable provided by `foobar`
since it's on the `PATH`. We don't enforce this for `--from`, so things
like `uvx --from foo bash -c "..."` are still totally valid. We also
still allow `uvx foo` where the `foo` executable is provided by a
_dependency_ of `foo` instead of `foo` itself.
Most of the diff here is consolidating the logic of the
`hint_on_not_found` and `warn_executable_not_provided_by_package `
utilities.
## Summary
If you use `--torch-backend=auto`, we want to avoid selecting (e.g.) a
`+cu124` build of `torch` alongside a `+cu126` build of `torchvision`.
<!--
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? -->
This adds `poetry-core` as a build backend choice.
## Test Plan
<!-- How was it tested? -->
---------
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
Part of #8607. This is a pure refactor aimed at paving the way for
supporting the `default-extras` configuration in the `pyproject.toml`
file.
The `ExtraSpecification` struct has been refactored to align more
closely with the
[`DependencyGroups`](256b100a9e/crates/uv-configuration/src/dependency_groups.rs (L9))
struct.
## Test Plan
Existing tests.
## Summary
In https://github.com/astral-sh/uv/pull/12968, we added support for
upload time to `uv.lock`, but stylized as `upload_time`. The other keys
in `uv.lock` use kebab casing, as in common in Python formats, so this
really should've been `upload-time`. I want to change it ASAP to
minimize churn for users. Any users that already upgraded will of course
experience churn in their files a second time. But if we don't change it
now, we'll only increase the surface area of affected users.
So, this PR uses `upload-time` instead, but continues reading
`upload_time` to make it non-breaking.
Part of #11834
Currently, all Python installation are a streaming download-and-extract.
With this PR, we add the `UV_PYTHON_CACHE_DIR` variable. When set, the
installation is split into downloading the interpreter into
`UV_PYTHON_CACHE_DIR` and extracting it there from a second step. If the
archive is already present in `UV_PYTHON_CACHE_DIR`, we skip the
download.
The feature can be used to speed up tests and CI. Locally for me, `cargo
test -p uv -- python_install` goes from 43s to 7s (1,7s in release mode)
when setting `UV_PYTHON_CACHE_DIR`. It can also be used for offline
installation of Python interpreter, by copying the archives to a
directory in the offline machine, while the path rewriting is still
performed on the target machine on installation.
This PR restores #13041 and integrates two PRs from @zanieb:
* #13038
* #13040
It also adds tests for relative URI and fragment handling.
Closes#13037.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
Just a small PR to add mentions to `pylock.toml` in the CLI manual where
appropriate.
I tried to say "PEP-751 compatible lock files" when appropriate to also
include the case `r"^pylock\.([^.]+)\.toml$"`. Feel free to change that
if you think it's cluttery.
I also tried to include the "single-use" wording when it made sense.
I also have almost never used the `uv pip` interface, so maybe there are
some other minor things to add here and there about the usage of
`pylock.toml` that I missed.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
When working on #13025 I noticed this message was lacking versions,
which seems frustrating if you're debugging things.
I refactored the general `matches_interpreter` utilities that were added
in https://github.com/astral-sh/uv/pull/12884 into a more purpose-fit
function that returns an `Option` with the versions if there's a
mismatch.
This PR restores the `bogus_redirect` test that was
non-deterministically hanging (reverting #13076).
Mismatched package and distribution names were causing uv to hang prior
to #12917 (which added the `bogus_redirect` test). But with that fix, uv
was only checking for mismatched package names on the main thread (and
not the resolver thread). This allowed for a race condition which would
prevent uv from ever doing the check, triggering the original hang
condition. This PR adds the check to the resolver thread to prevent this
race condition.
When an index performs a bogus redirect or otherwise returns a different
distribution name than expected, uv currently hangs.
In the example case, requesting the simple index page for any package
returns the page for anyio. This mean querying the sniffio version map
returns only anyio entries, and the version maps resolves to an anyio
version. When the resolver makes a query for sniffio and waits for it to
resolve, the main thread finds an anyio and resolves only that in the
wait map, causing the hang.
We fix this by checking the name of the returned distribution against
the name of the requested distribution. For good measure, we add the
same check in `Request::Dist` and `Request::Installed`. For performance
and complexity reasons, we don't perform this check in the version map
itself, but only after a candidate distribution has been selected.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
Fixes several occurrences of the minor typo “This options” for “This
option.”
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
<!-- How was it tested? -->
Since this is just a typo fix in documentation and comment strings, no
particular testing was conducted.
## Notes
The typo fixes in `crates/uv-cli/src/lib.rs` would affect
`docs/reference/cli.md`. I assumed you might want to just re-generate
the reference documention, but fixing it up manually would look like:
```diff
diff --git a/docs/reference/cli.md b/docs/reference/cli.md
index 338fa0ff9..8851ca2c0 100644
--- a/docs/reference/cli.md
+++ b/docs/reference/cli.md
@@ -355,7 +355,7 @@ uv run [OPTIONS] [COMMAND]
</dd><dt id="uv-run--no-group"><a href="#uv-run--no-group"><code>--no-group</code></a> <i>no-group</i></dt><dd><p>Disable the specified dependency group.</p>
-<p>This options always takes precedence over default groups, <code>--all-groups</code>, and <code>--group</code>.</p>
+<p>This option always takes precedence over default groups, <code>--all-groups</code>, and <code>--group</code>.</p>
<p>May be provided multiple times.</p>
@@ -1757,7 +1757,7 @@ uv sync [OPTIONS]
</dd><dt id="uv-sync--no-group"><a href="#uv-sync--no-group"><code>--no-group</code></a> <i>no-group</i></dt><dd><p>Disable the specified dependency group.</p>
-<p>This options always takes precedence over default groups, <code>--all-groups</code>, and <code>--group</code>.</p>
+<p>This option always takes precedence over default groups, <code>--all-groups</code>, and <code>--group</code>.</p>
<p>May be provided multiple times.</p>
@@ -2492,7 +2492,7 @@ uv export [OPTIONS]
</dd><dt id="uv-export--no-group"><a href="#uv-export--no-group"><code>--no-group</code></a> <i>no-group</i></dt><dd><p>Disable the specified dependency group.</p>
-<p>This options always takes precedence over default groups, <code>--all-groups</code>, and <code>--group</code>.</p>
+<p>This option always takes precedence over default groups, <code>--all-groups</code>, and <code>--group</code>.</p>
<p>May be provided multiple times.</p>
@@ -2855,7 +2855,7 @@ uv tree [OPTIONS]
</dd><dt id="uv-tree--no-group"><a href="#uv-tree--no-group"><code>--no-group</code></a> <i>no-group</i></dt><dd><p>Disable the specified dependency group.</p>
-<p>This options always takes precedence over default groups, <code>--all-groups</code>, and <code>--group</code>.</p>
+<p>This option always takes precedence over default groups, <code>--all-groups</code>, and <code>--group</code>.</p>
<p>May be provided multiple times.</p>
```
<!--
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
Provide an in-code switch to permit using the `uv build-backend` command
rather than the default `uv-build` in the Python PEP517 backend. This
option is intended to be used by downstream packagers to provide an
option of reusing `uv` that was built already instead of having to build
a second `uv-build` executable that largely overlaps with `uv`.
Fixes#12389
## Test Plan
The option is intended for downstream consumption only, and it is tested
downstream (via attempting to build a package using the `uv_build`
backend). The backend itself is covered by tests already.
---------
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
We accept `pylock.toml` as a requirements file (e.g., `uv sync
pylock.toml` or `uv pip install -r pylock.toml`). When you provide a
`pylock.toml` file, we don't allow you to provide other requirements, or
constraints, etc. And you can only provide one `pylock.toml` file, not
multiple.
We might want to remove this from `uv pip install` for now, since `pip`
may end up with a different interface (whereas `uv pip sync` is already
specific to uv), and most of the arguments aren't applicable (like
`--resolution`, etc.). Regardless, it's behind `--preview` for both
commands.
## Summary
This PR adds `uv export` support for [PEP
751](https://peps.python.org/pep-0751). We don't yet expose a way to
consume the generated lockfile, but it's a first step.
The logic to go from `uv.lock` to "flat set of packages to include, with
markers telling us when to include them" is all shared with the
`requirements.txt` export (and extracted in
https://github.com/astral-sh/uv/pull/12956). So most of the code is just
converting from our internal types to the PEP 751 schema.
As I suspected quite some time ago
(https://github.com/astral-sh/uv/pull/6738#issuecomment-2315466033),
it's problematic that we don't handle _every_ signal here. This PR adds
handling for all of the Unix signals except `SIGCHLD`, `SIGIO`, and
`SIGPOLL` which seem incorrect to forward. Also notable, we _cannot_
handle `SIGKILL` so if someone sends that to the PID instead of the
PGID, they will leave dangling subprocesses.
Instead, we could use `exec` and avoid this handling. However, we'd lose
the ability to add nice error message on failure (e.g., as someone is
trying to add in https://github.com/astral-sh/uv/pull/12201) and, more
critically, we'd need to figure out how to clean up resources properly
(i.e., temporary directories) which currently happens on `Drop`. In the
long-term, we'll probably want an option to use `exec` — but we'll need
to figure out when to clean up resources or accept that they will
dangle. This was last discussed in
https://github.com/astral-sh/uv/issues/3095 — discussion on that
approach should continue there.
A note on the implementation: I spent time time trying to write the
handler using a tokio stream, so we could dynamically iterate over a
list of signals instead of copy/pasting the implementation — I couldn't
get it to work though and it didn't seem critical.
Closes https://github.com/astral-sh/uv/issues/12830
## Summary
Before:
```console
$ uv python list py --managed-python
error: Interpreter discovery for `executable name `py`` requires `search path` but only only managed is allowed
```
After:
```console
$ uv python list py --managed-python
error: Interpreter discovery for `executable name `py`` requires `search path` but only `only managed` is allowed
```
Closes: #4567
## Summary
When adding a package with Git reference options (`--rev`, `--tag`,
`--branch`) that already has a Git source defined, use the existing Git
URL with the new reference instead of reporting an error.
This allows commands like `uv add requests --branch main` to work when
requests is already defined with a Git source in the project
configuration.
Previously, you would need to provide the whole Git url again for this
to work:
```bash
uv add git+https://github.com/psf/requests --branch main
```
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
- [x] Add unit tests for project
- [x] Add unit tests for script
- [x] Tested locally for project and script environments like below
### Testing Project
In a directory using the `uv` executable from this PR (via replacing
every `uv` with `cargo run --`) initialize a project and virtual
environment
```bash
uv init
uv venv
```
move into the environment
```bash
# on mac
source .venv/bin/activate
```
and add a dependency with a git url
```bash
uv add git+https://github.com/Textualize/rich --branch master
```
Then change the branch of the project to see that the branch can be
changed without need of the whole git url:
```bash
uv add rich --branch py310
```
### Testing Script
Create the following file, e.g. `script.py`:
```python
import time
from rich.progress import track
print("Starting")
for i in track(range(20), description="For example:"):
time.sleep(0.05)
print("Done")
```
Now using `uv` (referencing the executable of this PR) add the
dependency
```bash
uv add --script script.py 'git+https://github.com/Textualize/rich' --branch master
```
and check we can execute the script:
```bash
uv run script.py
```
To test the change update the branch
```bash
uv add --script script.py rich --branch py310
```
and check that the dependency is updated and the script is executed:
```bash
uv run script.py
```
<!-- How was it tested? -->
----
This is my first time contributing to `uv` (yay, 🤗) so let me know if
there is something obvious i am missing.
Unit tests will follow soon.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
This is included in PEP 751, so we lose it when converting from
`uv.lock`. I think it's a good piece of information to include in the
`uv.lock` anyway.
I noticed in the trace output that we weren't obfuscating the
`Credentials` password in a trace message. This PR creates a `Password`
newtype with a custom `Debug` implementation.
uv was failing to authenticate on 302 redirects when credentials were
available. This was because it was relying on `reqwest_middleware`'s
default redirect behavior which bypasses the middleware pipeline when
trying the redirect request (and hence bypasses our authentication
middleware). This PR updates uv to retrigger the middleware pipeline
when handling a 302 redirect, correctly using credentials from the URL,
the keyring, or `.netrc`.
Closes#5595Closes#11097
Fixes#12914.
When `PythonDownloadRequest` does not have the `implementation` set, do
not set it to CPython when calling `fill`, otherwise only CPython
interpreters are shown when listing interpreters available for download,
with `uv python list`.
## Summary
This PR simplifies the version formatting by replacing `.white()` with
`.cyan()` styling for consistency.
Resolves#12940
## Test Plan
I manually recreated the code and tested it with this patch:
```diff
diff --git i/crates/uv/src/lib.rs w/crates/uv/src/lib.rs
index b9c01b002..cf051351f 100644
--- i/crates/uv/src/lib.rs
+++ w/crates/uv/src/lib.rs
@@ -1019,6 +1019,20 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
}) => commands::self_update(target_version, token, printer).await,
#[cfg(not(feature = "self-update"))]
Commands::Self_(_) => {
+ eprintln!("{}: {}", "error".cyan().bold(), "fake error message");
+
+ let version_information = format!(
+ "from {} to {}",
+ "v0.1.1".bold().cyan(),
+ "v0.1.2".bold().cyan(),
+ );
+ eprintln!(
+ "{}{} Upgraded uv {}! {}",
+ "success".green().bold(),
+ ":".bold(),
+ version_information,
+ format!("https://github.com/astral-sh/uv/releases/tag/{}", "v0.1.2").cyan()
+ );
anyhow::bail!(
"uv was installed through an external package manager, and self-update \
is not available. Please use your package manager to update uv."
```
In a light terminal, this is what it looks like:
<img width="750" alt="image"
src="https://github.com/user-attachments/assets/dc0d283c-e845-41fb-9821-80b0a3f1c4fe"
/>
Closes#12929
## Summary
Untag the `config-settings` value to support JSON schema according to
the
[docs](https://docs.astral.sh/uv/reference/settings/#config-settings).
```toml
[tool.uv]
config-settings = { editable_mode = "compat" }
```
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
Verified using the "Even Better TOML" extension with paths to old and
new `uv.schema.json`.
## Notes
I could not reproduce the issue with either the `taplo` (on which Even
Better Toml is built, afaik) and `check-jsonschema` CLI tools; with both
old and new versions of the `uv.schema.json` validated the
`pyproject.toml`.
Maybe for these there is some additional regularization going on and
that's also how a breaking case ended up in the docs?
I'm unsure on how to test for this.
After about an hour, the Even better TOML VSCode extension was the only
way to reproduce failing validation.
Let me know if I can do something else.
<!-- How was it tested? -->
Currently, `uv init` works without a `git` executable, and with a
working `git` executable, but not with a broken `git`, be it from GitHub
Action's Windows CI or from the shim we insert.
`uv init` calls git twice: Once `git rev-parse` to check whether a git
repo already exists, and then `git init` (if there is no git repository
yet and no `--vcs none`).
By separately handling the cases where git failed during `git rev-parse`
doesn't work vs. where the is no repository when checking for an
existing repo work tree, we can avoid calling `git init` for broken git
and erroring. We have to hardcode the expected git command outputs to be
able to check.
This is a rebased and updated version of #11925 based on my review (I
didn't have permission to push to their branch).
For posterity I've preserved their commits but my final commit
essentially rewrites the whole thing anyway.
Fixes#11637
---------
Co-authored-by: Chris Lieb <clieb@bitsighttech.com>
"Only show Python downloads, exclude installed distributions." might be
misunderstood as excluding installed distributions from `uv python list
--only-downloads`, implying that versions already installed won’t be
shown.
See #12769 for the motivation. We set the 4MB not only for the main
thread, but also for all tokio and rayon threads to fix a stack overflow
while unpacking wheels in production on Windows.
There are two variables for setting the stack size: A new
`UV_STACK_SIZE` that takes precedent, and the existing `RUST_MIN_STACK`.
When setting the stack size, `UV_STACK_SIZE` should be preferred, since
`RUST_MIN_STACK` affects all Rust applications, including build backends
we call (e.g., maturin). The minimum stack size is set to 1MB, the
lowest stack size we observed on a platform (Windows main thread).
Fixes#12769
## Test Plan
Tested manually with the example from #12769
## Summary
Closes#12855
This PR also fixed an issue, where `python_request` was matched against
`PythonVersion::Default`. Previously, if `python_request` was `3.13t`,
it would match the last branch, triggering a download of the Python
version if it wasn't already installed.
6b7f60c1ea/crates/uv/src/commands/project/init.rs (L421-L448)
```console
❯ uv init -v --managed-python --python 3.13t foo
DEBUG uv 0.6.14 (a4cec56dc 2025-04-09)
DEBUG Searching for Python 3.13t in managed installations
DEBUG Searching for managed installations at `/Users/Jo/.local/share/uv/python`
DEBUG Found managed installation `cpython-3.13.1-macos-aarch64-none`
DEBUG Found `cpython-3.13.1-macos-aarch64-none` at `/Users/Jo/.local/share/uv/python/cpython-3.13.1-macos-aarch64-none/bin/python3.13` (managed installations)
DEBUG Skipping interpreter at `/Users/Jo/.local/share/uv/python/cpython-3.13.1-macos-aarch64-none/bin/python3.13` from managed installations: does not satisfy request `3.13t`
DEBUG Skipping incompatible managed installation `cpython-3.12.8-macos-aarch64-none`
DEBUG Skipping incompatible managed installation `pypy-3.11.11-macos-aarch64-none`
DEBUG Requested Python not found, checking for available download...
DEBUG Acquired lock for `/Users/Jo/.local/share/uv/python`
DEBUG Using request timeout of 30s
INFO Fetching requested Python...
Downloading cpython-3.13.3+freethreaded-macos-aarch64-none (49.9MiB)
DEBUG Downloading https://github.com/astral-sh/python-build-standalone/releases/download/20250409/cpython-3.13.3%2B20250409-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst to temporary location: /Users/Jo/.local/share/uv/python/.temp/.tmpfoOLkE
DEBUG Extracting cpython-3.13.3%2B20250409-aarch64-apple-darwin-freethreaded%2Bpgo%2Blto-full.tar.zst
Downloaded cpython-3.13.3+freethreaded-macos-aarch64-none
DEBUG Moving /Users/Jo/.local/share/uv/python/.temp/.tmpfoOLkE/python/install to /Users/Jo/.local/share/uv/python/cpython-3.13.3+freethreaded-macos-aarch64-none
DEBUG Released lock at `/Users/Jo/.local/share/uv/python/.lock`
DEBUG Writing Python versions to `/private/tmp/foo/.python-version`
Initialized project `foo` at `/private/tmp/foo`
❯ cat foo/.python-version
3.13
```
After this PR, uv will not try to download it:
```console
❯ uv python uninstall 3.13t
❯ cargo run -- init -v --managed-python --python 3.13t bar
DEBUG uv 0.6.14+15 (6b7f60c1e 2025-04-12)
DEBUG Writing Python versions to `/private/tmp/bar/.python-version`
Initialized project `bar` at `/private/tmp/bar`
❯ cat bar/.python_version
3.13t
```
It was possible that a virtual environment became out of sync with the
interpreter it pointed to (for example, if a symlink was changed to an
updated Python version). In such a case, `pyvenv.cfg` and
`activate_this.py` would no longer be correct. This PR detects when the
`version` (`venv` module) or `version_info` (uv and `virtualenv`) field
in `pyvenv.cfg` is out of sync with the interpreter. In such a case, uv
recreates the virtual environment.
Closes#12461
We have been claiming in our releases that we provide
archives/installers for uv-build, but we only upload it as a wheel to
pypi. This is because cargo-dist tries to be helpful and find all your
apps, but this scales poorly to large workspaces like ours, as stuff
like this slips in. So invert the default and make uv the only package
dist will see until we say otherwise.
See e.g. https://github.com/astral-sh/uv/releases/tag/0.6.14Fixes#12883
By default, unlike on CI, a Windows machine does not allow creating
symlinks, so we have to unix-gate tests that assume symlinks.
We can't install the transformers ecosystem test on Windows due to
missing torch, so it is also unix-gated.
Windows translates error messages, so we have to filter the "File not
found" message, since it can also be a "Datei nicht gefunden".
## Summary
Closes#12687.
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
<!-- How was it tested? -->
Added the corresponding integration tests for:
- `uv sync --dry-run --locked`
- [x] Preview lock changes
- [x] Errors if lockfile is out-of-date
- `uv sync --dry-run --frozen`
- [x] Preview lock changes
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
Collapse whitespace into a single space in python_list tests, in order
to make them agnostic of padding, and therefore pass both with Python
3.12.9 and Python 3.12.10.
Fixes#12799
## Test Plan
cargo test --features python --profile=fast-build --no-default-features
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| [mimalloc](https://redirect.github.com/purpleprotocol/mimalloc_rust) |
dependencies | patch | `0.1.45` -> `0.1.46` |
---
### Release Notes
<details>
<summary>purpleprotocol/mimalloc_rust (mimalloc)</summary>
###
[`v0.1.46`](https://redirect.github.com/purpleprotocol/mimalloc_rust/releases/tag/v0.1.46):
Version 0.1.46
[Compare
Source](https://redirect.github.com/purpleprotocol/mimalloc_rust/compare/v0.1.45...v0.1.46)
##### Changes
- Fixed musl builds.
</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:eyJjcmVhdGVkSW5WZXIiOiIzOS4yMzguMCIsInVwZGF0ZWRJblZlciI6IjM5LjIzOC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJpbnRlcm5hbCJdfQ==-->
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
From PEP 440:
> The exclusive ordered comparison <V MUST NOT allow a pre-release of
the specified version unless the specified version is itself a
pre-release. Allowing pre-releases that are earlier than, but not equal
to a specific pre-release may be accomplished by using <V.rc1 or
similar.
We had an additional check that would block this even if the specifier
did have a pre-release.
This likely didn't show up earlier because `Ranges` uses different code
in the resolver.
I checked these changes against `packaging` to verify their behavior:
```python
print(SpecifierSet("<1").contains("1a1", prereleases=True)) # False
print(SpecifierSet("<1a2").contains("1a1", prereleases=True)) # True
print(SpecifierSet("<1").contains("1dev1", prereleases=True)) # False
print(SpecifierSet("<1dev2").contains("1dev1", prereleases=True)) # True
print(SpecifierSet("<1a2").contains("1dev1", prereleases=True)) # True
```
Closes#12834
## Summary
This PR errors out when an Unknown Dependency Object Specifier is used
in dependency groups.
Fixes#12638
## Test Plan
The current behaviour is as follows:
```bash
➜ example git:(12638/dependency-object-specifier) ✗ cargo run -- sync
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
Running `/home/luna/Documents/uv/target/debug/uv sync`
error: Failed to generate package metadata for `example==0.1.0 @ virtual+.`
Caused by: Group `bar` contains a Dependency Object Specifier, which is not supported by uv
```
And the pyproject.toml to produce this is:
```toml
[project]
name = "example"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13.2"
dependencies = []
[dependency-groups]
foo = ["pyparsing"]
bar = [{set-phasers-to = "stun"}]
```
## Summary
Closes#12806
Split `UV_INDEX` by any whitespace rather than only ASCII 32, which does
not align with the behavior of `PIP_EXTRA_INDEX_URL` and can possibly
lead to difficulties when migrating from pip to uv.
Clap unfortunately does not support passing multiple delimiters, writing
a custom parsing function involved parsing index into a Vec<Vec<Index>>
and flattening it afterwards in order to avoid breaking the --index
command line option.
There might be a prettier solution I overlooked, let me know if there is
anything I should change!
<!--
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? -->
Adds the env arg `UV_NO_EDITABLE`.
Closes#12735
## Test Plan
<!-- How was it tested? -->

I could not find a place where to add tests, any help would be
appreciated
---------
Co-authored-by: Aria Desires <aria.desires@gmail.com>
Check that the source and module directory exist when build a source
distribution, instead of delaying the check to building the wheel. This
prevents building source distributions that can never be built into
wheels.
I removed the `set_cksum` as the value of it is replaced inside of
`append_data`.
## Summary
This should fix#12762 but I don't know how to test it.
---------
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
I think the lack of enforcement here is an oversight. We _do_ already
enforce this for user-level configuration files (contrary to the issue
-- at least, in my testing and from reading the code).
Closes https://github.com/astral-sh/uv/issues/12753.
## Summary
See the test cases. Previously, you could end up with something like:
```toml
[tool.uv.workspace]
members = [
"foo",
"bar",
"baz", "bop",
]
```
<!--
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
Fixes#12606.
Two options considered, thanks to @zanieb's guidance are:
1. Special-casing on parse error and encountering the `environment.yml`
filename, possibly at `RequirementsTxt::parse`
2. Adding a new `RequirementsSource::EnvironmentYml` variant and
erroring on `RequirementSpecification::from_source`
I went with the latter for the following reasons:
- This edge case is explicitly modelled within the type system. However,
it changes the semantics of `RequirementsSource` to also model
_unsupported_ sources.
- (**Separation of concerns**) The special-casing would occur in the
`uv-requirements-txt` crate, which seems to be relatively deep in the
guts of the codebase. In my opinion, maintainers working in
`uv-requirements-txt` would reasonably assume the input file to be a
`requirements.txt` file, instead of having to be concerned with it being
another file format (`environment.yml`, `pyproject.toml`, etc.)
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
Manually tested as follows:
```sh
>>> cargo run -- pip install -r environment.yml
error: Conda environment file `environment.yml` is not supported
>>> cargo run -- add -r environment.yml
error: Conda environment file `environment.yml` is not supported
```
If you can point me to the appropriate test module, I can write up tests
for these to use `insta`.
<!-- How was it tested? -->
## Summary
Add an option to overwrite the list of available Python downloads from a
local JSON file by using the environment variable
`UV_PYTHON_DOWNLOADS_JSON_URL`
as an experimental support for providing custom sources for Python
distribution binaries #8015
related #10203
I probably should make the JSON to be fetched from a remote URL instead
of a local file.
please let me know what you think and I will modify the code
accordingly.
## Test Plan
### normal run
```
root@75c66494ba8b:/# /code/target/release/uv python list
cpython-3.14.0a4+freethreaded-linux-x86_64-gnu <download available>
cpython-3.14.0a4-linux-x86_64-gnu <download available>
cpython-3.13.1+freethreaded-linux-x86_64-gnu <download available>
cpython-3.13.1-linux-x86_64-gnu <download available>
cpython-3.12.8-linux-x86_64-gnu <download available>
cpython-3.11.11-linux-x86_64-gnu <download available>
cpython-3.10.16-linux-x86_64-gnu <download available>
cpython-3.9.21-linux-x86_64-gnu <download available>
cpython-3.8.20-linux-x86_64-gnu <download available>
cpython-3.7.9-linux-x86_64-gnu <download available>
pypy-3.10.14-linux-x86_64-gnu <download available>
pypy-3.9.19-linux-x86_64-gnu <download available>
pypy-3.8.16-linux-x86_64-gnu <download available>
pypy-3.7.13-linux-x86_64-gnu <download available>
```
### empty JSON file
```sh
root@75c66494ba8b:/# export UV_PYTHON_DOWNLOADS_JSON_URL=/code/crates/uv-python/my-download-metadata.json
root@75c66494ba8b:/# cat $UV_PYTHON_DOWNLOADS_JSON_URL
{}
root@75c66494ba8b:/# /code/target/release/uv python list
root@75c66494ba8b:/#
```
### JSON file with valid version
```sh
root@75c66494ba8b:/# export UV_PYTHON_DOWNLOADS_JSON_URL=/code/crates/uv-python/my-download-metadata.json
root@75c66494ba8b:/# cat $UV_PYTHON_DOWNLOADS_JSON_URL
{
"cpython-3.11.9-linux-x86_64-gnu": {
"name": "cpython",
"arch": {
"family": "x86_64",
"variant": null
},
"os": "linux",
"libc": "gnu",
"major": 3,
"minor": 11,
"patch": 9,
"prerelease": "",
"url": "https://github.com/astral-sh/python-build-standalone/releases/download/20240814/cpython-3.11.9%2B20240814-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz",
"sha256": "daa487c7e73005c4426ac393273117cf0e2dc4ab9b2eeda366e04cd00eea00c9",
"variant": null
}
}
root@75c66494ba8b:/# /code/target/release/uv python list
cpython-3.11.9-linux-x86_64-gnu <download available>
root@75c66494ba8b:/#
```
### Remote Path
```sh
root@75c66494ba8b:/# export UV_PYTHON_DOWNLOADS_JSON_URL=http://a.com/file.json
root@75c66494ba8b:/# /code/target/release/uv python list
error: Remote python downloads JSON is not yet supported, please use a local path (without `file://` prefix)
```
---------
Co-authored-by: Aria Desires <aria.desires@gmail.com>
## Summary
closes#12234
[fetch_with_cli](e0f81f0d4a/crates/uv-git/src/git.rs (L573))
doesn't respect the registry client's [connectivity
setting](e0f81f0d4a/crates/uv-client/src/registry_client.rs (L1009))
- this pr updates `fetch_with_cli` to set `GIT_ALLOW_PROTOCOL=file` when
the client's connectivity setting is `Connectivity::Offline`
## Test Plan
E2E
```sh
cargo run add "pycurl @ git+https://github.com/pycurl/pycurl.git" --directory ~/src/offline-test/ --offline
```
```sh
Compiling uv-cli v0.0.1 (/Users/justinchapman/src/uv/crates/uv-cli)
Compiling uv v0.6.11 (/Users/justinchapman/src/uv/crates/uv)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.47s
Running `target/debug/uv add 'pycurl @ git+https://github.com/pycurl/pycurl.git' --directory /Users/justinchapman/src/offline-test/ --offline`
Updating https://github.com/pycurl/pycurl.git (HEAD) × Failed to download and build `pycurl @ git+https://github.com/pycurl/pycurl.git`
├─▶ Git operation failed
├─▶ failed to fetch into: /Users/justinchapman/.cache/uv/git-v0/db/9a596e5213c3162d
╰─▶ process didn't exit successfully: `/usr/bin/git fetch --force --update-head-ok 'https://github.com/pycurl/pycurl.git' '+HEAD:refs/remotes/origin/HEAD'` (exit status: 128)
--- stderr
fatal: transport 'https' not allowed
help: If you want to add the package regardless of the failed resolution, provide the `--frozen` flag to skip locking and syncing.
```
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
@jtfmumm mentioned a desire for this. I'm not sure how we should do
this. I kind of want to change this to something like...
```
$ uv python find
CPython 3.13 @ <path>
$ uv python find --only-path
<path>
$ uv python find --short
<path>
$ uv python find --only-version
3.13
```
The change in defaults would be breaking though.
uv has a global `--no-config` option, which indeed has an effect.
## Test Plan
```console
❯ cat pyproject.toml
[project]
name = "hello"
version = "0.1.0"
[[tool.uv.index]]
url = "http://non-exist-host.com/simple"
default = true
❯ echo requests | uv pip compile -
⠦ Resolving dependencies... error: Failed to fetch: `http://non-exist-host.com/simple/requests/`
Caused by: Could not connect, are you offline?
Caused by: Request failed after 3 retries
Caused by: error sending request for url (http://non-exist-host.com/simple/requests/)
Caused by: client error (Connect)
Caused by: dns error: failed to lookup address information: nodename nor servname provided, or not known
Caused by: failed to lookup address information: nodename nor servname provided, or not known
# Before
❯ echo requests | uv pip compile --no-config -
warning: pip-compile's `--no-config` has no effect (uv does not use a configuration file)
Resolved 5 packages in 13ms
# This file was autogenerated by uv via the following command:
# uv pip compile --no-config -
certifi==2025.1.31
# via requests
charset-normalizer==3.4.1
# via requests
idna==3.10
# via requests
requests==2.32.3
urllib3==2.3.0
# via requests
# After
❯ echo requests | uv pip compile --no-config -
Resolved 5 packages in 13ms
# This file was autogenerated by uv via the following command:
# uv pip compile --no-config -
certifi==2025.1.31
# via requests
charset-normalizer==3.4.1
# via requests
idna==3.10
# via requests
requests==2.32.3
urllib3==2.3.0
# via requests
```
## Summary
Fix a suggestion in the docs on configs through environment variables,
which lists an option value that doesn't appear to exist.
The description implies that `unsafe-best-match` was intended here.
## Test Plan
Verified by providing `unsafe-any-match` as a parameter to `uv`. It
didn't error, but appeared to use the `first-index` strategy instead.
The value I changed it to behaves as described by the documentation.
In an attempt to avoid reporting shims as their resolved
`sys.executable` path, we report the queried executable path instead.
This seems more correct for this command, broadly? Interestingly, it
changes the reported paths for Homebrew Python
<img width="1430" alt="Screenshot 2025-04-02 at 11 05 18 AM"
src="https://github.com/user-attachments/assets/0e1600e8-fb07-40c7-a6d6-56eaeb4b9293"
/>
Closes#9979
This will in principle fix the problem reported in #12611 that
`authenticate = "always"` is ignored for an index when `explicit =
true`. This change ensures all indexes are added to the URL auth
policies list passed to our auth middleware.
Incorporates #12624Fixes#12611
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
Fixes#12618
Instead of succeeding the user now gets:
```
uvdloc pip install osqp==1.0.2 --reinstall --python-platform=linux
Resolved 7 packages in 171ms
× Failed to download `osqp==1.0.2`
├─▶ Failed to extract archive
╰─▶ a computed CRC32 value did not match the expected value
```
I am not entirely sure if we have infra for testing this kind of thing,
but it would be nice to check in a test or two. I'm also not entirely
clear if there's any cases where these checks are overzealous.
## Summary
I noticed that these only support Basic credentials, but we may want to
allow users to provide Bearer tokens? This PR just generalizes the type.
serde needs to be told where to put underscores. someone clearly noticed
this when adding attributes for schemars, but they need to be present
for serde too and then schemars gets them for free.
Strictly speaking this would be a breaking change for anyone who noticed
the parsing was messed up and worked around it. So we add aliases for
backcompat, at least for a few releases.
Fixes#12590
## Summary
<!-- What's the purpose of the change? What does it do, and why? -->
Support the `UV_PROJECT` environment variable to set the project
directory.
#11946
## Test Plan
<!-- How was it tested? -->
`cargo nextest run` passed except the cache_prune.
```
export UV_PROJECT=/path/to/project
uv sync
```
works.
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
The overall strategy here is to make this code look more like
`requirements_txt.rs`: we seed the root members, then perform a DFS.
Previously, we created all nodes upfront, which caused problems when
using `--only-group`, since we'd omit "production" dependencies of
development dependencies.
Closes https://github.com/astral-sh/uv/issues/12526.
Adding dependency trace/parent comments ("via ...") to the export
command output.
This is a similar behavior to the pip compile output.
#### Note to the eager reviewer:
First of all - thanks!
Secondly, this is still a very rough draft. These are the first lines of
code I've ever written in Rust. This is still mostly an educational/fun
exercise for myself. If opening a Draft PR is creating too much noise -
I apologize and I will close it until it is ready.
## Summary
Resolves#7777
## Test Plan
- [X] manual command execution
- [x] update expected output in tests
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
uv doesn't separate the metadata block from other blocks when adding the
`script` block to a script, which results in the next block being
considered part of the script block and causes errors when running.
See #12499 for more details.
Closes#12499
## Test Plan
I manually tested the most common scenario, but there's a few edge cases
that would be good to have tests for.
I would have written the tests also, but I was running into errors like
this:
```bash
$ cargo test --package uv-scripts
Compiling uv-configuration v0.0.1 (/home/merlin/Projects/uv/crates/uv-configuration)
error: cannot find attribute `value` in this scope
--> crates/uv-configuration/src/project_build_backend.rs:8:38
|
8 | #[cfg_attr(feature = "schemars", value(hide = true))]
| ^^^^^
error: could not compile `uv-configuration` (lib) due to 1 previous error
```
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
We tend not to run tests for individual crates, which can lead to weird
situations like this, where crates are missing optional features that
are otherwise installed globally.
## Test Plan
Run `cargo test --profile fast-build -p uv-scripts`, which otherwise
fails to compile.
The idea here is that we introduce a new stdout_important method for
things that want to care about the difference between "quiet" and
"silent".
This PR is WIP because it has no actual uses of stdout_important, and we
should have at least one before landing this. Perhaps someone has a
suggestion for commands that would really benefit from this distinction?
Fixes#10431
## Summary
This PR extends `[[tool.uv.index]]` to support `--find-links`-style
"flat" indexes, so that users can point to such indexes without using
`--find-links` _and_ get access to the full functionality of
`[[tool.uv.index]]` (e.g., they can now pin packages to
`--find-links`-style indexes).
Note that, at present, `--find-links` indexes actually have some quirky
behavior, in that we combine them into a single entity and then merge
the discovered distributions into each Simple API-style index. The
motivation here, IIRC, was to match pip's behavior quite closely. I'm
interested in _removing_ that behavior, but it'd be breaking (and may
also be inconvenient for some use-cases). So, the behavior for indexes
passed in via `--find-links` remains completely unchanged. However,
`[[tool.uv.index]]` entries with `format = "flat"` are now treated
identically to those defined with `format = "simple"` (the default), in
that we stop after we find the first-matching index, etc.
Closes https://github.com/astral-sh/uv/issues/11634.
## Summary
I don't know if I actually want to commit this, but I did it on the
plane last time and just polished it off (got it to compile) while
waiting to board.
We were not applying the `authenticate = "always"` behavior to `uv pip`
commands (related to #12362). This PR addresses that, applying
authentication policies wherever we set up a registry client.
## Summary
This fixes https://github.com/astral-sh/uv/issues/12426 which helps use
a more accurate arg name in the help output.
## Test Plan
I didn't test it locally, @charliermarsh gave me guidance on what to
change so I looked around that file for another example of `value_name`
and repeated what I saw. I kept it formatted to 1 line based on it not
being a long line. The other example of `value_name` had everything on
separate lines because there were a bunch of parameters passed in.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
This PR modifies the requirement source entities to store a (new)
container struct that wraps `IndexUrl`. This will allow us to store
user-defined metadata alongside `IndexUrl`, and propagate that metadata
throughout resolution.
Specifically, I need to store the "kind" of the index (Simple API vs.
`--find-links`), but I also ran into this problem when I tried to add
support for overriding `Cache-Control` headers on a per-index basis: at
present, we have no way to passing around metadata alongside an
`IndexUrl`.
Match the module name to its module directory with potentially different
casing.
For example, a package may have the dist-info-normalized package name
`pil_util`, but the importable module is named `PIL_util`.
We get the module name either as dist-info-normalized package name, or
explicitly from the user. For dist-info-normalizing a package name, the
rules are lowercasing, replacing `.` with `_` and replace `-` with `_`.
Since `.` and `-` are not allowed in module names, we can check whether
a directory name matches our expected module name by lowercasing it.
Fixes#12187
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
In general, we merge `--find-links` entries into each index. If a
package is pinned to an index, though, it seems surprising (and wrong)
that we'd ever select a distribution from `--find-links`. This PR
modifies the provider to ignore `--find-links` for any explicitly pinned
packages.
Allows `uv python list <request>` to filter the installed list. I often
want this and it's not hard to add.
I tested the remote download filtering locally (#12381 is needed for
snapshot tests)
```
❯ cargo run -q -- python list --all-versions 3.13
cpython-3.13.2-macos-aarch64-none <download available>
cpython-3.13.1-macos-aarch64-none /opt/homebrew/opt/python@3.13/bin/python3.13 -> ../Frameworks/Python.framework/Versions/3.13/bin/python3.13
cpython-3.13.1-macos-aarch64-none <download available>
cpython-3.13.0-macos-aarch64-none /Users/zb/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/bin/python3.13
❯ cargo run -q -- python list --all-versions 3.13 --only-installed
cpython-3.13.1-macos-aarch64-none /opt/homebrew/opt/python@3.13/bin/python3.13 -> ../Frameworks/Python.framework/Versions/3.13/bin/python3.13
cpython-3.13.0-macos-aarch64-none /Users/zb/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/bin/python3.13
```
## Summary
I want to use the flat index client from within the registry client, so
making them both depend on the same underlying primitives rather than
having the flat index client depend on the registry client.
## Summary
We respect `--exclude-newer` during resolution, but we weren't applying
it to individual _files_ when writing the lockfile. As a result, if
wheels were added to a distribution after its initial release, we'd end
up including them in the lockfile, even if they were uploaded after the
`--exclude-newer` date.
Closes https://github.com/astral-sh/uv/issues/12296.
## Summary
It's possible that the PyTorch version the user depends on isn't in the
latest index. These indexes are equally trusted, so we should override
the policy.
Closes#12357.
<!--
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? -->
This fixes a case described in #12333, where trailing comments in
dependencies can be unexpectedly shifted when a new dependency is added.
Fixes#12333.
## Test Plan
<!-- How was it tested? -->
`cargo test` (Added a snapshot test)
## Summary
Resolves#11794.
When `uv python find` is given a `--script` option, either the existing
environment for that script or the Python executable that would be used
to create it will be returned. If neither are found, the command exits
with exit code 1.
`--script` is incompatible with all other options to the same command.
## Test Plan
Unit tests.
## Summary
This crate is for standards-compliant types, but this is explicitly a
type that's custom to uv. It's also strange because we kind of want to
reference `IndexUrl` on the registry type, but that's in a crate that
_depends_ on `uv-pypi-types`, which to me is a sign that this is off.
There was a bug where `UV_MANAGED_PYTHON` and `UV_NO_MANAGED_PYTHON`
only accepted `true` or `false`. This switches to the boolish value
parser for those flags.
Closes#12336
## Summary
Fixes the failing `cache_prune::prune_unzipped` test that was causing CI
failures in my other PR (#12328) and others like PR #12327.
The error message format changed to show a specific version constraint
(`iniconfig<=2.0.0`) rather than the generic 'all versions' message.
This PR updates the test to expect the new, more specific error message.
## Test Plan
Ran `cargo test -p uv cache_prune::prune_unzipped` to verify the test
now passes.
Previously, we required a username to perform a fetch from the keyring
because the `keyring` CLI only supported fetching password for a given
service and username. Unfortunately, this is different from the keyring
Python API which supported fetching a username _and_ password for a
given service. We can't (easily) use the Python API because we don't
expect `keyring` to be installed in a specific environment during
network requests. This means that we did not have parity with `pip`.
Way back in https://github.com/jaraco/keyring/pull/678 we got a `--mode
creds` flag added to `keyring`'s CLI which supports parity with the
Python API. Since `keyring` is expensive to invoke and we cannot be
certain that users are on the latest version of keyring, we've not added
support for invoking keyring with this flag. However, now that we have a
mode that says authentication is _required_ for an index (#11896), we
might as well _try_ to invoke keyring with `--mode creds` when there is
no username. This will address use-cases where the username is
non-constant and move us closer to `pip` parity.
This addresses a small part of #12280, namely when you have
`authenticate` set to `always`, it will output a distinct error message
for the case where you have a username but are missing a password.
## Summary
This is a prototype that I'm considering shipping under `--preview`,
based on [`light-the-torch`](https://github.com/pmeier/light-the-torch).
`light-the-torch` patches pip to pull PyTorch packages from the PyTorch
indexes automatically. And, in particular, `light-the-torch` will query
the installed CUDA drivers to determine which indexes are compatible
with your system.
This PR implements equivalent behavior under `--torch-backend auto`,
though you can also set `--torch-backend cpu`, etc. for convenience.
When enabled, the registry client will fetch from the appropriate
PyTorch index when it sees a package from the PyTorch ecosystem (and
ignore any other configured indexes, _unless_ the package is explicitly
pinned to a different index).
Right now, this is only implemented in the `uv pip` CLI, since it
doesn't quite fit into the lockfile APIs given that it relies on feature
detection on the currently-running machine.
## Test Plan
On macOS, you can test this with (e.g.):
```shell
UV_TORCH_BACKEND=auto UV_CUDA_DRIVER_VERSION=450.80.2 cargo run \
pip install torch --python-platform linux --python-version 3.12
```
On a GPU-enabled EC2 machine:
```shell
ubuntu@ip-172-31-47-149:~/uv$ UV_TORCH_BACKEND=auto cargo run pip install torch -v
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/uv pip install torch -v`
DEBUG uv 0.6.6 (e95ca063b 2025-03-14)
DEBUG Searching for default Python interpreter in virtual environments
DEBUG Found `cpython-3.13.0-linux-x86_64-gnu` at `/home/ubuntu/uv/.venv/bin/python3` (virtual environment)
DEBUG Using Python 3.13.0 environment at: .venv
DEBUG Acquired lock for `.venv`
DEBUG At least one requirement is not satisfied: torch
warning: The `--torch-backend` setting is experimental and may change without warning. Pass `--preview` to disable this warning.
DEBUG Detected CUDA driver version from `/sys/module/nvidia/version`: 550.144.3
...
```
## Summary
I think this is reasonable to change. Right now, if you're on Python
3.11, the resolver returns `multiprocess-0.70.17-py311-none-any.whl`,
but `multiprocess-0.70.17-py310-none-any.whl` is in the cache, we'll
reuse `multiprocess-0.70.17-py310-none-any.whl` (since it _is_
compatible with Python 3.11).
Instead, we now _require_ the cached wheel to match the wheel returned
by the resolver.
Closes https://github.com/astral-sh/uv/issues/12273.
Currently, for users to specify at the command line whether to use
uv-managed or system Python interpreters, they use the
`--python-preference` parameter, which takes four possible values. This
is more complex than necessary since the normal case is to either say
"only managed" or "not managed". This PR hides the old
`--python-preference` parameter from help and documentation and adds two
new flags: `--managed-python` and `--no-managed-python` to capture the
"only managed" and "not managed" cases.
I have successfully tested this locally but currently cannot add
snapshot tests because of problems with distinguishing managed vs.
system interpreters in CI (and non-determinism when run on different
developers' machines). The `--python-preference` test in
`tool-install.rs` is currently ignored for this reason. See #5144 and
#7473.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
I suspect this only affects packages with quotes in the requires-python,
which is typically an error but one that we correct for in
`LenientVersionSpecifiers`.
Closes https://github.com/astral-sh/uv/issues/12260.
Reject lockfiles where the package version and the wheel versions are
incoherent. This implicitly checks that all wheel files have the same
version.
It does not check for the source dist version, since a source dist may
not contain a version in the filename and attempting to deserialize
source dist filenames we may not need is a performance overhead for
something that's already slow in `uv run`.
Fixes#12164
## Summary
This has come up a few times, so it seems worth addressing. If you
migrate from a flat layout to a `src` layout or vice versa, we now
invalidate the package metadata.
Closes https://github.com/astral-sh/uv/issues/12047
## Summary
This ended up being more involved than expected. The gist is that we
setup all the packages we want to reinstall upfront (they're passed in
on the command-line); but at that point, we don't have names for all the
packages that the user has specified. (Consider, e.g., `uv pip install
.` -- we don't have a name for `.`, so we can't add it to the list of
`Reinstall` packages.)
Now, `Reinstall` also accepts paths, so we can augment `Reinstall` based
on the user-provided paths.
Closes#12038.
This is a minimal redux of #10861 to be compatible with `uv pip`.
This implements the interface described in:
https://github.com/pypa/pip/pull/13065#issuecomment-2544000876 for `uv
pip install` and `uv pip compile`. Namely `--group <[path:]name>`, where
`path` when not defined defaults to `pyproject.toml`.
In that interface they add `--group` to `pip install`, `pip download`,
and `pip wheel`. Notably we do not define `uv pip download` and `uv pip
wheel`, so for parity we only need to implement `uv pip install`.
However, we also support `uv pip compile` which is not part of pip
itself, and `--group` makes sense there too.
----
The behaviour of `--group` for `uv pip` commands makes sense for the
cases upstream pip supports, but has confusing meanings in cases that
only we support (because reading pyproject.tomls is New Tech to them but
heavily supported by us). **Specifically case (h) below is a concerning
footgun, and case (e) below may get complaints from people who aren't
well-versed in dependency-groups-as-they-pertain-to-wheels.**
## Only Group Flags
Group flags on their own work reasonably and uncontroversially, except
perhaps that they don't do very clever automatic project discovery.
a) `uv pip install --group path/to/pyproject.toml:mygroup` pulls up
`path/to/project.toml` and installs all the packages listed by its
`mygroup` dependency-group (essentially treating it like another kind of
requirements.txt). In this regard it functions similarly to
`--only-group` in the rest of uv's interface.
b) `uv pip install --group mygroup` is just sugar for `uv pip install
--group pyproject.toml:mygroup` (**note that no project discovery
occurs**, upstream pip simply hardcodes the path "pyproject.toml" here
and we reproduce that.)
c) `uv pip install --group a/pyproject.toml:groupx --group
b/pyproject.toml:groupy`, and any other instance of multiple `--group`
flags, can be understood as completely independent requests for the
given groups at the given files.
## Groups With Named Packages
Groups being mixed with named packages also work in a fairly
unsurprising way, especially if you understand that things like
dependency-groups are not really supposed to exist on pypi, they're just
for local development.
d) `uv pip install mypackage --group path/to/pyproject.toml:mygroup`
much like multiple instances of `--group` the two requests here are
essentially completely independent: pleases install `mypackage`, and
please also install `path/to/pyproject.toml:mygroup`.
e) `uv pip install mypackage --group mygroup` is exactly the same, but
this is where it becomes possible for someone to be a little confused,
as you might think `mygroup` is supposed to refer to `mypackage` in some
way (it can't). But no, it's sourcing `pyproject.toml:mygroup` from the
current working directory.
## Groups With Requirements/Sourcetrees/Editables
Requirements and sourcetrees are where I expect users to get confused.
It behaves *exactly* the same as it does in the previous sections but
you would absolutely be forgiven for expecting a different behaviour.
*Especially* because `--group` with the rest of uv *does* do something
different.
f) `uv pip install -r a/pyproject.toml --group b/pyproject.toml:mygroup`
is again just two independent requests (install `a/pyproject.toml`'s
dependencies, and `b/pyproject.toml`'s `mygroup`).
g) `uv pip install -r pyproject.toml --group mygroup` is exactly like
the previous case but *incidentally* the two requests refer to the same
file. What the user wanted to happen is almost certainly happening, but
they are likely getting "lucky" here that they're requesting something
simple.
h) `uv pip install -r a/pyproject.toml --group mygroup` is again exactly
the same but the user is likely to get surprised and upset as this
invocation actually sources two different files (install
`a/pyproject.toml`'s dependencies, and `pyproject.toml`'s `mygroup`)! I
would expect most people to assume the `--group` flag here is covering
all applicable requirements/sourcetrees/editables, but no, it continues
to be a totally independent reference to a file with a hardcoded
relative path.
------
Fixes https://github.com/astral-sh/uv/issues/8590
Fixes https://github.com/astral-sh/uv/issues/8969
## Summary
Closes https://github.com/astral-sh/uv/issues/12178
## Test Plan
Added new test. Manually tested on Windows and Linux.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
This PR enables module name overrides for editable installs.
Builds upon https://github.com/astral-sh/uv/pull/11884. The
`tool.uv.build-backend.module-name` option is now respected during
editable build processes.
## Test Plan
Added a test.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
<!--
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
There were no GraalPy binary wheels were available when uv support was
added, and thus the abi tag was never tested against actual packages.
Now that GraalPy publishes binary wheels to
https://www.graalvm.org/python/wheels/ we noticed the abi tag was
incorrect and the version info incorrectly determined.
## Test Plan
I tested manually:
```
> target/debug/uv venv --python graalpy testvenv
Using GraalPy 3.11.7 interpreter at: /home/tim/.pyenv/versions/graalpy-24.1.1/bin/graalpy
Creating virtual environment at: testvenv
Activate with: source testvenv/bin/activate
> cat <<EOF> uv.toml
> [[index]]
> url = "https://www.graalvm.org/python/wheels/"
> EOF
> target/debug/uv -v pip install psutil
warning: Found both a `uv.toml` file and a `[tool.uv]` section in an adjacent `pyproject.toml`. The `[tool.uv]` section will be ignored in favor of the `uv.toml` file.
DEBUG uv 0.6.6+3 (be8725553 2025-03-13)
DEBUG Searching for default Python interpreter in virtual environments
DEBUG Found `graalpy-3.11.7-linux-x86_64-gnu` at `/home/tim/dev/uv/.venv/bin/python3` (virtual environment)
DEBUG Using Python 3.11.7 environment at: .venv
DEBUG Acquired lock for `.venv`
DEBUG At least one requirement is not satisfied: psutil
DEBUG Using request timeout of 30s
DEBUG Solving with installed Python version: 3.11.7
DEBUG Solving with target Python version: >=3.11.7
DEBUG Adding direct dependency: psutil*
DEBUG Found fresh response for: https://www.graalvm.org/python/wheels/psutil/
DEBUG Searching for a compatible version of psutil (*)
DEBUG Selecting: psutil==5.9.8 [compatible] (psutil-5.9.8-graalpy311-graalpy241_311_native-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_28_x86_64.whl)
DEBUG No cache entry for: https://gds.oracle.com/download/graalpy-wheels/psutil-5.9.8-graalpy311-graalpy241_311_native-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_28_x86_64.whl
DEBUG Tried 1 versions: psutil 1
DEBUG marker environment resolution took 0.968s
Resolved 1 package in 971ms
DEBUG Identified uncached distribution: psutil==5.9.8
DEBUG No cache entry for: https://gds.oracle.com/download/graalpy-wheels/psutil-5.9.8-graalpy311-graalpy241_311_native-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_28_x86_64.whl
Prepared 1 package in 268ms
Installed 1 package in 28ms
+ psutil==5.9.8
DEBUG Released lock at `/home/tim/dev/uv/.venv/.lock`
```
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
In #10669, a pyproject.toml with requires-python but no environment had
a lockfile covering only a subset of the requires-python space:
```toml
resolution-markers = [
"python_full_version >= '3.10' and platform_python_implementation == 'CPython'",
"python_full_version == '3.9.*'",
"python_full_version < '3.9'",
]
```
This marker set is invalid, we have to reject the lockfile. (We can
still use the versions though, to avoid churn).
Part 1/2 of #10669
These changes add support for
```
uv python pin 3.12 --global
```
This adds the specified version to a `.python-version` file in the
user-level config directory. uv will now use the user-level version as a
fallback if no version is found in the project directory or its
ancestors.
Closes#4972
When making changes to uv that require new (or altered) settings, there
are many places in the code that need to change. This slows down work,
reduces confidence in changes for new developers, and adds noise to PRs.
The goal of this PR is to reduce the number of points that need to
change (and that the developer needs to understand) when making changes
to settings.
This PR consolidates `ResolverSettings` and `ResolverInstallerSettings`
by factoring out the shared settings and using a new field
`resolver_settings` on `ResolverInstallerSettings`. This not only
reduces repetition, but makes it easier for a human to parse the code
without having to compare long lists of fields to spot differences (the
difference was that `ResolverInstallerSettings` had two extra fields).
This also removes `ResolverSettingsRef` and
`ResolverInstallerSettingsRef`, using normal Rust references instead.
For the time being, I've left `InstallerSettingsRef` in place because it
appears to have a semantic meaning that might be relied upon. However,
it would now be straightforward to refactor to pass
`&ResolverInstallerSettings` wherever `InstallerSettingsRef` appears,
further reducing sprawl.
The change has the downside of adding
`settings.resolver_settings.<field>` and requiring dereferencing at
various points where it was not required before (with the *SettingsRef
approach). But this means there are significantly fewer places that must
change to update settings.
Following the upstream release and #12120, removes gating preventing
installation of the managed musl Python versions.
Of note
- The filtering of musl Python distributions has moved from the Rust
runtime to the metadata fetcher
- The filtering is now conditional on the PBS release date, removing all
old static musl distributions
- We could support the `+static` musl downloads in the future; right
now, they are deprioritized when selecting a variant
- I added test to CI which uses Alpine and installs numpy
<!--
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
This PR is meant to fix issue #11862
It allows to send sized bodies during `publish`
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
the PR was tested on the MRE from #11862
<!-- How was it tested? -->
## Summary
Follow up to https://github.com/astral-sh/uv/pull/11888 with added
support for uv tool run.
Changes
* Added functionality for running windows scripts in previous PR was
moved from run.rs to uv_shell::runnable.
* EXE was added as a supported type, this simplified integration across
both uv run and uvx while retaining a backwards compatible behavior and
properly prioritizing .exe over others. Name was adjusted to runnable as
a result to better represent intent.
## Test Plan
New tests added.
## Documentation
Added new documentation.
Reduce the overhead of `uv run` in large workspaces. Instead of
re-discovering the entire workspace each time we resolve the metadata of
a member, we can the discovered set of workspace members. Care needs to
be taken to not cache the discovery for `uv init`, `uv add` and `uv
remove`, which change the definitions of workspace members.
Below is apache airflow e3fe06382df4b19f2c0de40ce7c0bdc726754c74 `uv run
python` with a minimal payload. With this change, we avoid a ~350ms
overhead of each `uv run` invocation.
```
$ hyperfine --warmup 2 \
"uv run --no-dev python -c \"print('hi')\"" \
"uv-profiling run --no-dev python -c \"print('hi')\""
Benchmark 1: uv run --no-dev python -c "print('hi')"
Time (mean ± σ): 492.6 ms ± 7.0 ms [User: 393.2 ms, System: 97.1 ms]
Range (min … max): 482.3 ms … 501.5 ms 10 runs
Benchmark 2: uv-profiling run --no-dev python -c "print('hi')"
Time (mean ± σ): 129.7 ms ± 2.5 ms [User: 105.4 ms, System: 23.2 ms]
Range (min … max): 126.0 ms … 136.1 ms 22 runs
Summary
uv-profiling run --no-dev python -c "print('hi')" ran
3.80 ± 0.09 times faster than uv run --no-dev python -c "print('hi')"
```
The profile after those change below. We still spend a large chunk in
toml parsing (both `uv.lock` and `pyproject.toml`), but it's not
excessive anymore.

Adds a new optional key `auth-policy` to `[tool.uv.index]` that sets the
authentication policy for the index URL.
The default is `"auto"`, which attempts to authenticate when necessary.
`"always"` always attempts to authenticate and fails if the endpoint is
unauthenticated. `"never"` never attempts to authenticate.
These policy address two kinds of cases:
* Some indexes don’t fail on unauthenticated requests; instead they just
forward to the public PyPI. This can leave the user confused as to why
their package is missing. The "always" policy prevents this.
* "never" allows users to ensure their credentials couldn't be leaked to
an unexpected index, though it will only allow for successful requests
on an index that doesn't require credentials.
Closes#11600
In the publish client, we have to set the client retries to 0 as the
retry middleware is incompatible with upload bodies. This however also
sets `client.retry_policy()` to a zero-retry policy, so we need to
construct our own policy.
Fixes#12027
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
This PR moves functions for finding user- and system-level config
directories to public functions in `uv_fs::config`. This will allow them
to be used in future work without duplicating code.
This adds support for inferring dependency group conflict sets from the
directly defined conflicts in configuration. For example, if you declare
a conflict between groups `alpha` and `beta` and `dev` includes `beta`,
then we will infer a conflict between `dev` and `alpha`. We will also
handle a conflict between two groups if they transitively include groups
that conflict with each other. See #11232 for more details.
Closes#11232
## Summary
<!-- What's the purpose of the change? What does it do, and why? -->
The way the `tool update-shell` checks if the command to export the PATH
exists or not in the RC files is a blind search, and therefore if finds
the command inside comments.
example with .zshenv
This content
```
# uv
# export PATH="/Users/cholas/.local/bin:$PATH"
```
Generates the following msg
```
error: The executable directory /Users/cholas/.local/bin is not in PATH, but the Zsh configuration files are already up-to-date
```
With this change, that content won't be considered as configured and the
following will be added
```
# uv
export PATH="/Users/cholas/.local/bin:$PATH"
```
This will make the `update-shell` more reliable
## Test Plan
I tested with and without the change with commented export in zsh in
mac. Tested running `cargo run -- tool update-shell`
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
I somehow missed running an actual integration test of the PEP 517 API
in CI and the python shim was using the old uv CLI interface still.
The tests include pip, uv and `python -m build`. They must be a in CI
job since we can't depend on the Python package in the Rust tests (we
only get the binary in `cargo test`, not the `uv_build` wheel).
## Summary
The order here is slightly off... As-is, we fetch the metadata for the
dependency, _then_ insert the URLs and indexes into the fork state -- so
the fetch doesn't take the explicit index or URL into account. This has
mostly been unobserved because we re-fetch anyway in the next request,
but if we do things in the right order (add to fork state, fetch
dependencies, insert dependencies), we can cut down on the fetches.
Closes https://github.com/astral-sh/uv/issues/12056.
Thank you for uv, it has game-changer capabilities in the field of
Python package and environment maangement!
## Summary
This is a small PR adding the option `module-name`
(`tool.uv.build-backend.module-name`) to the uv build backend (
https://github.com/astral-sh/uv/issues/8779 ).
Currently, the uv build backend will assume that the module name matches
the (dash to underdash-transformed) package name. In some packaging
scenarios this is not the case, and currently there exists no
possibility to override it, which this PR addresses.
From the main issue ( https://github.com/astral-sh/uv/issues/8779 ) I
could not tell if there is any extensive roadmap or plans how to
implement more complex scenarios, hence this PR as a suggestion for a
small feature with a big impact for certain scenarios.
I am new to Rust, I hope the borrow/reference usage is correct.
## Test Plan
So far I tested this at an example, if desired I can look into extending
the tests.
Fixes#11428
---------
Co-authored-by: konstin <konstin@mailbox.org>
This PR is in support of #12005, where we need to import
`DependencyGroups` in the `uv-pypi-types` crate without a circular
dependency on `uv-workspace`.
uv itself is a large package with many dependencies and lots of
features. To build a package using the uv build backend, you shouldn't
have to download and install the entirety of uv. For platform where we
don't provide wheels, it should be possible and fast to compile the uv
build backend. To that end, we're introducing a python package that
contains a trimmed down version of uv that only contains the build
backend, with a minimal dependency tree in rust.
The `uv_build` package is publish from CI just like uv itself. It is
part of the workspace, but has much less dependencies for its own
binary. We're using cargo deny to enforce that the network stack is not
part of the dependencies. A new build profile ensure we're getting the
minimum possible binary size for a rust binary.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
In https://github.com/astral-sh/uv/issues/11998, a user is attempting to
vendor `pydantic-core`. But when they add `pydantic-core = { path =
"src/foo/vendor/pydantic-core" } `, we're installing it as a virtual
package, since `pydantic-core/pyproject.toml` contains `package =
false`.
This PR allows users to mark dependencies as "explicitly a package" or
"explicitly not a package" (i.e., virtual), as a workaround.
Closes https://github.com/astral-sh/uv/issues/11998.
## Summary
This PR adds support for `SitePackages::satisfies` with unnamed
overrides and requirements.
The main challenge here was cases like: you have a `requirements.in`
with `git+https://github.com/pallets/flask` in it, and an
`overrides.txt` with `flask==2.0.0` in it. You _need_ to include
`flask==2.0.0`, but you can't know that without resolving the unnamed
URL requirement (since overrides only take effect when the package is
included, like constraints).
We now make the assumption that any unnamed overrides _are_ relevant,
for the purpose of the satisfies check. This is conservative, but this
whole check is an optimization anyway.
## Summary
Closes https://github.com/astral-sh/uv/issues/9151
This adds support for running .ps1, .cmd, .bat legacy scripts typically
provided by setuptools [legacy script
files](https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#scripts).
Note, .bat and .cmd scripts were somewhat supported previously by
[Command](https://doc.rust-lang.org/std/process/index.html#batch-file-special-handling)
when the extension was explicit but documentation says such behavior
should not be relied upon.
In addition, when no extension is provided and a legacy script exists,
it will try to infer the appropriate extension on Windows and use the
right runtime with preference for .ps1. Only powershell.exe and cmd.exe
are supported right now.
## Test Plan
Added tests. Tested with nuitka locally via uv run.
Note uvx support will be added in a follow up.
## Summary
Small omission I noticed last night. This was overly strict (so, didn't
lead to any incorrect behavior; more that we did unnecessary work in
some cases).
<!--
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? -->
Similar to https://github.com/astral-sh/uv/pull/11399
This adds `UV_NO_BUILD` and `UV_NO_BUILD_PACKAGE` environment variables
for non-pip commands.
## Test Plan
<!-- How was it tested? -->
Tested manually and with snapshot tests.
Fixes#11963
Signed-off-by: Alex Lowe <alex@lowe.dev>
## Summary
If we're looking at (e.g.) `python3.12`, and we have a `requires-python:
">=3.12.7, <3.13"`, then checking if the range includes `3.12` will
return `false`. Instead, we need to look at the lower- and upper-bound
major-minors of the `requires-python`.
Closes https://github.com/astral-sh/uv/issues/11825.
We prepend the interpreter discovery in a temporary path to `sys.path`,
which we have to strip to avoid the `sys.path` value containing a
then-deleted temp dir.
If we see `uvx script.py`, we exit early, giving a hint to use `uv run
script.py` if the script exists. If it does not exist, we suggest
running `uv run` with a normalized package name.
This PR includes a snapshot test for each of these scenarios.
An alternative approach would be to wait until we encounter an error,
and then add the hint. But if there happens to be a malicious package
called `script-py`, this would be run unintentionally (a point raised by
@zanieb).
Closes#10784
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
<!--
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
Fix error message suggesting `--user` instead of `--username`:
```
> uv publish --publish-url ... ... --password $(cat ~/.token)
Publishing 1 file to ...
error: Attempted to publish with a password, but no username. Either provide a username with `--user` (`UV_PUBLISH_USERNAME`), or use `--token` (`UV_PUBLISH_TOKEN`) instead of a password.
> uv publish --publish-url ... ... --user lewis --password $(cat ~/.token)
error: unexpected argument '--user' found
tip: a similar argument exists: '--username'
Usage: uv publish <FILES|--index <INDEX>|--username <USERNAME>|--password <PASSWORD>|--token <TOKEN>|--trusted-publishing <TRUSTED_PUBLISHING>|--keyring-provider <KEYRING_PROVIDER>|--publish-url <PUBLISH_URL>|--check-url <CHECK_URL>|--skip-existing>
For more information, try '--help'.
```
## Test Plan
I have not tested manually, I'm hoping this isn't necessary and there
will be sufficient CI coverage.
<!--
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? -->
## Test Plan
<!-- How was it tested? -->
## Summary
I believe `zlib-rs` is now a better choice on ARM and x86, so I'm just
going to assume it's a better choice everywhere. It's much easier to
build (removes our CMake dependency), and in my benchmarking, it's
substantially faster on ARM and faster or ~exactly even on my x86
Windows machine.
We migrated to `zlib-rs` once before (#9184); however, I later reverted
it as I learned that they were only doing compile-time feature
detection, and so `zlib-rs` was meaningfully slower on x86. They now
perform runtime feature detection:
https://trifectatech.org/blog/zlib-rs-is-faster-than-c/.
To benchmark, I wrote a script to create a local Simple API-compliant
registry (see the commit history) for a single package. Then I ran the
`install-cold` benchmark against that registry to install NumPy.
On ARM:
```
❯ uv run resolver --uv-pip-path ../../zlib-ng --uv-pip-path ../../zlib-rs \
--benchmark install-cold \
req.txt --warmup 10 --min-runs 30
Benchmark 1: ../../zlib-ng (install-cold)
Time (mean ± σ): 165.7 ms ± 34.7 ms [User: 64.4 ms, System: 93.2 ms]
Range (min … max): 141.8 ms … 293.2 ms 30 runs
Benchmark 2: ../../zlib-rs (install-cold)
Time (mean ± σ): 150.9 ms ± 16.2 ms [User: 57.4 ms, System: 86.4 ms]
Range (min … max): 135.3 ms … 202.4 ms 30 runs
Summary
../../zlib-rs (install-cold) ran
1.10 ± 0.26 times faster than ../../zlib-ng (install-cold)
```
I benchmarked this about 100 times on my Windows machine and found it
difficult to conclude anything beyond "They're nearly the same". Here's
an example:
```
PS C:\Users\crmar\workspace\puffin> hyperfine --prepare "uv venv" "zlib-rs.exe pip sync ./scripts/benchmark/req.txt" "zlib-ng.exe pip sync ./scripts/benchmark/req.txt" "zlib-rs.exe pip sync ./scripts/benchmark/req.txt" "zlib-ng.exe pip sync ./scripts/benchmark/req.txt" --runs 10 --warmup 5
Benchmark 1: zlib-rs.exe pip sync ./scripts/benchmark/req.txt
Time (mean ± σ): 240.6 ms ± 10.8 ms [User: 6.1 ms, System: 92.2 ms]
Range (min … max): 229.4 ms … 267.9 ms 10 runs
Benchmark 2: zlib-ng.exe pip sync ./scripts/benchmark/req.txt
Time (mean ± σ): 241.3 ms ± 6.2 ms [User: 7.7 ms, System: 90.6 ms]
Range (min … max): 233.9 ms … 252.1 ms 10 runs
Benchmark 3: zlib-rs.exe pip sync ./scripts/benchmark/req.txt
Time (mean ± σ): 242.8 ms ± 7.7 ms [User: 6.2 ms, System: 23.4 ms]
Range (min … max): 236.1 ms … 262.8 ms 10 runs
Benchmark 4: zlib-ng.exe pip sync ./scripts/benchmark/req.txt
Time (mean ± σ): 245.9 ms ± 5.7 ms [User: 1.5 ms, System: 59.4 ms]
Range (min … max): 240.9 ms … 257.3 ms 10 runs
Summary
zlib-rs.exe pip sync ./scripts/benchmark/req.txt ran
1.00 ± 0.05 times faster than zlib-ng.exe pip sync ./scripts/benchmark/req.txt
1.01 ± 0.06 times faster than zlib-rs.exe pip sync ./scripts/benchmark/req.txt
1.02 ± 0.05 times faster than zlib-ng.exe pip sync ./scripts/benchmark/req.txt
```
Closes#11885.
At certain points in the code, dependency groups are represented by
`DevGroups*` naming, probably as a historical artifact. This PR updates
the naming.
This includes renaming `uv-configuration/src/dev.rs` to
`uv-configuration/src/dependency_groups.rs`.
<!--
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
Follow on to #11706. In the original PR, I tried to solve the issue by
getting rid of the `ctrlc::set_handler` call. Unfortunately, this didn't
work on windows due to an issue with the console crate. console 0.15.11
includes https://github.com/console-rs/console/pull/235, which resolves
the issue, so now we can get rid of the call.
<!-- What's the purpose of the change? What does it do, and why? -->
This change is not super important but I still think it's worthwhile.
For one, spinning up a background thread to handle `SIGINT`s when we're
going to be raising the `SIGINT` from within the function is more
technical complexity than needed, now that there's an easy way to
explicitly catch the Ctrl-C from the terminal input. Secondly,
`ctrlc::set_handler`'s
[docs](https://docs.rs/ctrlc/3.4.5/ctrlc/fn.set_handler.html) advise
that you set the handler just once, at the beginning of the program, so
this use seems somewhat error prone. In fact, uv already has a second
[callsite](461f4d9007/crates/uv/src/commands/project/add.rs (L596-L611))
for this function (though I'm not sure if the two callsites could
currently ever both occur on the same run of uv)
## Test Plan
I've tested this manually on linux (WSL ubuntu) and windows, though not
on aarch64-apple-darwin as I don't have a machine running that. I would
appreciate if someone would double-check that it works on such machines.
As discussed in the original PR, this change is pretty hard to test due
to the fact that the behavior only occurs if stderr is connected to a
tty. I experimented with using pseudoterminals to test this but it's
still quite tricky due to the lack of x-platform non-blocking reads on
the pty.
<!-- How was it tested? -->
Fixes#11217
By default, a 64-bit uv does not see a 32-bit global (HKLM) installation
of Python in the registry
(https://github.com/astral-sh/uv/issues/11217). To work around this, we
manually request both 32-bit and 64-bit access using registry access
flags (https://peps.python.org/pep-0514/#sample-code). The flags have no
effect on 32-bit (https://stackoverflow.com/a/12796797/3549270).
This effect is that there is an asymmetry between discovery modes: For
the registry-based discovery using PEP 514, we discover both 32-bit and
64-bit Pythons, while for managed installations, we are stricter and
only discover those matching in bit-ness.
I tested this manually with an additional 32-bit installation of CPython
on a 64-bit machine and windows with 32-bit and 64-bit (x86_64 and i686)
builds of uv.
Three edition 2021 compatible sets of changes in preparation for the
edition 2025 split out from #11724.
In edition 2025, `gen` is a keyword, so we escape it as `r#gen`. `ref`
and `ref mut` are not allowed anymore for `&T` and `&mut T`, so we
remove them. `cargo fmt` now formats inside of macros, which the 2021
formatter doesn't undo.
## Summary
Testing with `UV_PYTHON_INSTALL_DIR` environment variable has some
problems. This PR fix them.
- `UV_PYTHON_INSTALL_DIR` must be an absolute path.
- Cargo tries to find Python executables from each crates in test. If it
is relative path, cargo searches in different directories for each
tests.
- Skip the test asserting help messages.
- Clap shows the current value of the environment variables. If
`UV_PYTHON_INSTALL_DIR` is set, the test fails.
## Test Plan
<!-- How was it tested? -->
All tests pass with
`UV_PYTHON_INSTALL_DIR=/path/to/my/home/uv/target/testpython`.
## Summary
In. https://github.com/astral-sh/uv/issues/11857, we had a case of a
user that was seeing incorrect resolution results after upgrading to a
newer version of macOS, since we retained cache information about the
interpreter. This PR adds the OS name and version to the cache key for
the interpreter. This seems to be extremely cheap, and it's nice to make
this robust so that users don't run into the same confusion in the
future.
Closes https://github.com/astral-sh/uv/issues/11857.
## Summary
We need to decouple the "Is this shell supported by `update-shell`?"
logic from the "Does this shell have known configuration files?" logic,
specifically for Windows, which we can always update but not via
configuration files.
Closes https://github.com/astral-sh/uv/issues/11803.
## Summary
We use a similar strategy to the ephemeral overlay: set
`include-system-site-packages` in the `pyvenv.cfg`, and clear it
whenever we access a new environment.
Closes https://github.com/astral-sh/uv/issues/11829.
## Test Plan
Difficult to test because we don't really have support for system
packages in our test infrastructure. But...
```
> uv venv --system-site-packages
> ['', '/Users/crmarsh/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python313.zip', '/Users/crmarsh/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python3.13', '/Users/crmarsh/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python3.13/lib-dynload', '/Users/crmarsh/.cache/uv/archive-v0/AhKcORkaCdbBl31VweRtG/lib/python3.13/site-packages', '/Users/crmarsh/workspace/uv/foo/.venv/lib/python3.13/site-packages', '/Users/crmarsh/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python3.13/site-packages']
```
```
> uv venv
> ['', '/Users/crmarsh/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python313.zip', '/Users/crmarsh/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python3.13', '/Users/crmarsh/.local/share/uv/python/cpython-3.13.0-macos-aarch64-none/lib/python3.13/lib-dynload', '/Users/crmarsh/.cache/uv/archive-v0/AhKcORkaCdbBl31VweRtG/lib/python3.13/site-packages', '/Users/crmarsh/workspace/uv/foo/.venv/lib/python3.13/site-packages']
```
Reworks how log verbosity flags work.
* `<no argument>` is the same, equivalent to `RUST_LOG=off`
* `-v` is the same, equivalent to `RUST_LOG=uv=debug`
* `-vv` is now equivalent to `RUST_LOG=uv=trace` (previously it only
enabled more log message context)
* `-vvv` is now equivalent to `RUST_LOG=trace` (previously it was
equivalent to `-vv`)
The "more context" that `-vv` had has been moved to an orthogonal
setting via an environment variable. Setting `UV_LOG_CONTEXT=1` will add
the extra context that `-vv` did.
In the future we may make these more granular as we try to use
`info!/warn!` more.
Fixes#1569
Fixes#11793
On Windows, trying to read a file inside what is not a directory but
another file results in a not found error, while on Unix we get a not a
directory error. We check explicitly if something included in a
workspace glob is a non-directory to fix the behavior on Windows.
Three network settings are always passed together (though in random
method parameter orders). I factored these out into a struct to make planned future
changes easier.
<!--
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
This pull request introduces validation for unique index names in the
`tool.uv.index` field and adds corresponding tests to ensure the
functionality. The most important changes include adding a custom
deserializer function, updating the `ToolUv` struct to use the new
deserializer, and adding tests to verify the behavior.
Validation and deserialization:
*
[`crates/uv-workspace/src/pyproject.rs`](diffhunk://#diff-e12cd255985adfd45ab06f398cb420d2f543841ccbeea4175ccf827aa9215b9dR283-R311):
Added a custom deserializer function `deserialize_index_vec` to validate
that index names in the `tool.uv.index` field are unique.
*
[`crates/uv-workspace/src/pyproject.rs`](diffhunk://#diff-e12cd255985adfd45ab06f398cb420d2f543841ccbeea4175ccf827aa9215b9dR374):
Updated the `ToolUv` struct to use the `deserialize_index_vec` function
for the `index` field.
Testing:
*
[`crates/uv/tests/it/lock.rs`](diffhunk://#diff-82edd36151736f44055f699a34c8b19a63ffc4cf3c86bf5fb34d69f8ac88a957R15336):
Added a test `lock_repeat_named_index` to verify that duplicate index
names result in an error.
[[1]](diffhunk://#diff-82edd36151736f44055f699a34c8b19a63ffc4cf3c86bf5fb34d69f8ac88a957R15336)
[[2]](diffhunk://#diff-82edd36151736f44055f699a34c8b19a63ffc4cf3c86bf5fb34d69f8ac88a957R15360-R15402)
*
[`crates/uv/tests/it/lock.rs`](diffhunk://#diff-82edd36151736f44055f699a34c8b19a63ffc4cf3c86bf5fb34d69f8ac88a957R15360-R15402):
Added a test `lock_unique_named_index` to verify that unique index names
result in successful lock file generation.
Schema update:
*
[`uv.schema.json`](diffhunk://#diff-c669473b258a19ba6d3557d0369126773b68b27171989f265333a77bc5cb935bR205):
Updated the schema to set the default value of the `index` field to
`null`.
Fixes#11804
## Test Plan
### Steps to reproduce and verify the fix:
1. Clone the repository and checkout the feature branch
```bash
git clone https://github.com/astral-sh/uv.git
cd uv
git checkout feature/warn-duplicate-index-names
```
2. Build the modified binary
```bash
cargo build
```
3. Create a test project using the system installed uv
```bash
uv init uv-test
cd uv-test
```
4. Manually edit pyproject.toml to add duplicate index names
```toml
[[tool.uv.index]]
name = "alpha_b"
url = "<omitted>"
[[tool.uv.index]]
name = "alpha_b"
url = "<omitted>"
```
5. Try to add a package using the modified binary
```bash
../target/debug/uv add numpy
```
### Results
Before: use release binary

After: use self build binary

Now when attempting to use a pyproject.toml with duplicate index names,
the modified binary correctly detects the issue and produces an error
message:
```
error: Failed to parse: `pyproject.toml`
Caused by: TOML parse error at line 9, column 1
|
9 | [[tool.uv.index]]
| ^^^^^^^^^^^^^^^^^
duplicate index name `alpha_b`
```
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
<!--
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
Follow up for https://github.com/astral-sh/uv/pull/11738
I missed this while reviewing the truncation changes.
`format!("{:.N}", value)` only truncates if the `fmt::Display`
implementation supports it (by reading `f.precision()` in trait
implementation).
So in our case `format!("{:.N}", version.to_string())` will work but not
`format!("{:.N}", version)` unless `Version` supports it.
Since we only need it once, I am just truncating after the string is
created.
## Test Plan
<!-- How was it tested? -->
<!--
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
Closes#2410
<!-- What's the purpose of the change? What does it do, and why? -->
This changes the name of files in `wheels` bucket to use a hash instead
of the wheel name as to not exceed maximum file length limit on various
systems.
This only addresses the primary concern of #2410. It still does _not_
address:
- Path limit of 260 on windows:
https://github.com/astral-sh/uv/issues/2410#issuecomment-2062020882
To solve this we need to opt-in to longer path limits on windows
([ref](https://github.com/astral-sh/uv/issues/2410#issuecomment-2150532658)),
but I think that is a separate issue and should be a separate MR.
- Exceeding filename limit while building a wheel from source
distribution
As per my understanding, this is out of uv's control. Name of the output
wheel will be decided by build-backend used by the project. For wheels
built from source distribution, pip also uses the wheel names in cache.
So I have not touched `sdists` cache.
I have added a `filename: WheelFileName` field in `Archive`, so we can
use it while indexing instead of relying on the filename on disk.
Another way to do this was to read `.dist-info/WHEEL` and
`.dist-info/METADATA` and build `WheelFileName` but that seems less
robust and will be slower.
## Test Plan
<!-- How was it tested? -->
Tested by installing `yt-dlp`, `httpie` and `sqlalchemy` and verifying
that cache files in `wheels` bucket use hash.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Currently, we're using both the official `windows-*` with
`windows-registry` crates as well as `winreg`, an older,
community-maintained crate.
To unify the codebase, we follow the lead of rustup that already
performed this migration
(bce3ed67d2).
This is also a prerequisite to unblock the unification of the
windows-sys crate versions.
I've manually tested that `uv tool update-shell` works for adding to
PATH and correctly detects when PATH was already added.
<!--
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? -->
Resolves#11704
Propagate errors from `uv_console::confirm` up instead of `unwrap`ping
them, causing panics.
## Test Plan
<!-- How was it tested? -->
Regression testing the bug is very difficult, as the behavior of
`confirm` changes based on whether `uv` is talking to a `tty`. We can
trick it using ptys, but the best rust pty crate I could find only
provides blocking reads of the spawned child, which is insufficient to
write the regression test.
---------
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
The animation shadows any interactive authentication prompt which may
occur when resolving dependencies of private repos.
Fixes https://github.com/astral-sh/uv/issues/5107.
## Test Plan
I started creating `install_git_private_https_interactive` as a
regression test but am unsure how to test this because it is interactive
and I don't really know the test framework
## Summary
This is the pattern I see in a variety of crates, and I believe this is
preferred if you don't _need_ an owned `String`, since you can avoid the
allocation. This could be pretty impactful for us?
## Summary
Since we use `SmallString` internally, there's no benefit to passing an
owned string to the `PackageName` constructor (same goes for
`ExtraName`, etc.). I've kept them for now (maybe that will change in
the future, so it's useful to have clients passed own values if they
_can_), but removed a bunch of usages where we were casting from `&str`
to `String` needlessly to use the constructor.
<!--
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
I noticed that when running "uv build --all-packages" in an empty
workspace with no buildable packages, it reports that there are
buildable packages. Which I believe to be an error in the message. This
patch fixes the typo. I did not find any relevant issues.
## Test Plan
I've verified, to the best of my ability, that this did not introduce
any additional errors in related existing tests. Considering the nature
of the change I believe it's sufficient.
## Summary
Like `uv add --script`, `uv lock --script` will now initialize a PEP 723
script tag if it doesn't already exist.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
* Upgrade the rust toolchain to 1.85.0. This does not increase the MSRV.
* Update windows trampoline to 1.86 nightly beta (previously in 1.85
nightly beta).
## Test Plan
Existing tests
## Summary
Likely not critical since these tend to run prior to resolution rather
than in parallel with it, but we _should_ respect in-flight requests
here.
According to the [UV
documentation](https://docs.astral.sh/uv/configuration/files/#env), the
UV_ENV_FILE environment variable should support multiple .env files,
separated by spaces. However, when I tried using this feature in my
repository, it didn’t work as expected.
To investigate, I checked the UV repository for relevant tests and found
`run_with_multiple_env_files`.
This test asserts the following `error: No environment file found at:
.env1 .env2.`
This discrepancy could indicate either a mismatch between the
documentation and the implementation or a bug in the code.
I decided to fix the issue in the code since the ability to pass
multiple `.env` files is a valuable feature.
If my fix isn’t appropriate, I’d be happy to make any necessary
adjustments.
---------
Co-authored-by: Yaroslav Limanskiy <yaroslav.limanskiy@pandadoc.com>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
I believe these are not necessary... They're currently used in two
places:
1. When building wheels. But that's already wrapped in an in-flight map,
which does the same thing.
2. When fetching source distribution metadata. But every route there
uses it's own `flock` to coordinate across processes, so this seems
redundant?
In #6827, we switched the uv-dev binary to not being built by default.
As an unintended side effect, we were also stopping to run the tests
that ensured the schema was up-to-date.
To fix this, we split uv-dev into an unconditional library, with only
the binary being a conditional build. This way, `cargo test` and `cargo
nextest` pick those tests up again.
An alternative would be running tests with the `dev` feature, with the
side effect of always building the uv-dev binary, too.
## Summary
As-is, we used the refined interpreter to _resolve_, but we then created
the tool environment with the "old" interpreter. So we risked running
(e.g.) code that requires Python 3.12 in a Python 3.10 environment. We
need to propagate the updated interpreter.
This is fairly hard to test, because it requires an environment in which
we're able to download new interpreters.
Closes
https://github.com/astral-sh/uv/issues/11678#issuecomment-2672659074.
Currently, `uv tool list --show-paths` will show backslashes as path
separators for packages but not entrypoints. This PR changes this to be
consistent.
Closes#10426.
## Summary
Today, if you have a lockfile that includes conflict markers, we write
those markers out to `requirements.txt` in `uv export`. This is
problematic, since no tool will ever evaluate those markers correctly
downstream.
This PR adds handling for the conflict markers, though it's quite
involved. Specifically, we have a new reachability algorithm that
tracks, for each node, the reachable marker for that node _and_ the
marker conditions under which each conflict item is `true` (at that
node).
I'm slightly worried that this algorithm could be wrong for graphs with
cycles, but we only use this logic for lockfiles with conflicts anyway,
so I think it's a strict improvement over the status quo.
Closes https://github.com/astral-sh/uv/issues/11559.
Closes https://github.com/astral-sh/uv/issues/11548.
## Summary
We need to compute the set of activated groups prior to evaluating the
conflict markers on the groups' dependencies.
Closes https://github.com/astral-sh/uv/issues/11648.
Solving spent a chunk of its time just converting resolutions, the left
two blocks:

These blocks are `ResolverOutput::from_state` with 1.3% and
`ForkState::into_resolution` with 4.1% of resolver thread runtime for
apache airflow universal.
We reduce the overhead spent in those functions, to now 1.1% and 2.1% of
resolver time spend in those functions by:
Commit 1: Replace the hash set for the edges with a vec in
`ForkState::into_resolution`. We deduplicate edges anyway when
collecting them, and the hash-and-insert was slow.
Commit 2: Reduce the distribution clonign in
`ResolverOutput::from_state` by using an `Arc`.
The same profile excerpt for the resolver with the branch (note that
there is now an unrelated block between the two we optimized):

Wall times are noisy, but the profiles show those changes as
improvements.
```
$ hyperfine --warmup 2 "./uv-main pip compile --no-progress scripts/requirements/airflow.in --universal" "./uv-branch pip compile --no-progress scripts/requirements/airflow.in --universal"
Benchmark 1: ./uv-main pip compile --no-progress scripts/requirements/airflow.in --universal
Time (mean ± σ): 99.1 ms ± 3.8 ms [User: 111.8 ms, System: 115.5 ms]
Range (min … max): 93.6 ms … 110.4 ms 29 runs
Benchmark 2: ./uv-branch pip compile --no-progress scripts/requirements/airflow.in --universal
Time (mean ± σ): 97.1 ms ± 4.3 ms [User: 114.8 ms, System: 112.0 ms]
Range (min … max): 90.9 ms … 112.4 ms 29 runs
Summary
./uv-branch pip compile --no-progress scripts/requirements/airflow.in --universal ran
1.02 ± 0.06 times faster than ./uv-main pip compile --no-progress scripts/requirements/airflow.in --universal
```
Instead of always using all available threads for bytecode compilation,
respect `UV_CONCURRENT_INSTALLS`, so the parallelism is configurable
instead of hardcoded. We reuse the install limit since bytecode
compilation only runs after install.
Revert #11601 for now
We run Python interpreter discovery with `-I` (#2500) which means these
environments variables are ignored when determining `sys.path`. Unless
we decide to remove the `-I` flag from the `sys.path` query, we
shouldn't release these changes to interpreter discovery caching.
I noticed that the latest two `sync-python-releases` jobs failed due to
`httpx.RemoteProtocolError: peer closed connection without sending
complete message body (incomplete chunked read)`.
For the current python-build-standalone release, each request page
(defaulting to 30 items per page) takes about 20 seconds and loads
around 32MB of data. This extensive data load might be causing the
request to frequently fail.
In this PR, I reduced number of items per page to 10 and added
`Accept-Encoding: gzip, deflate` to the request header. Now, it takes
about 6 seconds to load, and the compressed response size has been
reduced to 534KB. I hope this would addresses the request failure.
We want to use `sys.path` for package discovery (#2500, #9849). For
that, we need to know the correct value of `sys.path`. `sys.path` is a
runtime-changeable value, which gets influenced from a lot of different
sources: Environment variables, CLI arguments, `.pth` files with
scripting, `sys.path.append()` at runtime, a distributor patching
Python, etc. We cannot capture them all accurately, especially since
it's possible to change `sys.path` mid-execution. Instead, we do a best
effort attempt at matching the user's expectation.
The assumption is that package installation generally happens in venv
site-packages, system/user site-packages (including pypy shipping
packages with std), and `PYTHONPATH`. Specifically, we reuse
`PYTHONPATH` as dedicated way for users to tell uv to include specific
directories in package discovery.
A common way to influence `sys.path` that is not using venvs is setting
`PYTHONPATH`. To support this we're capturing `PYTHONPATH` as part of
the cache invalidation, i.e. we refresh the interpreter metadata if it
changed. For completeness, we're also capturing other environment
variables documented as influencing `sys.path` or other fields in the
interpreter info.
This PR does not include reading registry values for `sys.path`
additions on Windows as documented in
https://docs.python.org/3.11/using/windows.html#finding-modules. It
notably also does not include parsing of python CLI arguments, we only
consider their environment variable versions for package installation
and listing. We could try parsing CLI flags in `uv run python`, but we'd
still miss them when Python is launched indirectly through a script, and
it's more consistent to only consider uv's own arguments and environment
variables, similar to uv's behavior in other places.
This change keeps dependency group keys sorted when adding new ones.
If earlier dependency group keys were not sorted, we just append the new
group key to avoid churn in `pyproject.toml`. See discussion on #11447.
I've added a new snapshot test to capture this case.
Closes#11447.
## Summary
When tests are run downstream, the `COLUMNS` environment variable is
used to force fixed output width and avoid test failures due to
different terminal widths. However, this occasionally causes test
regressions when other tests rely on different output width. Use the
same `COLUMNS` value in CI to ensure consistent output and catch any
regressions.
## Test Plan
It wasn't, it's supposed to be tested by the CI :-).
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
The particular example I honed in on here was the `e3nn -> sympy 1.13.1`
and `e3nn -> sympy 1.13.3` dependency edges. In particular, while the
former correctly has a conflict marker, the latter's conflict marker was
getting simplified to `true`. This makes the edges trivially
overlapping, and results in both of them getting installed
simultaneously. (A similar problem happens for the `e3nn -> torch`
dependency edges.)
Why does this happen? Well, conflict marker simplification works by
detecting which extras are known to be enabled (and disabled) for each
node in the graph. This ends up being expressed as a set of sets, where
each inner set contains items corresponding to "extras is included" or
"extra is excluded."
The logic then is if _all_ of these sets are satisfied by the conflict
marker on the dependency edge, then this conflict marker can be
simplified by assuming all of the inclusions/exclusions to be true.
In this particular case, we run into an issue where the set of
assumptions discovered for `e3nn` is:
{test[sevennet]}, {}, {~test[m3gnet], ~test[alignn], test[all]}
And the corresponding conflict marker for `e3nn -> sympy 1.13.1` is:
extra == 'extra-4-test-all'
or extra == 'extra-4-test-chgnet'
or (extra != 'extra-4-test-alignn' and extra != 'extra-4-test-m3gnet')
And the conflict marker for `e3nn -> sympy 1.13.3` is:
extra == 'extra-4-test-alignn' or extra == 'extra-4-test-m3gnet'
Evaluating each of the sets above for `sympy 1.13.1`'s conflict
marker results in them all being true. Simplifying in turn results in
the marker being true. For `sympy 1.13.3`, not all of the sets are
satisfied, so this marker is not simplified.
I think the fundamental problem here is that our inferences aren't quite
rich enough to make these logical leaps. In particular, the conflict
marker for `e3nn -> sympy 1.13.3` is not satisfied by _any_ of our sets.
One might therefore conclude that this dependency edge is impossible.
But! The `test[sevennet]` set doesn't actually rule out `test[m3gnet]`
from being included, for example, because there is no conflict. So it is
actually possible for this marker to evaluate to true.
And I think this reveals the problem: for the `e3nn -> sympy 1.13.1`
conflict marker, the inferences don't capture the fact that
`test[sevennet]` _might_ have `test[m3gnet]` enabled, and that would in
turn result in the conflict marker evaluating to `false`. This directly
implies that our simplification here is inappropriate.
It would be nice to revisit how we build our inferences here so that
they are richer and enable us to make correct logical leaps. For now, we
fix this particular bug with a bit of a cop-out: we skip conflict marker
simplification when there are ambiguous dependency edges.
Fixes#11479
The place to look in this snapshot is the `name = "e3nn"` dependency.
Its dependencies on `sympy` and `torch` consist of multiple versions
with overlapping conflict markers. They are getting incorrectly
simplified to `true`.
Initially, we were limiting Git schemes to HTTPS and SSH as only
supported schemes. We lost this validation in #3429. This incidentally
allowed file schemes, which apparently work with Git out of the box.
A caveat for this is that in tool.uv.sources, we parse the git field
always as URL. This caused a problem with #11425: repo = { git =
'c:\path\to\repo', rev = "xxxxx" } was parsed as a URL where c: is the
scheme, causing a bad error message down the line.
This PR:
* Puts Git URL validation back in place. It bans everything but HTTPS,
SSH, and file URLs. This could be a breaking change, if users were using
a git transport protocol were not aware of, even though never
intentionally supported.
* Allows file: URL in Git: This seems to be supported by Git and we were
supporting it albeit unintentionally, so it's reasonable to continue to
support it.
* It does not allow relative paths in the git field in tool.uv.sources.
Absolute file URLs are supported, whether we want relative file URLs for
Git too should be discussed separately.
Closes#3429: We reject the input with a proper error message, while
hinting the user towards file:. If there's still desire for relative
path support, we can keep it open.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
Resolves#6913.
Add `tool.uv.build-constraint-dependencies` to pyproject.toml.
The changes are analogous to the constraint-dependencies feature
implemented in #5248.
Add documentation for `build-constraint-dependencies`
## Test Plan
Add tests for `uv lock`, `uv add`, `uv pip install` and `uv pip
compile`.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This typo wasn't caught because the `($arg:expr, false)` macro branch
was never exercised.
For example, prior to this change, if you add
```
show_settings!(globals, false);
```
below, you'll get a compiler error.
When running `uv pip install .` in a directory with a pyproject.toml
that does not configure a build, we will invoke setuptools and get a
wheel we can't parse (https://github.com/astral-sh/uv/issues/11344).
This PR adds warnings around these setups.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
We want to build `uv-build` without depending on the network crates. In
preparation for that, we split uv-git into uv-git and uv-git-types,
where only uv-git depends on reqwest, so that uv-build can use
uv-git-types.
## Summary
This PR fixes a subtle issue arising from our propagation of
preferences. When we resolve a fork, we take the solution from that fork
and mark all the chosen versions as "preferred" as we move on to the
next fork.
In this specific case, the resolver ended up solving a macOS-specific
fork first, which led us to pick `2.6.0` rather than `2.6.0+cpu`. This
in itself is correct; but when we moved on to the next fork, we
preferred `2.6.0` over `2.6.0+cpu`, despite the fact that `2.6.0` _only_
includes macOS wheel, and that branch was focused on Linux.
Now, in preferences, we prefer local variants (if they exist). If the
local variant ends up not working, we'll presumedly backtrack to the
base version anyway.
Closes https://github.com/astral-sh/uv/issues/11406.
## Summary
If the user provides a PEP 508 requirement (e.g., `uvx
change_wheel_version`), then we should us that verbatim for the
executable, rather than normalizing the package name.
Closes https://github.com/astral-sh/uv/issues/11521.
## Summary
This PR revives https://github.com/astral-sh/uv/pull/10017, which might
be viable now that we _don't_ enforce any platforms by default.
The basic idea here is that users can mark certain platforms as required
(empty, by default). When resolving, we ensure that the specified
platforms have wheel coverage, backtracking if not.
For example, to require that we include a version of PyTorch that
supports Intel macOS:
```toml
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["torch>1.13"]
[tool.uv]
required-platforms = [
"sys_platform == 'darwin' and platform_machine == 'x86_64'"
]
```
Other than that, the forking is identical to past iterations of this PR.
This would give users a way to resolve the tail of issues in #9711, but
with manual opt-in to supporting specific platforms.
## Summary
This is an alternative to the approach we took in #11063 whereby we
always included `provides-extra` and `requires-dist`, since we needed
some way to differentiate between "no extras" and "lockfile was
generated by a uv version that didn't include extras".
Instead, this PR adds a minor version (called a "revision") to the
lockfile that we can use to indicate support for this feature. While
lockfile version bumps are backwards-incompatible, older uv versions
_can_ read lockfiles with a later revision -- they just won't understand
all the data.
In a future major version bump, we could simplify things and change the
schema to use a (major, minor) format instead of these two separate
fields. But this is the only way to do it that's backwards-compatible
with existing uv versions.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
Initially it seemed like `app.py` might be slightly more desirable but
people seem to overwhelmingly favour `main.py` as a good "generic" name.
Fixes#7782
Closes#11285
Closes https://github.com/astral-sh/uv/pull/11437
This changes `-p` from an alias of `--python-version` to `--python`
while retaining backwards compatibility for `--python-version`-like
fallback behavior when the requested version, e.g., `-p 3.12`, cannot be
found.
This was initially implemented with a hidden `--python-legacy` flag
which allows us to special case the short `-p` flag — unlike the
implementation in #11437. However, after further discussion, we decided
the behavior difference between `-p` and `--python` would be confusing
so now `-p` is an alias for `--python` and `--python` is special-cased
when a version is used.
Additionally, we now respect the `UV_PYTHON` environment variable, but
it is ignored when `--python-version` is set. If you want different
`--python-version` and `--python` values, you must do so explicitly. I
considered banning this, but it is valid for e.g. `--python pypy
--python-version 3.12`
Unlike https://github.com/astral-sh/uv/pull/10222, this does not respect
`UV_PYTHON` in `uv python uninstall` (continuing to require an explicit
target there) which I think is simpler and matches our `.python-version`
file behavior.
---------
Co-authored-by: Choudhry Abdullah <cabdulla@trinity.edu>
Co-authored-by: Choudhry Abdullah <choudhry347@choudhrys-air-2.trinity.local>
Co-authored-by: Aria Desires <aria.desires@gmail.com>
Closes#10597.
Recreated https://github.com/astral-sh/uv/pull/10925 that got closed as
the base branch got merged.
Snapshot tests.
---------
Co-authored-by: Aria Desires <aria.desires@gmail.com>
`uv publish` has not changed for some time, it has [notable production
usage](https://github.com/search?q=%22uv+publish%22&type=code) and there
are no outstanding blockers, it is time to stabilize it with the 0.6
release.
Publishing is only usable through `uv publish`. You need to build source
distributions and wheels ahead of time, usually with `uv build`.
By default, `uv publish` will upload all source distributions and wheels
in the `dist/` folder, ignoring all non-matching filenames. By default,
`uv build` and most other build frontend write their artifacts to
`dist/`. Together, we can build a publish workflow including a smoke
test that all relevant files have actually been included in the wheel:
```
uv build
uv venv
uv pip install --find-links dist ...
uv run smoke_test.py
uv publish
```
There are 3 options supported in configuration files:
- `tool.uv.publish-url`
- `tool.uv.trusted-publishing`
- `tool.uv.check-url`
Options support on the CLI and through environment variables for index
configuration:
```
--index <INDEX>
The name of an index in the configuration to use for publishing [env: UV_PUBLISH_INDEX=]
--publish-url <PUBLISH_URL>
The URL of the upload endpoint (not the index URL) [env: UV_PUBLISH_URL=]
--check-url <CHECK_URL>
Check an index URL for existing files to skip duplicate uploads [env: UV_PUBLISH_CHECK_URL=]
```
There are two ways to configure `uv publish`: Passing options
individually or using the index API.
For the individual options, there `--publish-url` and `--check-url`, and
their configuration counterparts, `tool.uv.publish_url` and
`tool.uv.check_url`. `--publish-url` is named this way to be clearly
different from the simple index URL, since uploading to the index URL
leads to unclear errors, or worse a 200 OK with no effect. While we
intend to keep supporting this configuration, the index API is better
integrated.
In the index API, the user specifies `[[tool.uv.index]]`, with an index
name, the simple index URL and the publish URL. The `publish-url` and
`url` are equivalent to `--publish-url` and `--check-url`. The `url`
being mandatory makes for a better upload behavior (next paragraph).
```toml
[[tool.uv.index]]
name = "pypi"
url = "https://pypi.org/simple"
publish-url = "https://upload.pypi.org/legacy/"
```
A version of a package contains multiple files, for pure-python packages
usually a source distribution and a wheel, for native packages usually
many, larger wheels and a source distributions. Uploads in the not
officially specified Upload API 1.0 are file based: Once you upload a
file, the version is created, even though most files are still missing.
When uploading a series of files fails in the middle (e.g. the CI server
breaks), the release is only half uploaded. For such cases, you want to
re-try the upload. The response of an index when re-uploading a file is
implementation defined. Notably, PyPI accepts uploads of the same file
again with status 200, but rejects uploads of a file with the same name
but different contents with status 400. Other indexes reject all
attempts at re-uploads with different status codes and messages. Twine
handles this with `--skip-existing`, which allows ignoring errors due to
files with the same name as an existing file being uploaded, however
this does also not error when uploading a file with different contents
but the same name, which indicates a problem with the publish pipeline.
To properly solve this, we need the ability to stage releases: Files of
a version are uploaded to a staging area, and only when all files are
uploaded, we atomically publish the release. When an upload breaks or CI
fails, we can discard or overwrite the staging area and try again. This
will only be properly solved by PEP 694 "Upload 2.0 API for Python
Package Indexes", with unclear progress. For local publishing, it would
also be convenient to be able to check which files exist and what their
hashes are from only the publish URL, so files in the `dist/` folder
from a previous release can be ignored.
In the Upload API 1.0, we need to upload transformed METADATA fields
along with the file as form-data. We currently upload only recognized
metadata fields, where we know how to translate the field name to the
form-data name. This means when a user adds unknown, wrong or future-PEP
metadata we miss it. To me best knowledge no index currently verifies
that the form-data and the METADATA file in the wheel match.
Upload API 2.0 will be an entirely new protocol. It is unclear how we
will decide whether to use Upload API 1.0 or Upload API 2.0 once the
latter is released. Upload API 2.0 will remove the need for a check URL.
This means no changes for `--index`, but `--check-url` will be
incompatible with Upload API 2.0.
Options support on the CLI and through environment variables for
authentication:
```
-u, --username <USERNAME>
The username for the upload [env: UV_PUBLISH_USERNAME=]
-p, --password <PASSWORD>
The password for the upload [env: UV_PUBLISH_PASSWORD=]
-t, --token <TOKEN>
The token for the upload [env: UV_PUBLISH_TOKEN=]
--trusted-publishing <TRUSTED_PUBLISHING>
Configure using trusted publishing through GitHub Actions [possible values: automatic, always,
never]
--keyring-provider <KEYRING_PROVIDER>
Attempt to use `keyring` for authentication for remote requirements files [env:
UV_KEYRING_PROVIDER=] [possible values: disabled, subprocess]
```
We need credentials for the publish URL, and we may need credentials for
the check URL.
We support credentials from environment variables, the CLI, the URL, the
keyring, trusted publishing or a prompt.
The username can come from, in order:
- Mutually exclusive:
- `--username` or `UV_PUBLISH_USERNAME`. The CLI option overrides the
environment variable
- The username field in the publish URL
- If `--token` or `UV_PUBLISH_TOKEN` are used, it is `__token__`. The
CLI option overrides the environment variable
- If trusted publishing is available, it is `__token__`
- (We currently do not read the username from the keyring)
- If stderr is a tty, prompt the user
The password can come from, in order:
- Mutually exclusive:
- `--password` or `UV_PUBLISH_PASSWORD`. The CLI option overrides the
environment variable
- The password field in the publish URL
- If `--token` or `UV_PUBLISH_TOKEN` are used, it is the token value.
The CLI option overrides the environment variable
- If the keyring is enabled, the keyring entry for the URL and username
- If trusted publishing is available, the trusted publishing token
- If stderr is a tty, prompt the user
If no credentials are found, we do a final check in the auth middleware
cache and otherwise error without sending the request.
Trusted publishing is only supported in GitHub Actions. By default, we
try to retrieve a token from it in GitHub Actions (`GITHUB_ACTIONS` is
`true`) but continue even it this fails. Trusted publishing can be
forced with `--trusted-publishing always`, to error on misconfiguration,
or deactivated with `--trusted-publishing never`. The option can also be
configured through `tool.uv.trusted-publishing`.
When `--check-url` or `--index` are used, we may need credentials for
the index URL, too. These are handle separately by the same rules as
using the index anywhere else. The `--keyring-provier` option is however
shared between them, turning the keyring on for either turns it on for
both.
As future option, we could read `UV_INDEX_USERNAME` and
`UV_INDEX_PASSWORD` as fallbacks for the publish credentials
(https://github.com/astral-sh/uv/issues/9845). This however would clash
with prompting: When index credentials and upload credentials are not
the same (they usually should be different, since regular uv operations
should have less privileges than publish), we would then instead of
prompting use the wrong credentials from `UV_INDEX_*` and fail.
A major UX problem is that there is no standard for the username when
using a token (or rather, there is no standard for just sending a token
without a username). PyPI uses `__token__`, Cloudsmith used to use your
username or `token`, but now also supports `__token__`
(https://github.com/astral-sh/uv/issues/8221), while Google Cloud
Artifacts always uses `oauth2accesstoken`
(https://github.com/astral-sh/uv/issues/9778). This means the index
documentation may say you're getting a token for authentication, but you
must not use `--token`, you must instead set username and password. This
is something that we can hopefully fix with Upload API 2.0.
An unsolved problem with the keyring is that you it's best practice to
use publish tokens scoped to projects and store tokens in a secure
location such as the keyring, but the keyring saves a single password
per publish URL and username combination. That means that it can't
natively store separate passwords for publishing multiple packages. The
current hack around this is using the package name as query parameter,
e.g. `https://test.pypi.org/legacy/?astral-test-keyring`, as PyPI
ignores this query parameter. This is however only applicable when
publishing locally and not from CI.
Another problem is that the keyring implementation currently relies on
the `keyring` pypi package, which needs to be installed in PATH together
with its plugins and is comparatively slow. This would be improved by
native keyring support (https://github.com/astral-sh/uv/issues/10867),
with the same caveats such as keyring plugins that shared with the
simple index API.
We currently don't upload attestations (PEP 740). Attestations are an
additional field in the form-data, so we should be able to add them
transparently without any changes to the API, unless we want to add a
switch to deactivate even when trusted publishing is used. See also
https://trailofbits.github.io/are-we-pep740-yet/.
Setuptools is writing an invalid combination of Metadata-Version and
used metadata fields in some cases, which PyPI correctly rejects
(https://github.com/astral-sh/uv/issues/9513).
We set a 15min overall timeout since reqwest is missing a write timeout
option (https://github.com/seanmonstar/reqwest/issues/2403).
https://github.com/astral-sh/uv/issues/8641 and
https://github.com/astral-sh/uv/issues/8774: We build artifact checking
in some capacity. This should be done ideally by the build backend or at
latest as part of `uv build`, doing it as part of publish is too late.
Closes#7839
---
Let me know if i missed anything.
We added this to help with resolving some specific packages, and for
parity with Poetry. But in some cases, this metadata is just wrong, and
at the very least it's unreliable.
Closes https://github.com/astral-sh/uv/issues/8989.
Closes#10945.
Instead of using junctions, we can just write files that contain (as the
file contents) the target path. This requires a little more finesse in
that, as readers, we need to know where to expect these. But it also
means we get to avoid junctions, which have led to a variety of
confusing behaviors. Further, `replace_symlink` should now be on atomic
on Windows.
Closes#11263.
We've never bumped the version of this bucket, and we may never do so...
But it's still incorrect for us to omit it from these serialized structs
in the cache. Specifically, these structs include a pointer into the
archive bucket (namely, the ID). But we don't include the bucket
version! So, in theory, we could end up pointing to archives that don't
match the current bucket version expected in the code.
## Summary
Just a logic issue... If we see a dynamic field that isn't `"version"`,
we end up _not_ propagating the fact that `"version"` is also dynamic.
Closes https://github.com/astral-sh/uv/issues/11460.
<!--
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
Handle potential infinite recursion if `uv run` recursively invokes `uv
run`. This can happen if the shebang line of a script includes `uv run`,
but does not pass `--script`.
Handled by adding a new environment variable `UV_RUN_RECURSION_DEPTH`,
which contains a counter of the number of times that uv run has been
recursively invoked. If unset, it defaults to zero, and each time uv run
starts a subprocess we increment the counter, erroring if the value is
greater than a configurable (but not currently exposed or documented)
threshold.
Closes https://github.com/astral-sh/uv/issues/11220.
## Test Plan
I've added a snapshot test to `uv/crates/uv/tests/it/run` that tests the
end-to-end recursion detection flow. I've currently made it a unix-only
test because I'm not sure offhand how uv run will interact with shebang
lines on windows.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>