## Summary
Lockfile re-validation was iterating from the workspace roots; but for
scripts, we have no roots! This is similar to the approach we use in `uv
tree`, `uv export`, etc.
Closes https://github.com/astral-sh/uv/issues/18312.
In a warm cache situation, e.g. with `uv run`, toml parsing is by far
our slowest operation. These kinda hacky spans help debugging that. It
would be better if `toml::from_str` would be instrumented itself, but
this way we can add paths in the relevant places.
<!--
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
will close https://github.com/astral-sh/uv/issues/17160
Basically, old code using nodes with no incoming edges included
transitive deps which resulted in orphaned roots. We didnt actually need
that code as well, infinite cycle handling was done in `fn visit`
correctly so just using root node directly solves the issue. I also
found another bug during the process where packages were marked as
"visited" prematurely resulting in not even expanding them and not
showing them at the tree.
## Test Plan
I added two tests with snapshots.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
If no revision is specified, we should assume the input used the default
branch (rather than returning early and ignoring the existing SHA).
Closes https://github.com/astral-sh/uv/issues/18224.
Resolves#18014 (also related to
https://github.com/astral-sh/uv/issues/18010)
When `--exclude-newer` is active and all versions of a required package
were uploaded after the cutoff, resolution fails with no indication that
the setting is the cause. This diff adds a resolver hint that surfaces
the relationship.
The diff also includes debug-level logging of the exclude-newer
configuration at resolver start (happy to get this in separately, if
needed), and trace-level logging each time an individual file is
excluded by the timestamp cutoff. These help diagnose resolution
behavior without requiring the hint to fire.
In https://github.com/astral-sh/uv/issues/18026, we received a report
that resolution took >90m and the root cause appears to be that repeated
conflicts with a single extra causes an exponential explosion.
I used Codex to find an optimization to avoid this.
> When processing N pairwise conflict sets that share a common extra
(e.g., {pinned, a}, {pinned, b}, {pinned, c}, ...), the resolver creates
forks by iterating over each conflict set and splitting every existing
fork into N+1 sub-forks. Without the optimization, this is
multiplicative — each conflict set multiplies the fork count, producing
O(2^N) forks even though most are redundant.
>
> The key observation is: if a prior conflict set already excluded an
extra from a fork, then a later conflict set involving that same extra
is already satisfied in that fork — there's nothing left to separate.
For example, after processing {pinned, a}, one fork has pinned excluded.
When we then process {pinned, b}, that fork already can't have pinned
active, so the constraint "at most one of pinned or b" is trivially
true. We call this fork dominated by the earlier split — no further
forking is needed.
>
> The one subtlety: even if a conflict set is satisfied in a fork, we
might still need to fork if the remaining non-excluded item appears in
another conflict set that's still live (i.e., has two or more
non-excluded items). That's the refined check — we only skip forking
when the item is truly dominated across all conflict sets, not just the
current one.
I then did some rough benchmarking
```
┌────┬────────┬───────┬─────────┐
│ N │ Before │ After │ Speedup │
├────┼────────┼───────┼─────────┤
│ 5 │ 29ms │ 27ms │ ~1× │
├────┼────────┼───────┼─────────┤
│ 8 │ 58ms │ 27ms │ 2× │
├────┼────────┼───────┼─────────┤
│ 10 │ 185ms │ 26ms │ 7× │
├────┼────────┼───────┼─────────┤
│ 15 │ 20.0s │ 46ms │ 435× │
├────┼────────┼───────┼─────────┤
│ 20 │ >60s │ 28ms │ >2,000× │
└────┴────────┴───────┴─────────┘
```
This came out of an experience on whether claude code can find missing
updates in from changes in PRs:
#18096 — Propagate project-level conflicts to package extras (zanieb)
**Stale docstring on `filter_by_group`**: In
`crates/uv-resolver/src/resolver/environment.rs`, the docstring still
says "Include rules have no effect in `included_by_group`". After this
PR, include rules DO affect `included_by_group` when a project-level
exclusion exists for a package — an explicit inclusion for a specific
extra overrides the exclusion.
#18081 — Filter `pylock.toml` wheels by tags and `requires-python`
(konstin)
**Inverted docstring on `is_wheel_unreachable`**: At
`crates/uv-resolver/src/lock/mod.rs:6088`, the docstring says "Returns
`false` if the wheel is definitely unreachable" but the function
actually returns `true` when unreachable. The `true`/`false` are
swapped.
#18075 — make missing files warning debug (dead10ck)
**Analogous `warn!` not changed in flat_index.rs**:
`crates/uv-client/src/flat_index.rs:215` has a similar `warn!("Skipping
file in {}: {err}", &url)` that exhibits the same noisy pattern.
Arguable whether flat indexes warrant the same change since they're
user-configured and less likely to trigger mass warnings.
Closes#18015
Project-level conflict items (e.g., `{ package = "pkg-a" }`) were not
properly excluding the package's extras and groups from the conflicting
fork. When a project-level conflict excludes a package, all of that
package's extras should also be excluded (since they transitively depend
on the base package). The alternative seems to be that the user needs to
enumerate all of the extra conflicts explicitly in addition to the
package conflict, which seems excessive.
---------
Co-authored-by: Claude <noreply@anthropic.com>
## Summary
This PR replaces `bitflags` in favour of `enumflags2` (which we already
transitively depended on) so that `PreviewFeatures` can be replaced with
`PreviewFeature` which is an enum. This clarifies intent in cases where
we only care about one specific `PreviewFeature`.
To avoid a bunch of boilerplate changes, the `Preview` wrapper has been
kept and creation now involves a `&[PreviewFeature]` in all cases. The
alternative was to have everything which initialises a `Preview` use
`BitFlags` directly and possibly to remove `Preview` entirely but this
keeps things simpler and limits the changes throughout the rest of the
codebase solely to changes which deal with the name changes (ALL_CAPS to
PascalCase) and the impact on `--show-settings` which I don't believe we
care about stability of output for?
The changes to `--show-settings` could be avoided with some custom
`Debug` implementation but that seems excessive.
This PR will impact #16452. But the changes were inspired by trying to
remove the need for that particular PR to add more runtime type
checking.
## Test Plan
Existing tests were adjusted (I also fixed some missing cases). The test
for panicking in cases which are now prevented through the use of type
changes has been dropped. All the rest of the tests were ran, snapshot
changes reviewed and applied.
These should be generated alongside all the other resolver hints, I
think.
The order changes here, but that seems fine.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Resolves https://github.com/astral-sh/uv/issues/16846,
https://github.com/astral-sh/uv/issues/16813
This diff adds support for disabling `exclude-newer` for specific
packages using `<name>=false`. This allows packages
without upload dates (e.g., CPU-only PyTorch wheels from custom indices)
to be resolved when a global `exclude-newer` is set, without disabling
it globally.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
## Summary
Right now, when we return a `Dist` from a lockfile, we concatenate all
hashes for all distributions for a given package. In the case of
https://github.com/astral-sh/uv/issues/17143, I think that means we'll
return the SHA256 from the sdist, plus the SHA512 from the wheel. If the
wheel was previously installed (i.e., it's in the cache), and we
computed the SHA256 at that point in time, then `Hashed::has_digests`
would return `true` because we have _at least_ one SHA256. We now limit
the hashes to the distribution that we expect to install.
Closes https://github.com/astral-sh/uv/issues/17143.
<!--
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
Pyproject.toml index url may contain a username while lockfile doesn't.
Treat it as the same index to prevent unintended package updates
Fixes#16436
---------
Co-authored-by: konstin <konstin@mailbox.org>
<!--
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: #17103
## Test Plan
The following settings will be enabled for the schema.
```toml
[tool.uv]
exclude-newer = "P7D"
```
Adds support for "friendly" durations like, 1 week, 7 days, 24 hours
using Jiff's parser. During resolution, we calculate this relative to
the current time and resolve it into a concrete timestamp for the
lockfile. If the span has not changed, e.g., to another relative value,
then locking again will not change the lockfile. The locked timestamp
will only be updated when the lockfile is invalidated, e.g., with
`--upgrade`. This prevents the lockfile from repeatedly churning when a
relative value is used.
## Summary
There are a class of outcomes whereby an index might not be included in
"allowed indexes", but could still correctly appear in a lockfile. In
the linked case, we have two `default = true` indexes, and one of them
is also named. We omit the second `default = true` index from the list
of "allowed indexes", but since it's named, a dependency can reference
it explicitly. We handle this correctly for `project.dependencies`, but
the handling was incorrectly omitting dependency groups.
Closes https://github.com/astral-sh/uv/issues/16843.
Fixes https://github.com/astral-sh/uv/issues/16447
Passing this around explicitly uncovers some behaviors where we pass
e.g. the credentials store to reading the lockfile. The changes in this
PR should preserve the existing behavior for now, they only make the
locations we read from more explicit.
Labeling this PR as "Enhancement" instead of "Internal" in case this
changes behavior when it shouldn't have.
## Summary
Like in `uv.lock`, we should omit artifacts that are filtered out by
`--no-binary` or by the target platform tags.
Closes https://github.com/astral-sh/uv/issues/13413.
## Summary
Follow up to https://github.com/astral-sh/uv/pull/15563
Closes https://github.com/astral-sh/uv/issues/13485
This is a first-pass at adding support for conditional support for Git
LFS between git sources, initial feedback welcome.
e.g.
```
[tool.uv.sources]
test-lfs-repo = { git = "https://github.com/zanieb/test-lfs-repo.git", lfs = true }
```
For context previously a user had to set `UV_GIT_LFS` to have uv fetch
lfs objects on git sources. This env var was all or nothing, meaning you
must always have it set to get consistent behavior and it applied to all
git sources. If you fetched lfs objects at a revision and then turned
off lfs (or vice versa), the git db, corresponding checkout lfs
artifacts would not be updated properly. Similarly, when git source
distributions were built, there would be no distinction between sources
with lfs and without lfs. Hence, it could corrupt the git, sdist, and
archive caches.
In order to support some sources being LFS enabled and other not, this
PR adds a stateful layer roughly similar to how `subdirectory` works but
for `lfs` since the git database, the checkouts and the corresponding
caching layers needed to be LFS aware (requested vs installed). The
caches also had to isolated and treated entirely separate when handling
LFS sources.
Summary
* Adds `lfs = true` or `lfs = false` to git sources in pyproject.toml
* Added `lfs=true` query param / fragments to most relevant url structs
(not parsed as user input)
* In the case of uv add / uv tool, `--lfs` is supported instead
* `UV_GIT_LFS` environment variable support is still functional for
non-project entrypoints (e.g. uv pip)
* `direct-url.json` now has an custom `git_lfs` entry under VcsInfo
(note, this is not in the spec currently -- see caveats).
* git database and checkouts have an different cache key as the sources
should be treated effectively different for the same rev.
* sdists cache also differ in the cache key of a built distribution if
it was built using LFS enabled revisions to distinguish between non-LFS
same revisions. This ensures the strong assumption for archive-v0 that
an unpacked revision "doesn't change sources" stays valid.
Caveats
* `pylock.toml` import support has not been added via git_lfs=true,
going through the spec it wasn't clear to me it's something we'd support
outside of the env var (for now).
* direct-url struct was modified by adding a non-standard `git_lfs`
field under VcsInfo which may be undersirable although the PEP 610 does
say `Additional fields that would be necessary to support such VCS
SHOULD be prefixed with the VCS command name` which could be interpret
this change as ok.
* There will be a slight lockfile and cache churn for users that use
`UV_GIT_LFS` as all git lockfile entries will get a `lfs=true` fragment.
The cache version does not need an update, but LFS sources will get
their own namespace under git-v0 and sdist-v9/git hence a cache-miss
will occur once but this can be sufficient to label this as breaking for
workflows always setting `UV_GIT_LFS`.
## Test Plan
Some initial tests were added. More tests likely to follow as we reach
consensus on a final approach.
For IT test, we may want to move to use a repo under astral namespace in
order to test lfs functionality.
Manual testing was done for common pathological cases like killing LFS
fetch mid-way, uninstalling LFS after installing an sdist with it and
reinstalling, fetching LFS artifacts in different commits, etc.
PSA: Please ignore the docker build failures as its related to depot
OIDC issues.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
At present, we only have support for the detail routes (e.g.,
`https://pypi.org/simple/requests`), but not the top-level index route
(e.g., `https://pypi.org/simple/`). I need this for some downstream work
so pulling it into its own PR.
Resolves https://github.com/astral-sh/uv/issues/16496
This PR updates the resolver so `build-system` dependency failures
surface prerelease hints even when prerelease selection is fixed. When a
build dependency only has prerelease candidates, or the requested
version explicitly includes a prerelease marker, we now emit a tailored
hint explaining that build environments can’t auto-enable prereleases
and describing how to opt in.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
This PR adds an `exclude-dependencies` setting that allows users to omit
a dependency during resolution. It's effectively a formalized version of
the `flask ; python_version < '0'` hack that we've suggested to users in
various issues.
Closes#12616.
Provide a good error message when the discovered workspace members
mismatch with the locked workspace members in `uv export --frozen`,
instead of panicking.
Fixes#16406
Fixes#16340
## Summary
Some package registries (PyTorch, corporate PyPI mirrors) return `403
Forbidden` when a package is not found, instead of `404 Not Found`. The
previous error message incorrectly suggested this was always an
authentication issue, causing confusion for users.
This PR updates the error hint to clarify that a 403 error could
indicate either missing authentication credentials OR that the package
doesn't exist on the index.
## Test Plan
- Updated existing snapshot test in `crates/uv/tests/it/edit.rs` to
reflect the new error message
- The change is purely a message improvement with no behavioral changes
## Changes
### Before
hint: An index URL (https://example.com/simple) could not be queried due
to a lack of valid authentication credentials (403 Forbidden).
### After
hint: An index URL (https://example.com/simple) returned a 403 Forbidden
error. This could indicate missing authentication credentials, or the
package may not exist on this index.
## Files Changed
- `crates/uv-resolver/src/pubgrub/report.rs` - Updated error message
- `crates/uv/tests/it/edit.rs` - Updated snapshot test expectation
---------
Co-authored-by: eun2ce <eun2ce@eun2ceui-MacBookPro.local>
Co-authored-by: konstin <konstin@mailbox.org>
## Summary
fixes issue #15938
- show platform wheel hint with a concrete
`tool.uv.required-environments` example so users know how to configure
compatibility
- add `WheelTagHint::suggest_environment_marker` to pick a sensible
environment marker based on the available wheel tags
- update the `sync_required_environment_hint` integration snapshot to
expect the new multi-line hint
## Test Plan
cargo test --package uv --test it --
sync::sync_required_environment_hint