Now that uv’s MSRV is Rust 1.94, use Rust 2024 let chains where they
make nested guards clearer in the parser, configuration, and data-model
crates.
This flattens related pattern matches and predicates while preserving
evaluation order, short-circuiting, borrow scopes, and mutation
behavior. Guards involving effects, staged work, or non-obvious
lifetimes remain nested.
## Summary
This is a refactor of `uv_python::implementation::ImplementationName`.
It gets rid of the From impls in favour of just having functions which
returns the long and short names for an implementation. I think this is
just strictly cleaner at the call sites, and is less code. It gets rid
of the `long_names` and `short_names` functions in favour of using the
existing `iter_all`. It uses these changes to centralise a bunch of the
information so the short and long names aren't duplicated in various
locations anymore.
The PR becomes most useful for the centralised environments PR but the
refactor seemed helpful stand-alone and is completely isolated from that
PR.
## Test Plan
Relies on existing coverage.
Now that uv’s MSRV is Rust 1.94, use clearer `Option` and `Result`
predicate helpers where it makes sense.
This replaces `map(...).unwrap_or(...)` probes with APIs such as
`is_some_and`, `is_none_or`, and `is_ok_and`, preserving existing
ownership and `None`/`Err` behavior.
## Summary
While centralising the whole environment, I noticed that this code was
unnecessarily complicated so I deleted it.
Using `remove_dir` instead of `junction::delete` also fixes a bug of
sorts if we were to actually encounter a regular directory symlink on
windows.
`junction::get_target` has this weird quirk where it will produce
non-verbatim long paths. This means that prefix comparisons will work
assuming the prefix isn't a verbatim path (so actually this is a bug
disguised as a feature).
To avoid the opposite issue in this case, it's necessary to verbatim
both paths.
## Test Plan
Existing coverage, some augmentations, and a targeted test for a
verbatim_path idiom.
## Summary
Prior to this change, a Unicode numeric character in a Python version
request could cause variant parsing to panic. The parser treated the
character's byte offset as though the character were one byte long, then
sliced through the middle of it.
This PR restricts variant detection to ASCII digits, matching Python
version syntax. Malformed Unicode numeric requests now return the
existing invalid-version error.
## Test plan
- `cargo test -p uv-python version_request_from_str`
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
Pull in metadata from python-build-standalone 20260610 which includes
CPython 3.13.14 and 3.14.6
Note that this includes the change from #19786
---------
Co-authored-by: Tomasz (Tom) Kramkowski <tom@astral.sh>
## Summary
Consolidate the sequential and parallel Python discovery paths
introduced in #18684.
Both modes now share a single `PythonRequest` dispatcher and
executable-to-installation pipeline. A private `QueryStrategy` selects
either lazy sequential querying or eager parallel querying while
preserving the existing filtering, ordering, and error-handling
behavior.
No prefetching is introduced here. Sequential discovery still defers
reading and traversing `PATH`, advances candidate executables one at a
time, and stops querying after finding an acceptable installation. The
pre-existing eager behavior for explicit file, directory, and
executable-name requests is unchanged.
## Test Plan
- Added parity coverage across every `PythonRequest` variant, using
independent caches and out-of-order query completion to verify ordered
results and non-critical error handling.
- Added regression coverage proving sequential strategy construction
pulls no candidates, one iteration pulls exactly one candidate, later
`PATH` directories are not inspected early, and later interpreters are
not queried after a match.
- Mutation-checked the laziness tests against candidate collection,
eager interpreter querying, and eager `PATH` directory filtering.
- `cargo test -p uv-python`
- `cargo test -p uv --test python python_list --no-fail-fast`
- `cargo test -p uv --test python python_find --no-fail-fast`
- `cargo +stable clippy -p uv-python --tests --no-deps --locked -- -D
warnings`
- `cargo +stable fmt --all -- --check`
#18684 accidentally regressed handling of non-critical errors when path
and directory Python requests are collected in parallel rather than
sequentially.
Currently, Python discovery happens sequentially — which is good most of
the time because uv will lazily exit once it finds a satisfactory
version. However, in `uv python list`, uv needs to query all
interpreters on the system which makes sequential discovery quite slow.
Here, we add parallelization to discovery. Annoyingly, it means a fair
amount of code repetition, but I've done my best to minimize that.
In the long-term, we may want to consolidate these code paths such that
uv can do prefetching during normal discovery, since technically if the
interpreter we want is far down the PATH then laziness does not save us
anything.
```
Benchmark 1: main
Time (mean ± σ): 3.108 s ± 0.026 s [User: 2.290 s, System: 0.386 s]
Range (min … max): 3.072 s … 3.157 s 8 runs
Benchmark 2: branch
Time (mean ± σ): 414.1 ms ± 11.1 ms [User: 2955.2 ms, System: 743.7 ms]
Range (min … max): 398.1 ms … 429.5 ms 8 runs
Summary
branch ran
7.51 ± 0.21 times faster than main
```
## Summary
This expands the usage of global preview in a key area which has
wide-ranging impact.
Key notes:
* uv-dev needs to set up default global preview because of this.
Although it seems like it should technically have been setting it up
earlier?
* A number of tests no longer pass `Preview::default()` but also don't
use the guard. This is because they only call into the preview stuff
when the relevant conda environment variables are present. Which we
assume they're not (otherwise the tests would be invalid for other
reasons). There's really a hermeticity issue here, but I've addressed it
in a separate PR.
## Test Plan
Tests have been modified, but existing coverage should be sufficient.
## Summary
This PR runs [hawk](https://github.com/astral-sh/hawk) over the uv
codebase to:
1. Remove dead code (i.e., code marked as `pub` that isn't called by any
other crates).
2. Reduce visibility of symbols (i.e., to make code non-`pub` if only
called internally, etc.).
I skimmed through the changes and added exceptions where they felt
warranted (to `hawk.toml`). I've included the `hawk.toml` file in the
prior commit, but am excluding it from being committed, since it's not
at a point that we have automated tooling.
## Summary
Keep the sysconfig generator aware of compiler paths that appeared in
older downloadable `python-build-standalone` releases, so regenerating
the mappings on current `main` does not drop support for previously
shipped aarch64 and riscv64 binaries.
## Context
uv patches compiler paths embedded in managed Python `sysconfig` data to
use portable `cc` and `c++` commands. These mappings are generated from
the current python-build-standalone targets.
Older python-build-standalone releases remain downloadable and retain
the compiler paths used when they were built. When a target changes
toolchains or is removed from the current targets, regenerating the
mappings silently drops support for those releases.
This previously removed the aarch64 GNU mappings. The upcoming
python-build-standalone sync in #19531 would similarly replace the
riscv64 GNU mappings with LLVM mappings.
## Test plan
I've manually verified, that before this patch, on an arm64 linux box:
```
% uvx python3.12.10 -c 'import sysconfig; print(sysconfig.get_config_var("CC"))'
/usr/bin/aarch64-linux-gnu-gcc -pthread
```
and after (after uninstalling the python version using `uv python
uninstall 3.12.10`):
```
% ./target/debug/uvx python3.12.10 -c 'import sysconfig; print(sysconfig.get_config_var("CC"))'
cc -pthread
```
## Summary
`uv-python` has some unit tests for python discovery. Python discovery
is sensitive to various environment variables. The code tries to be
"hermetic" by dropping some significant ones and setting others to fixed
values, but it misses some conda specific environment variables.
This PR fixes this gap by just using `EnvVars::all_names` to drop all
environment variables that uv cares about eagerly to avoid this and
similar future gaps.
## Test Plan
Existing coverage.
## Summary
Previously installing a windows python on linux failed, because the
windows python install does not have a `python` binary and it does not
use a `_sysconfigdata_*` file.
## Test Plan
Manually tested by running `uv python install
cpython-3.12.3-windows-x86_64-none`
---------
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
This adds PyEmscripten platform, which is standardized in [PEP
783](https://peps.python.org/pep-0783/). This basically replaces the
Pyodide platform. I kept the Pyodide platform for backward
compatibility.
## Test Plan
`cargo test`
## Why
Dependency linting needs to connect imported Python modules back to the
packages in a workspace resolution so it can identify unused
dependencies and imports that are undeclared or only indirect. `uv
workspace metadata` already exposes the resolution graph; this adds the
module ownership data needed to use that graph for linting.
## What changed
- Adds `--sync` to `uv workspace metadata`. When requested, uv syncs the
environment in inexact mode with all extras and dependency groups, then
adds a `module_owners` mapping from importable module names to
resolution package node IDs when mapped modules are present.
- Makes `--sync` conflict with `--dry-run`, since module ownership is
determined from installed distributions after syncing.
- Adds a validated `ModuleName` type and `InstalledDist::read_modules`,
which derives module names from distribution `RECORD` entries.
- Detects Python modules from packages, `.py` files, legacy sourceless
`.pyc` files, and supported extension-module filenames; parent packages
are recorded as owners as well.
- Filters owners through the selected installed resolution, so
distributions outside that resolution and virtual workspace packages do
not produce metadata entries.
A module may be owned by more than one package, for example when two
distributions provide entries under the same namespace package.
## Limitations
Editable installs that only record a `.pth` file do not currently
provide module ownership entries and will require separate handling.
This adds abstractions for handing hints, pulling them out of ad-hoc
addendums to error messages and treating them as a first-class concept
so we can render them more consistently.
The key concepts are:
- `Hints`: a wrapper struct for `Vec<Cow<'a, str>>`
- `Hint`: a trait with `hints(&self) -> Hints<'_>`
Error types now `impl Hint` to provide hints. This means errors need to
carry more information to construct the `Hint` lazily instead of ahead
of time. The majority of refactoring around error types is to support
that change.
When displaying errors, we
[downcast](https://github.com/astral-sh/uv/pull/18090/changes#r2935662966)
to each type known to implement the trait and extract hints. We then
display all the hints after the error chain is complete.
The user-facing affect of this is that hints are now always rendered
outside the error chain.
This is a blocker to #17110 and is pulled out of that project. There is
still some weirdness with miette's `help:` prompts that will be removed
entirely in a following change.
---------
Co-authored-by: Codex <noreply@openai.com>
## Summary
This is a Codex-driven PR to remove as many `pub` interfaces as we can,
and then fixing the resulting Clippy violations. (I then read the diff
and reverted some changes.)
We use `pub` to expose members across crates within our workspace, even
though we don't intend each of those `pub` members to be usable API --
we are largely our only caller. (I've explicitly retained pieces that we
added to our API based on prior requests from other users.) As a result,
we can remove >1,000 unused LOC.
## Summary
Right now, it appears that we parse the JSON manifest for ~every lock
and sync (I _think_ this regressed in b9826778b). By avoiding that work,
this PR improves (warm) `uv lock` from 38.94ms to 24.06ms (38.2%
faster!), and (warm) `uv sync` from 146.96ms to 129.80ms.
Note that we _do_ need to read it if you're using a pre-release, since
we have this dedicated pre-release warning.
Closes https://github.com/astral-sh/uv/issues/19397.
Required for https://github.com/astral-sh/uv/pull/19034
Arguably, we could keep our existing behavior and have
`UV_PYTHON_SEARCH_PATH` implicitly disable the registry lookup, but I
think that's too confusing.
This disables registry lookups everywhere and modifications in `uv
python install`. In the latter case, `UV_PYTHON_INSTALL_REGISTRY` takes
precedence.
## Summary
This adds a regression test and fix for #18795. I ran the test and
confirmed reproduction before implementing the fix.
The underlying bug here happens only on Windows, and only when
exercising the PEP 514 Python installation registration pathway (which
the integration tests disable by default, since it involves global
mutable state that leaks between tests). The bug itself is just an
imprecision in how we compute the "tag" for the Python entry -- we
weren't including the variant (the `t` in `3.14t`), so two distinct
installs (`3.14` and `3.14t`) would end up with the same registry tag.
For an end user, this surfaces as Python installation entries missing
when running `uv python list`.
One thing to note about the test here is that it _does_ exercise the
Windows registry pathway, which means that it intentionally bypasses the
guardrail around global mutations in the integration tests. This is
"fine" in the sense that there are on other tests observing that state
at the moment, but I think it's a risk in terms of isolation (in the
sense that devs who run our integration tests will actually observe
global changes to their Python installations, plus any failure in the
test means we won't clean up our global changes). Two options there:
- I could try and harden/isolate the registry mutation pathways a bit
more, e.g. we could add `UV_DEV_WINDOWS_REGISTRY_COMPANY_KEY` or
something like that to do some more test-level isolation of HKCU writes.
This still modifies global state, but at least it'll be more namespaced.
- I could remove the integration test entirely, now that we've confirmed
that the fix itself works. This leaves us without coverage, but given
that the fix itself is ~2 lines that might be acceptable.
Fixes#18795.
## Test Plan
This PR includes a regression test.
---------
Signed-off-by: William Woodruff <william@astral.sh>
See https://github.com/astral-sh/uv/issues/18890
Adds special-case validation for `SSL_CERT_FILE` and `SSL_CERT_DIR`
where we actually check if webpki will accept the given certificates
and, if not, emit a better error message about why. This means we
perform eager validation of certificates, parsing them more than once
since reqwest will parse them again on client build. Unfortunately,
there's not a straight-forward way to provide our pre-parsed
certificates to reqwest without doing a lot more work. Nor is there a
clear way to retrieve the parsed certificates on error.
We use https://github.com/rusticata/x509-parser for parsing which seems
reputable.
We may want to _drop_ all invalid certificates instead, but that can be
a future decision and this machinery can be reused for warnings.
Ideally webpki would just have better error messages, but that's a
separate project.
See https://github.com/astral-sh/uv/issues/18890
We can load a certificate that is a valid bundle, but on client build we
can fail if the certificate is unsupported for various reasons. This
propagates the error instead of panicking.
## Summary
This PR fixes a problem in `uv pip install`, which currently refuses to
install debug wheels in virtual environments with debug CPythons.
Before this change, wheel parsing already preserved debug ABI suffixes
like cp313d and cp314d, but Tags::from_env only propagated free-threaded
and legacy pymalloc variants. As a result, uv would detect a debug
interpreter correctly during discovery while still generating
cp313/cp314 environment tags, causing debug-built wheels to be rejected
as incompatible.
Fix this by accepting a debug_enabled flag in Tags::from_env, mapping it
to CPythonAbiVariants::Debug, and passing the interpreter debug state
from the production call sites in uv-python and uv pip resolution.
Also update the affected tests and helpers, and add a regression test
that verifies debug CPython 3.13 generates cp313-cp313d manylinux tags.
## Test Plan
Tests run:
- cargo test -p uv-platform-tags
tags::tests::test_system_tags_debug_cpython -- --exact
- cargo test -p uv-installer
plan::tests::test_abi3_on_free_threaded_python_hint -- --exact
- cargo test -p uv-installer
plan::tests::test_gil_enabled_cpython_on_free_threaded_python_hint --
--exact
- cargo test -p uv-installer
plan::tests::test_abi3_on_regular_python_no_special_hint -- --exact
- cargo test -p uv --test it
pip_install::abi_compatibility_on_debug_python -- --exact
---------
Co-authored-by: konstin <konstin@mailbox.org>
Applies a patch to use Python 3.6 compatible types in our vendored
`packaging` implementation used in the interpreter query script. Adds
Python 3.6 and 3.7 test coverage in CI.
## Summary
We need to normalize any relative managed-Python install roots before
checking whether the active environment’s interpreter is uv-managed, so
that `sync --active` reuses the environment.
Closes https://github.com/astral-sh/uv/issues/16631.