Commit Graph

592 Commits

Author SHA1 Message Date
Charlie Marsh 3d36b71ab1
Show additions and removals in `uv lock` updates (#5410)
## Summary

Closes https://github.com/astral-sh/uv/issues/5401.
2024-07-24 15:33:09 +00:00
konsti 9b12fcb90d
Remove outdated `DistributionWire` serde annotations (#5400)
This struct doesn't implement `serde::Serialize`, the annotations are
dead.
2024-07-24 15:30:58 +02:00
konsti 20018cd0fc
Add two `ForkState` methods (#5404)
Small refactoring split out of the instability work. No functional
changes.
2024-07-24 12:09:33 +00:00
konsti 6d34f53ee5
Update `Resolution` docs (#5402)
They didn't reflect the recent nodes/edges changes.
2024-07-24 11:39:48 +00:00
Zanie Blue 3e067766de
Stylize `Requires-Python` consistently (#5304) 2024-07-23 18:05:21 +00:00
Charlie Marsh 0f8186d9ad
Add `requires-python` to `uv init` (#5322)
## Summary

Prefers, in order:

- The major-minor version of an interpreter discovered via `--python`.
- The `requires-python` from the workspace.
- The major-minor version of the default interpreter.

If the `--python` request is a version or a version range, we use that
without fetching an interpreter.

Closes https://github.com/astral-sh/uv/issues/5299.
2024-07-23 16:02:40 +00:00
Ibraheem Ahmed c8ac8ee57a
Allow conflicting prerelease strategies when forking (#5150)
## Summary

Similar to https://github.com/astral-sh/uv/pull/5232, we should also
track prerelease strategies per-fork, instead of globally per package.
The common functionality for tracking locals and prerelease versions
across forks is extracted into the `ForkMap` type.

Resolves https://github.com/astral-sh/uv/issues/4579. This doesn't quite
solve https://github.com/astral-sh/uv/issues/4959, as that issue relies
on overlapping markers.
2024-07-23 11:57:14 -04:00
konsti bea8bc6c61
Stable sorting of requirements.txt in universal mode (#5334)
The `RequirementsTxtComparator` was written assuming there is one
distribution per package name. This changed with the universal
resolution, which allows multiple versions or urls for the same package
name. The sorting we emitted for these new entries was incidental.

With this change, we properly sort these entries by name, version and
then url in universal mode.

This is an output format change for `--universal` users.
2024-07-23 16:46:32 +02:00
Charlie Marsh 26e042a794
Include URLs on graph edges (#5312)
## Summary

Excellent find from @konstin. If we have a package that's included in
two forks at the same version, but with different URLs, we need to avoid
collapsing them in the lockfile.

Closes https://github.com/astral-sh/uv/issues/5294.
2024-07-22 22:41:03 +00:00
konsti 0a95b7abcd
Simplify `Resolution` type (#5301)
Looking at how to merge identical forks, i found that the `Resolution`
can be simplified by treating it as a nodes and edges store (plus pins,
they are separate since they are per name-version, not per
(virtual-)package-version). This should also make #5294 more apparent,
which i didn't touch here.

I additionally added some doc comments to the `Resolution` types.
2024-07-22 15:10:30 -04:00
konsti e207a909ad
Slim down `solve` (#5300)
Our everything-method `solve` tends to grow large, so before i'm adding
more logic, i'm moving some code and some logging statements around to
keep it manageable.

I made minor changes to the logging, otherwise no logic changes, only
refactoring.
2024-07-22 20:52:45 +02:00
Charlie Marsh 2d9df488ef
Use tag error rather than requires-python error for ABI filtering (#5296)
## Summary

`dearpygui==1.9.1 has no wheels are available with a matching Python
ABI` is way better than `he requested Python version (>=3.12.3) does not
satisfy Python>=3.12.3`.

Closes https://github.com/astral-sh/uv/issues/5284.
2024-07-22 18:31:08 +00:00
Yorick 0c4627e2f2
If multiple indices contain the same version, use the first index (#5288)
This fixes resolving packages that publish an invalid stub to pypi, such
as tensorrt-llm.

## Summary

In https://github.com/astral-sh/uv/pull/3138 , we implemented
`unsafe-best-match`. However, it seems to not quite work as expected.
When multiple indices contain the same version, it's not clear which
index the current code uses. This PR fixes that to use the first index
the package is in.

## Test Plan

```console
$ echo 'tensorrt-llm==0.11.0' | ./target/debug/uv pip compile - --extra-index-url https://pypi.nvidia.com --python-version=3.10 --index-strategy=unsafe-best-match --annotation-style=line
```
2024-07-22 14:03:30 -04:00
Charlie Marsh b6f470416e
Match wheel tags against `Requires-Python` major-minor (#5289)
## Summary

Given `Requires-Python: ">=3.12.3"`, we were rejecting wheels like
`dearpygui-1.11.1-cp312-cp312-win_amd64.whl`, since `3.12.0` is not
included in `>=3.12.3`. We instead need to test against the major-minor
version of `Requires-Python`.

The easiest way to do this, I think, is the use the `RequiresPython`
struct, which has a single bound that we can truncate the major-minor.
This also means that we now allow
`dearpygui-1.11.1-cp312-cp312-win_amd64.whl` for specifiers like
`Requires-Python: "==3.10.*"`. This is incorrect on the surface, but it
does match our semantics for `Requires-Python` elsewhere: we treat it as
a lower bound.

Closes https://github.com/astral-sh/uv/issues/5287.
2024-07-22 14:33:53 +00:00
Charlie Marsh 5a23f05799
Store resolution options in lockfile (#5264)
## Summary

This PR modifies the lockfile to include the impactful resolution
settings, like the resolution and pre-release mode. If any of those
values change, we want to ignore the existing lockfile. Otherwise,
`--resolution lowest-direct` will typically have no effect, which is
really unintuitive.

Closes https://github.com/astral-sh/uv/issues/5226.
2024-07-22 08:28:22 -04:00
Charlie Marsh 5c3d55afa8
Remove unused error variant (#5266) 2024-07-21 23:38:45 +00:00
Charlie Marsh 841edc3718
Move workspace abstractions to `uv-workspace` crate (#5236)
## Summary

These are really different from the rest of the existing crate as
evidenced by the bifurcation in the requirements.
2024-07-20 02:15:32 +00:00
Charlie Marsh 1243c5e28c
Avoid URL parsing when deserializing wheels (#5235)
## Summary

This PR slots in `UrlString` for `WheelWire`, which IIUC should avoid
parsing URLs during deserialization?
2024-07-20 02:11:26 +00:00
Ibraheem Ahmed bb73edb03b
Respect local versions for all user requirements (#5232)
## Summary

This fixes a few bugs introduced by
https://github.com/astral-sh/uv/pull/5104. I previously thought we could
track conflicting locals the same way we track conflicting URLs in
forks, but it turns out that ends up being very tricky. URL forks work
because we prioritize directly URL requirements. We can't prioritize
locals in the same way without conflicting with the URL prioritization
(this may be possible but it's not trivial), so we run into issues where
a correct resolution depends on the order in which dependencies are
traversed.

Instead, we track local versions across all forks in `Locals`. When
applying a local version, we apply all locals with markers that
intersect with the current fork. This way we end up applying some local
versions without creating a fork. For example, given:
```
// pyproject.toml
dependencies = [
    "torch==2.0.0+cu118 ; platform_machine == 'x86_64'",
]

// requirements.in
torch==2.0.0
.
```

We choose `2.0.0+cu118` in all cases. However, if a disjoint fork is
created based on local versions, the resolver will choose the most
compatible local when it narrows to a specific fork. Thus we correctly
respect local versions when forking:
```
// pyproject.toml
dependencies = [
    "torch==2.0.0+cu118 ; platform_machine == 'x86_64'",
    "torch==2.0.0+cpu ; platform_machine != 'x86_64'"
]

// requirements.in
torch==2.0.0
.
``` 

We should also be able to use a similar strategy for
https://github.com/astral-sh/uv/pull/5150.

## Test Plan

This fixes https://github.com/astral-sh/uv/issues/5220 locally for me,
as well as a few other bugs that were not reported yet.
2024-07-19 16:56:09 -05:00
Charlie Marsh ed9b820815
Remove trailing period from user-facing messages (#5218)
## Summary

Per #5209, we only show periods in messages when the message itself
spans more than a single sentence.
2024-07-19 10:43:49 -04:00
Charlie Marsh fb1a529106
Process completed Python installs and uninstalls as a stream (#5203)
## Summary

This ensures that we process Python installs and uninstalls as soon as
they complete, rather than waiting for them all to complete, then
processing them sequentially. In practice, it shouldn't be much of a
difference (since the processing is code is fairly light), but it
strikes me as more correct.
2024-07-19 12:50:38 +00:00
konsti 93ba676f2e
Log origin of version selection (#5186)
Log whether a version was picked because it was the next version or
because it was a preference (installed, lockfile or sibling fork)
2024-07-19 08:15:45 +00:00
konsti 5bcdaedf8b
Merge extras in lockfile (#5181)
As user, you specify a list of extras. Internally, we decompose this
into one virtual package per extra. We currently leak this abstraction
by writing one entry per extra to the lockfile:

```toml
[[distribution]]
name = "foo"
version = "4.39.0.dev0"
source = { editable = "." }
dependencies = [
    { name = "pandas" },
    { name = "pandas", extra = "excel" },
    { name = "pandas", extra = "hdf5" },
    { name = "pandas", extra = "html", marker = "os_name != 'posix'" },
    { name = "pandas", extra = "output-formatting", marker = "os_name == 'posix'" },
    { name = "pandas", extra = "plot", marker = "os_name == 'posix'" },
]
```

Instead, we should merge the extras into a list of extras, creating a
more concise lockfile:

```toml
[[distribution]]
name = "foo"
version = "4.39.0.dev0"
source = { editable = "." }
dependencies = [
    { name = "pandas", extra = ["excel", "hdf5"] },
    { name = "pandas", extra = ["html"], marker = "os_name != 'posix'" },
    { name = "pandas", extra = ["output-formatting", "plot"], marker = "os_name == 'posix'" },
]
```

The base package is now implicitly included, as it is in PEP 508.

Fixes #4888
2024-07-18 14:07:49 -04:00
Ibraheem Ahmed bbd65fc626
Set exact version specifiers when resolving from lockfile (#5193)
## Summary

Should resolve https://github.com/astral-sh/uv/issues/5192.

## Test Plan

@konstin can you confirm this fixes your issue?
2024-07-18 19:02:08 +02:00
konsti fbb00f701c
Warn about unconstrained direct deps in lowest resolution (#5142)
Warn when there is a direct dependency without a lower bound and
`--resolution lowest` is set.

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
2024-07-18 03:44:53 +00:00
Charlie Marsh 6a49dba30c
Enforce hashes in lockfile install (#5170)
## Summary

Hashes will be validated if present, but aren't required (since, e.g.,
some registries will omit them, as will Git dependencies and such).

Closes https://github.com/astral-sh/uv/issues/5168.
2024-07-17 23:10:37 +00:00
Charlie Marsh 218ae2c13e
Key hash policy on version, rather than package (#5169)
## Summary

First part of: https://github.com/astral-sh/uv/issues/5168.
2024-07-17 19:01:49 -04:00
Charlie Marsh ba4e2e3d2a
Use the strongest hash in the lockfile (#5167)
## Summary

We only need to store one hash -- it should be the "strongest" hash. In
practice, most registries (like PyPI) only serve one, and we only
compute a SHA256 hash for direct URLs.

Part of: https://github.com/astral-sh/uv/issues/4924

## Test Plan

I verified that changing:

```diff
diff --git a/crates/distribution-types/src/hash.rs b/crates/distribution-types/src/hash.rs
index 553a74f55..d36c62286 100644
--- a/crates/distribution-types/src/hash.rs
+++ b/crates/distribution-types/src/hash.rs
@@ -31,7 +31,7 @@ impl<'a> HashPolicy<'a> {
     pub fn algorithms(&self) -> Vec<HashAlgorithm> {
         match self {
             Self::None => vec![],
-            Self::Generate => vec![HashAlgorithm::Sha256],
+            Self::Generate => vec![HashAlgorithm::Sha256, HashAlgorithm::Sha512],
             Self::Validate(hashes) => {
                 let mut algorithms = hashes.iter().map(HashDigest::algorithm).collect::<Vec<_>>();
                 algorithms.sort();
```

Then running `uv lock` with a URL gave me:

```toml
[[distribution]]
name = "iniconfig"
version = "2.0.0"
source = { url = "62565a6e1ceac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" }
wheels = [
    { url = "62565a6e1ceac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha512:44cc53a6c8dd7cf4d6d52bded308bcc4b4f85fff2ed081f60f7d4beaa86a7cde6d099e3976331232d4cbd472ad5d1781064725b0999c7cd3a2a4d42df687ee81" },
]
```
2024-07-17 20:38:33 +00:00
Charlie Marsh e271d1fdde
Make registry hashes optional in the lockfile (#5166)
## Summary

If a registry doesn't include hashes, then we won't include them in the
lockfile either.

Part of: https://github.com/astral-sh/uv/issues/4924.

Closes: https://github.com/astral-sh/uv/issues/5120.
2024-07-17 16:29:49 -04:00
konsti a6dfd3953a
Handle universal vs. fork markers with `ResolverMarkers` (#5099)
* Use a dedicated `ResolverMarkers` check in the fork state. This is
better than the `MarkerTree::And(Vec::new())` check.
* Report the timing correct naming universal resolution instead of two
spaces around an empty string when there are no markers.
* On resolution error, show the split that we're in. I'm not sure how to
word this, since we're doing a universal resolution until we fork, so
the trace may contain information from requirements that are not part of
this fork.
2024-07-17 18:59:33 +02:00
Ahmed Ilyas eb35c05b89
Indicate that `uv lock --upgrade` has updated the lock file (#5110)
## Summary

Resolves #4346, I've gone with the suggested `cargo` approach here.

## Test Plan

`cargo test`

```console
❯ ../target/debug/uv lock --upgrade
warning: `uv lock` is experimental and may change without warning.
Resolved 11 packages in 41ms
Updating flask v2.3.3 -> v3.0.3
note: pass `--verbose` to see 9 unchanged dependencies
❯ ../target/debug/uv lock --upgrade -vv
    0.002478s DEBUG uv uv 0.2.24
warning: `uv lock` is experimental and may change without warning.
....
Resolved 11 packages in 50ms
    0.103703s DEBUG uv::commands::project::lock Unchanged blinker v1.8.2
    0.103719s DEBUG uv::commands::project::lock Unchanged click v8.1.7
    0.103731s DEBUG uv::commands::project::lock Unchanged colorama v0.4.6
    0.103742s DEBUG uv::commands::project::lock Unchanged flask v3.0.3
    0.103754s DEBUG uv::commands::project::lock Unchanged importlib-metadata v8.0.0
    0.103767s DEBUG uv::commands::project::lock Unchanged itsdangerous v2.2.0
    0.103778s DEBUG uv::commands::project::lock Unchanged jinja2 v3.1.4
    0.103788s DEBUG uv::commands::project::lock Unchanged markupsafe v2.1.5
    0.103798s DEBUG uv::commands::project::lock Unchanged werkzeug v3.0.3
    0.103809s DEBUG uv::commands::project::lock Unchanged zipp v3.19.2
```
2024-07-17 01:14:20 +00:00
Ibraheem Ahmed d583847f8b
Allow conflicting locals when forking (#5104)
## Summary

Currently, the `Locals` type relies on there being a single local
version for a given package. With marker expressions this may not be
true, a similar problem to https://github.com/astral-sh/uv/pull/4435.
This changes the `Locals` type to `ForkLocals`, which tracks locals for
a given fork. Local versions are now tracked on `PubGrubRequirement`
before forking.

Resolves https://github.com/astral-sh/uv/issues/4580.
2024-07-16 16:57:30 +00:00
Andrew Gallant 048ae8f7f3 uv-resolver: add TRACE dump of resolver output
Specifically, this shows the resolution produced by the
resolver *before* constructing a resolution graph.

Unlike most trace messages, this is a multi-line message
that needs to do some small amount of work to build
itself. So we do an explicit gating on the log level here
instead of just relying on the `trace!` macro itself.
2024-07-16 09:51:54 -07:00
Charlie Marsh 9a44bc1d35
Filter out invalid wheels based on `requires-python` (#5084)
## Summary

The example in the linked issue doesn't quite work, but I think it has
to do with the existing filtering logic. Will follow-up separately.

Closes https://github.com/astral-sh/uv/issues/5012.
2024-07-15 21:01:38 -04:00
Andrew Gallant 563507edba uv-resolver: add support for incomplete markers
In some cases, it's possible for the marker expressions on conflicting
dependency specification to be disjoint but *incomplete*. That is, if
one unions the disjoint markers, the result is not the complete set of
marker environments possible. There may be some "gap" of marker
environments not covered by the markers.

This is a problem in practice because, before this commit, we only
created forks in the resolver for specific marker expressions. So if a
dependency happened to fall in a "gap," our resolver would never see it.

This commit fixes this by adding a new split covering the negation of
the union of all marker expressions in a set of forks for a specific
package.

Originally, I had planned on only creating this split when it was known
that the gap actually existed. That is, when the negation of the marker
expressions did *not* correspond to the empty set. After a lot of
thought, unfortunately, this (I believe) effectively boils down to 3SAT,
which is NP-complete.

Instead, what we do here is *always* create an extra split unless we can
definitively tell that it is empty. We look for a few cases, but
otherwise throw our hands up and potentially do wasted work.

This also updates the lock scenario tests to reflect the actual bug fix
here.
2024-07-15 10:09:01 -07:00
konsti e34ab96e80
Remove special casing from no solution error (#5067)
The only pubgrub error that can occur is a `NoSolutionError`, and the
only place it can occur is `unit_propagation`, all other variants if
`PubGrubError` are unreachable. By changing the return type on pubgrub's
side (https://github.com/astral-sh/pubgrub/pull/28), we can remove the
pattern matching and the `unreachable!()` asserts on `PubGrubError`.

Our pubgrub error wrapper used to have a two phased initialization,
first mostly stubs in `solve[_tracked]()` and then adding the actual
context in `resolve()`. When constructing the error in `solve` we
already have all this context, so we can unify this to a regular
constructor and remove the special casing in `resolve()` and `hints()`.
2024-07-15 17:43:35 +02:00
konsti f3430c3a2a
Improve error message when package has no installation candidates (#5010)
Currently, with
```toml
[project]
name = "transformers"
version = "4.39.0.dev0"
requires-python = ">=3.10"
dependencies = [
  "torch==1.10.0"
]
```
i get
```
$ uv sync --preview
Resolved 3 packages in 7ms
error: found distribution torch==1.10.0 @ registry+https://pypi.org/simple with neither wheels nor source distribution
```
This error message is wrong, there are wheels, they are just not
compatible. I initially got this error message during `uv lock` (in a
build), so i also added that this is about installation, not about
locking.

We should reject this version immediately because with the current
requires python, it can never be installed, but even then we need to
change the error message because you can be on the correct python
version, but an unsupported platform.
2024-07-15 00:00:40 +00:00
Charlie Marsh a571150949
Normalize out complementary == or != markers (#5050)
## Summary

Closes https://github.com/astral-sh/uv/issues/5044.
2024-07-14 23:53:58 +00:00
Ibraheem Ahmed ba217f1059
Use lockfile to prefill resolver index (#4495)
## Summary

Use the lockfile to prefill the `InMemoryIndex` used by the resolver.
This enables us to resolve completely from the lockfile without making
any network requests/builds if the requirements are unchanged. It also
means that if new requirements are added we can still avoid most I/O
during resolution, partially addressing
https://github.com/astral-sh/uv/issues/3925.

The main limitation of this PR is that resolution from the lockfile can
fail if new versions are requested that are not present in the lockfile,
in which case we have to perform a fresh resolution. Fixing this would
likely require lazy version/metadata requests by `VersionMap` (this is
different from the lazy parsing we do, the list of versions in a
`VersionMap` is currently immutable).

Resolves https://github.com/astral-sh/uv/issues/3892.

## Test Plan

Added a `deterministic!` macro that ensures that a resolve from the
lockfile and a clean resolve result in the same lockfile output for all
our current tests.
2024-07-12 18:49:28 -04:00
Ibraheem Ahmed a1f71a36e7
Fix substring marker expression disjointness checks (#4998)
## Summary

Noticed a bug here, `'a' in env` and `env not in 'a'` are not disjoint
given `env == 'ab'`.
2024-07-12 15:21:21 +00:00
Ibraheem Ahmed d833910a5d
Avoid reparsing wheel URLs (#4947)
## Summary

We currently store wheel URLs in an unparsed state because we don't have
a stable parsed representation to use with rykv. Unfortunately this
means we end up reparsing unnecessarily in a lot of places, especially
when constructing a `Lock`. This PR adds a `UrlString` type that lets us
avoid reparsing without losing the validity of the `Url`.

## Test Plan

Shaves off another ~10 ms from
https://github.com/astral-sh/uv/issues/4860.

```
➜  transformers hyperfine "../../uv/target/profiling/uv lock" "../../uv/target/profiling/baseline lock" --warmup 3
Benchmark 1: ../../uv/target/profiling/uv lock
  Time (mean ± σ):     120.9 ms ±   2.5 ms    [User: 126.0 ms, System: 80.6 ms]
  Range (min … max):   116.8 ms … 125.7 ms    23 runs
 
Benchmark 2: ../../uv/target/profiling/baseline lock
  Time (mean ± σ):     129.9 ms ±   4.2 ms    [User: 127.1 ms, System: 86.1 ms]
  Range (min … max):   123.4 ms … 141.2 ms    23 runs

Summary
  ../../uv/target/profiling/uv lock ran
    1.07 ± 0.04 times faster than ../../uv/target/profiling/baseline lock
```
2024-07-10 05:16:30 -04:00
Charlie Marsh 23eb42deed
Allow constraints to be provided in `--upgrade-package` (#4952)
## Summary

Allows, e.g., `--upgrade-package flask<3.0.0`.

Closes https://github.com/astral-sh/uv/issues/1964.
2024-07-09 20:09:13 -07:00
Ibraheem Ahmed 8c9bd70c71
Avoid serializing if lockfile does not change (#4945)
## Summary

Avoid serializing and writing the lockfile if a cheap comparison shows
that the contents have not changed.

## Test Plan

Shaves ~10ms off of https://github.com/astral-sh/uv/issues/4860 for me.

```
➜  transformers hyperfine "../../uv/target/profiling/uv lock" "../../uv/target/profiling/baseline lock" --warmup 3
Benchmark 1: ../../uv/target/profiling/uv lock
  Time (mean ± σ):     130.5 ms ±   2.5 ms    [User: 130.3 ms, System: 85.0 ms]
  Range (min … max):   126.8 ms … 136.9 ms    23 runs
 
Benchmark 2: ../../uv/target/profiling/baseline lock
  Time (mean ± σ):     140.5 ms ±   5.0 ms    [User: 142.8 ms, System: 85.5 ms]
  Range (min … max):   133.2 ms … 153.3 ms    21 runs
 
Summary
  ../../uv/target/profiling/uv lock ran
    1.08 ± 0.04 times faster than ../../uv/target/profiling/baseline lock
```
2024-07-09 17:08:27 -04:00
konsti 53db63f6dd
Apply extra to overrides and constraints (#4829)
This is an attempt to solve https://github.com/astral-sh/uv/issues/ by
applying the extra marker of the requirement to overrides and
constraints.

Say in `a` we have a requirements
```
b==1; python_version < "3.10"
c==1; extra == "feature"
```

and overrides
```
b==2; python_version < "3.10"
b==3; python_version >= "3.10"
c==2; python_version < "3.10"
c==3; python_version >= "3.10"
```

Our current strategy is to discard the markers in the original
requirements. This means that on 3.12 for `a` we install `b==3`, but it
also means that we add `c` to `a` without `a[feature]`, causing #4826.
With this PR, the new requirement become,

```
b==2; python_version < "3.10"
b==3; python_version >= "3.10"
c==2; python_version < "3.10" and extra == "feature"
c==3; python_version >= "3.10" and extra == "feature"
```

allowing to override markers while preserving optional dependencies as
such.

Fixes #4826
2024-07-09 20:37:24 +02:00
Charlie Marsh 72dd34b225
Filter out markers based on Python requirement (#4912)
## Summary

In marker normalization, we now remove any markers that are redundant
with the `requires-python` specifier (i.e., always true for the given
Python requirement).

For example, given `iniconfig ; python_version >= '3.7'`, we can remove
the `python_version >= '3.7'` marker when resolving with
`--python-version 3.8`.

Closes #4852.
2024-07-09 09:15:58 -07:00
Charlie Marsh a046d23f79
Avoid AND-ing multi-term specifiers in marker normalization (#4911)
## Summary

Given `python_version != '3.8' and python_version < '3.10'`, the first
term was expanded to `python_version < '3.8'` and `python_version >
'3.8'`. We then AND'd all three terms together. We don't seem to have a
way to differentiate between the terms to AND and the terms to OR in the
normalization code (it all gets flattened together), so instead this PR
expands the expressions at the leaf level and then flattens them at the
level above when appropriate.

Closes https://github.com/astral-sh/uv/issues/4910.
2024-07-09 16:04:26 +00:00
Ibraheem Ahmed ed4234d52e
Filter and flatten markers (#4639)
## Summary

More marker simplification:
- Filters out redundant subtrees based on outer expressions, e.g. `a and (a or
b)` simplifies to `a`.
- Flattens nested trees internally, e.g. `(a and b) and c`

Resolves https://github.com/astral-sh/uv/issues/4536.
2024-07-08 18:59:21 +00:00
Ibraheem Ahmed dc7ad3abdb
Implement `uv tree` (#4708)
## Summary

Implements the `uv tree`, which displays dependencies from the lockfile
as a tree. Resolves https://github.com/astral-sh/uv/issues/4699.
2024-07-08 18:07:48 +00:00
Andrew Gallant 857d2e8f1e uv-resolver: partially revert Requires-Python version narrowing
The PR #4707 introduced the notion of "version narrowing," where a
Requires-Python constraint was _possibly_ narrowed whenever the
universal resolver created a fork. The version narrowing would occur
when the fork was a result of a marker expression on `python_version`
that is *stricter* than the configured `Requires-Python` (via, say,
`pyproject.toml`).

The crucial conceptual change made by #4707 is therefore that
`Requires-Python` is no longer an invariant configuration of resolution,
but rather a mutable constraint that can vary from fork to fork. This in
turn can result in some cases, such as in #4885, where different
versions of dependencies are selected. We aren't sure whether we can fix
those or not, with version narrowing, so for now, we do this revert to
restore the previous behavior and we'll try to address the version
narrowing some other time.

This also adds the case from #4885 as a regression test, ensuring that
we don't break that in the future. I confirmed that with version
narrowing, this test outputs duplicate distributions. Without narrowing,
there are no duplicates.

Ref #4707, Fixes #4885
2024-07-08 09:56:59 -07:00
Charlie Marsh ac3a085084
Respect `requires-python` when prefetching (#4900)
## Summary

This is fallout from https://github.com/astral-sh/uv/pull/4705. We need
to respect `requires-python` in the prefetch code to avoid building
unsupported distributions.

Closes https://github.com/astral-sh/uv/issues/4898.
2024-07-08 16:32:09 +00:00
Charlie Marsh 71c6a9fad3
Sort dependencies before wheels and source distributions (#4897)
Closes https://github.com/astral-sh/uv/issues/4889.
2024-07-08 14:25:05 +00:00
konsti 57cfe1e229
Small forking log improvements (#4894)
I found the current messages insufficient when tracking down a bug.
2024-07-08 13:56:57 +00:00
konsti d787e69f7c
Rename `Workspace.root` to `Workspace.install_path` (#4859)
Renaming in preparation of #4833, which adds a `Workspace.lock_path`. No
functional changes.
2024-07-07 18:35:41 +00:00
Charlie Marsh b588054dfb
Always use release-only comparisons for `requires-python` (#4794)
## Summary

There are a few ideas at play here:

1. pip always strips versions to the release when evaluating against a
`Requires-Python`, so we now do the same. That means, e.g., using
`3.13.0b0` will be accepted by a project with `Requires-Python: >=
3.13`, which does _not_ adhere to PEP 440 semantics but is somewhat
intuitive.
2. Because we know we'll only be evaluating against release-only
versions, we can use different semantics in PubGrub that let us collapse
ranges. For example, `python_version >= '3.10' or python_version <
'3.10'` can be collapsed to the truthy marker.

Closes https://github.com/astral-sh/uv/issues/4714.
Closes https://github.com/astral-sh/uv/issues/4272.
Closes https://github.com/astral-sh/uv/issues/4719.
2024-07-04 20:06:52 +00:00
konsti 11cb0059c1
Remove incompatible wheels from `uv.lock` (#4799)
Remove wheels from the lockfile that don't match the required python
version. For example, we remove
`charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl` when we have
`requires-python = ">=3.12"`.

Our snapshots barely show changes since we avoid the large binaries for
which matters. Here are 3 real world `uv.lock` before/after comparisons
to show the large difference:
*
[warehouse](https://gist.github.com/konstin/9a1ed6a32b410e250fcf4c6ea8c536a5)
(5677 -> 4214)
*
[transformers](https://gist.github.com/konstin/5636281b5226f64aa44ce3244d5230cd)
(6484 -> 5816)
*
[github-wikidata-bot](https://gist.github.com/konstin/ebbd7b9474523aaa61d9a8945bc02071)
(793 -> 454)

We only remove wheels we are certain don't match the python version and
still keep those with unknown tags. We could remove even more wheels by
also considering other markers, e.g. removing linux wheels for a
windows-only dep, but we would trade complex, easy-to-get-wrong logic
for diminishing returns.
2024-07-04 14:03:54 -04:00
konsti d178d97a40
Document the `RequiresPython` fields (#4798) 2024-07-04 08:27:02 -04:00
Zanie Blue dd7da6af5f
Change "toolchain" to "python" (#4735)
Whew this is a lot.

The user-facing changes are:

- `uv toolchain` to `uv python` e.g. `uv python find`, `uv python
install`, ...
- `UV_TOOLCHAIN_DIR` to` UV_PYTHON_INSTALL_DIR`
- `<UV_STATE_DIR>/toolchains` to `<UV_STATE_DIR>/python` (with
[automatic
migration](https://github.com/astral-sh/uv/pull/4735/files#r1663029330))
- User-facing messages no longer refer to toolchains, instead using
"Python", "Python versions" or "Python installations"

The internal changes are:

- `uv-toolchain` crate to `uv-python`
- `Toolchain` no longer referenced in type names
- Dropped unused `SystemPython` type (previously replaced)
- Clarified the type names for "managed Python installations"
- (more little things)
2024-07-03 07:44:29 -05:00
Ibraheem Ahmed a380e8e4df
Add conversion from lockfile `Distribution` to `Metadata` (#4706)
## Summary

Splitting this out from https://github.com/astral-sh/uv/pull/4495
because it's also useful to reuse the `uv pip tree` code for `uv tree`.
2024-07-02 18:03:20 +00:00
Charlie Marsh d9f389a58d
Narrow `requires-python` requirement in resolver forks (#4707)
## Summary

Given:

```text
numpy >=1.26 ; python_version >= '3.9'
numpy <1.26 ; python_version < '3.9'
```

When resolving for Python 3.8, we need to narrow the `requires-python`
requirement in the top branch of the fork, because `numpy >=1.26` all
require Python 3.9 or later -- but we know (in that branch) that we only
need to _solve_ for Python 3.9 or later.

Closes https://github.com/astral-sh/uv/issues/4669.
2024-07-02 12:23:38 +00:00
Charlie Marsh 89b3324ae1
Move `Requires-Python` incompatibilities out of version map (#4705)
## Summary

This is required to solve https://github.com/astral-sh/uv/issues/4669,
because the `Requires-Python` version can now vary across a resolution.
For example, within certain forks, we might have a more narrow range,
which would allow us to use distributions that would not be allowed for
the global resolution.

This should be fine because `requires-python` is part of the package
metadata, so it should be consistent between files within a package
version. As such, there shouldn't be any risk that we incorrectly
prioritize distributions by omitting this information.

(To be more specific, the risk is something like: we prioritize some
wheel over a source distribution within a package-version, so we don't
track the source distribution at all. Then, later, when we choose a
candidate, we see that the wheel doesn't meet the `Requires-Python`
requirement, even though the source distribution _would've_ met it. If
files within a distribution could have varied support, this would be a
real risk.)
2024-07-02 08:15:39 -04:00
Charlie Marsh f3d1e52e65
Use `requires-python` semantics for `--universal` (#4701)
## Summary

This doesn't actually change any behaviors, but it does make it a bit
easier to solve #4669, because we don't have to support "version
narrowing" for the non-`RequiresPython` variants in here. Right now, the
semantics are kind of muddied, because the `target` variant is
_sometimes_ interpreted as an exact version and sometimes as a lower
bound.
2024-07-01 15:16:40 -04:00
konsti 0ee4a2cc6e
Remove `SolveState` to `ForkState` (#4683)
It's hard to talk about solve state and resolver state, so i'm renaming
them to fork state and resolver state, indicating the hierarchy between
more directly.
2024-07-01 12:33:50 -04:00
Charlie Marsh 977a5c8835
Use a single mutable preferences for forks (#4690)
## Summary

Avoids a quadratic loop.

See: https://github.com/astral-sh/uv/pull/4662.
2024-07-01 12:47:28 +00:00
Charlie Marsh 2d57309b0f
Set fork solution as preference when resolving (#4662)
## Summary

This should both make it faster to solve forks (since we have a guess
for a valid resolution, and will bias towards packages we've already
fetched) and improve consistency between forks.

Closes https://github.com/astral-sh/uv/issues/4617.
2024-07-01 08:25:40 -04:00
konsti 049833e037
Log when we start solving a fork (#4684)
Adds a debug log message with the markers.
2024-07-01 08:46:54 +00:00
Charlie Marsh ac87fd4006
Disable Clippy's `too-many-arguments` rule (#4663)
## Summary

We allow this constantly, I think it's just too pedantic for us.
2024-06-30 19:30:38 +00:00
Charlie Marsh 8d9b4a5e1c
Sort indexes during graph edge removal (#4649)
## Summary

`remove_edge` will invalidate the last index in the graph, so we need to
ensure that each index we look at is "earlier" than the last.

Co-authored-by: bluss <bluss@users.noreply.github.com>
2024-06-29 13:31:54 -04:00
Charlie Marsh ea6185e082
Merge markers when applying constraints (#4648)
## Summary

When a constraint is applied to a requirement with a marker, the marker
needs to be propagated to the constraint.

If both the constraint and the requirement have a marker, they need to
be merged together (via `and`).

Closes https://github.com/astral-sh/uv/issues/4575.
2024-06-29 16:51:04 +00:00
Charlie Marsh 0bb99952f6
Sync all packages in a virtual workspace (#4636)
## Summary

This PR dodges some of the bigger issues raised by
https://github.com/astral-sh/uv/pull/4554 and
https://github.com/astral-sh/uv/pull/4555 by _not_ changing any of the
bigger semantics around syncing and instead merely changing virtual
workspace roots to sync all packages in the workspace (rather than
erroring due to being unable to find a project).

Closes #4541.
2024-06-29 12:43:59 -04:00
Charlie Marsh 164160da34
Avoid infinite loop for cyclic installs (#4633)
## Summary

Closes https://github.com/astral-sh/uv/issues/4629.

## Test Plan

Run `uv sync` with:

```toml
[project]
name = "foo"
version = "0.1.0"
requires-python = ">=3.9"
dependencies = ["poetry"]
```
2024-06-28 20:15:28 +00:00
Andrew Gallant 22ce8fdf4b uv-resolver: add some tests for other source types
This adds some coverage for source types that aren't used as much in
other tests. For example, a direct URL source with a sub-directory.
2024-06-28 12:02:33 -07:00
Andrew Gallant 47ea5effc7 uv-resolver: make source structured via an inline table 2024-06-28 12:02:33 -07:00
Andrew Gallant cbc83ecd8f uv-resolver: add DistributionId::to_toml
This centralizes writing out the DistributionId as TOML. This is again
just a refactor. No behavioral changes were made. In a subsequent
commit, we will tweak how `source` is written.
2024-06-28 12:02:33 -07:00
Andrew Gallant fcbc65c956 uv-resolver: add Distribution::to_toml
This splits out the TOML serialization logic for Distribution to its own
method. This is just moving code. No changes have been made.
2024-06-28 12:02:33 -07:00
konsti 796171e1e6
Normalize fork markers (#4623)
Looks much better than #4618:

```
DEBUG Pre-fork split universal took 0.644s
DEBUG Split python_version >= '3.12' and platform_machine == 'aarch64' and platform_system == 'Darwin' and platform_system == 'Linux' took 0.659s
DEBUG Split python_version == '3.9' and platform_machine == 'arm64' and platform_system == 'Darwin' took 0.291s
```
2024-06-28 17:55:48 +02:00
konsti e79fa774b8
Split out `SolveState::add_unavailable_version` (#4619)
Reduces the size of the overly complex `solve_tracked` a little. No
functional changes.
2024-06-28 14:47:54 +00:00
konsti 2b63dfd717
Log fork timings (#4618)
This includes a functional change, we now skip the forked state pop/push
if we didn't fork.

From transformers:

```
DEBUG Pre-fork split universal took 0.036s
DEBUG Split python_version >= '3.10' and python_version >= '3.10' and platform_system == 'Darwin' and python_version >= '3.11' and python_version >= '3.12' and python_version >= '3.6' and platform_system == 'Linux' and platform_machine == 'aarch64' took 0.048s
DEBUG Split python_version <= '3.9' and platform_system == 'Darwin' and platform_machine == 'arm64' and python_version >= '3.7' and python_version >= '3.8' and python_version >= '3.9' took 0.038s
```

The messages could use simplification from
https://github.com/astral-sh/uv/issues/4536

We can consider nested spans in the future but this works nicely for
now.
2024-06-28 14:45:30 +00:00
Charlie Marsh 9b38450998
Handle cycles when propagating markers (#4595)
## Summary

It turns out that `Topo` only works on graphs without cycles. If a graph
has a cycle, it seems to bail early. So we were losing markers for trees
that contain cycles (like Poetry, which depends on
`poetry-plugin-export`, which depends on Poetry).

Now, we remove cycles beforehand and re-add those edges afterwards.

It's a bit hard for me to reason about the implications of this. The way
that marker propagation works is that we do visit the nodes in-order and
propagate the markers from any incoming to any outgoing edges. We only
do this at a single depth (rather than recursively) because we visit the
nodes in-order anyway. But if you have a cycle... then in theory you
might need to propagate the markers recursively? Or maybe not?

As an example:

`A -> B -> C -> D -> B`

If `A -> B` has `sys_platform == 'darwin'`, and then `D -> B` has
`python_version >= '3.7`... then we don't need to propagate
`python_version >= '3.7'` back to `B` or any of its dependencies,
because the condition would be `(sys_platform == 'darwin' or
python_version >= '3.7) or sys_platform == 'darwin'`, which is
equivalent to `sys_platform == 'darwin'`.

Closes #4584.
2024-06-27 17:30:09 -04:00
konsti 80e45d3174
Indent wheels like dependencies in the lockfile (#4582)
This PR contains two style changes to the lockfile:
* Always indent lists of objects, even with they are only a single
element.
* Use 4 spaces instead of tabs for indenting, to mirror what we do in
the ruff formatter.
2024-06-27 22:26:47 +02:00
Charlie Marsh 9ac1a29c7a
Treat Python version as a lower bound in `--universal` (#4597)
## Summary

Closes https://github.com/astral-sh/uv/issues/4591.
2024-06-27 14:41:45 -04:00
konsti 86e6f76836
Use inline table for dependencies in lockfile (#4581)
Use indented inline tables for `distribution.dependencies`,
`distribution.optional-dependencies` and
`distribution.dev-dependencies`.

The new style is more concise (see examples below) and it makes the
association between a distribution and its dependencies clearer
(previously, they were both individual `[[...]]` blocks separated by
newlines). The style is optimized for small, meaningful diffs by placing
each dependency on a single line with a final trailing comma. Whenever a
dependency is added, removed or changed, there should be a one line diff
in `distribution.dependencies`. The final trailing comma ensures that
adding a dependency doesn't change the line ahead.

Part of #3611

## Examples

### Simple workspace package

Before:
```toml
[[distribution]]
name = "bird-feeder"
version = "1.0.0"
source = "editable+packages/bird-feeder"

[[distribution.dependencies]]
name = "anyio"

[[distribution.dependencies]]
name = "seeds"
```

After:
```toml
[[distribution]]
name = "bird-feeder"
version = "1.0.0"
source = "editable+packages/bird-feeder"
dependencies = [
    { name = "anyio" },
    { name = "seeds" },
]
```

### Flask

Before:
```toml
[[distribution]]
name = "flask"
version = "3.0.2"
source = "registry+https://pypi.org/simple"
sdist = { url = "a89e8120fa0bbafcb2c2387c0317be/flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d", size = 675248 }
wheels = [{ url = "aa98bfe0ebf27ce224fb4f766acb23/flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e", size = 101300 }]

[[distribution.dependencies]]
name = "blinker"

[[distribution.dependencies]]
name = "click"

[[distribution.dependencies]]
name = "itsdangerous"

[[distribution.dependencies]]
name = "jinja2"

[[distribution.dependencies]]
name = "werkzeug"

[distribution.optional-dependencies]

[[distribution.optional-dependencies.dotenv]]
name = "python-dotenv"
```

After:
```toml
[[distribution]]
name = "flask"
version = "3.0.2"
source = "registry+https://pypi.org/simple"
sdist = { url = "a89e8120fa0bbafcb2c2387c0317be/flask-3.0.2.tar.gz", hash = "sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d", size = 675248 }
dependencies = [
    { name = "blinker" },
    { name = "click" },
    { name = "itsdangerous" },
    { name = "jinja2" },
    { name = "werkzeug" },
]
wheels = [{ url = "aa98bfe0ebf27ce224fb4f766acb23/flask-3.0.2-py3-none-any.whl", hash = "sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e", size = 101300 }]

[distribution.optional-dependencies]
dotenv = [
    { name = "python-dotenv" },
]
```

### Forking

Before:
```toml
[[distribution]]
name = "project"
version = "0.1.0"
source = "editable+."

[[distribution.dependencies]]
name = "package-a"
version = "4.3.0"
source = "registry+https://astral-sh.github.io/packse/0.3.29/simple-html/"
marker = "sys_platform == 'darwin'"

[[distribution.dependencies]]
name = "package-a"
version = "4.4.0"
source = "registry+https://astral-sh.github.io/packse/0.3.29/simple-html/"
marker = "sys_platform == 'linux'"

[[distribution.dependencies]]
name = "package-b"
marker = "sys_platform == 'linux'"

[[distribution.dependencies]]
name = "package-c"
marker = "sys_platform == 'darwin'"
```

After:
```toml
[[distribution]]
name = "project"
version = "0.1.0"
source = "editable+."
dependencies = [
    { name = "package-a", version = "4.3.0", source = "registry+https://astral-sh.github.io/packse/0.3.29/simple-html/", marker = "sys_platform == 'darwin'" },
    { name = "package-a", version = "4.4.0", source = "registry+https://astral-sh.github.io/packse/0.3.29/simple-html/", marker = "sys_platform == 'linux'" },
    { name = "package-b", marker = "sys_platform == 'linux'" },
    { name = "package-c", marker = "sys_platform == 'darwin'" },
]
```
2024-06-27 20:06:45 +02:00
Charlie Marsh bbbe1f3968
Avoid enforcing extra-only constraints (#4570)
## Summary

In the dependency refactor (https://github.com/astral-sh/uv/pull/4430),
the logic for requirements and constraints was combined. Specifically,
we were applying constraints _before_ filtering on markers and extras,
and then applying that same filtering to the constraints. As a result,
constraints that should only be activated when an extra is enabled were
being enabled unconditionally.

Closes https://github.com/astral-sh/uv/issues/4569.
2024-06-26 22:52:46 +00:00
konsti b677a06aba
Break `choose_version` into three methods (#4543)
`ResolverState::choose_version` had become huge, with an odd match due
to the url handling from #4435. This refactoring breaks it into
`choose_version`, `choose_version_registry` and `choose_version_url`. No
functional changes.
2024-06-26 15:15:28 +02:00
Andrew Gallant 63dcc6fa57 uv-resolver: make `hash` on `SourceDistMetadata` required
Now that we only materialize a `SourceDist` when there is some
non-redundant information in it from `source`, we can require that a
hash is present.
2024-06-26 05:55:52 -07:00
Andrew Gallant 7c71aec68c uv-resolver: include 'sdist' entry for direct URL dependencies
In the case of a direct URL sdist, it includes a hash, and this hash is
not (and probably should not) be part of the `source`. The URL is part
of the source because it permits uniquely identifying this particular
package as distinct from any other package with the same name. But, we
should still include the hash.

So in this commit, we rejigger what we did previously to make it so the
`SourceDist` value isn't even constructed at all when it isn't needed.
This also in turn lets us make the hash field required (which we will do
in a subsequent commit).

This does mean the URL is stored twice for direct URL dependencies in
the lock file. This seems non-ideal. We could make the URL for the sdist
optional, but this seems like a bridge too far? Another choice is to add
a new key to `distribution` that is just `direct-url-hash`, but that
also seems mucky.

Maybe the duplication here is okay given the relative rarity of direct
URL dependencies.
2024-06-26 05:55:52 -07:00
Andrew Gallant 86c2a9b0b2 uv-resolver: only serialize sdist for registry sourced distributions
This fixes an issue in the lock file where, in cases where we had a
non-registry sdist, the information in the sdist was strictly redundant
with the information in the source. This was born out in the code
already where the `sdist` field was only ever used to build a source
distribution type when the source was a registry. In all other cases,
the source distribution data can be materialized from the `source`
field.
2024-06-26 05:55:52 -07:00
Andrew Gallant 4899612619 uv-resolver: refactor Distribution::to_dist
This makes it clear that an actual `sdist` is only required when a
distribution is from a registry. In all other cases, a source
distribution is manufactured directly from the `source`.
2024-06-26 05:55:52 -07:00
Andrew Gallant 840f61fc2b uv-resolver: re-arrange some code
Previously, we had Lock and LockWire impl blocks inter-mixed. This bugs
me a bit, so I've just shuffled things around so that we have Lock, impl
Lock, LockWire and then impl LockWire.

No changes are otherwise made to the code here.
2024-06-26 05:55:52 -07:00
konsti d7f195fdc9
Add `PubGrubPackage::name_no_root` (#4542)
Small code style improvement.
2024-06-26 12:29:26 +00:00
Andrew Gallant 4accbfd915 uv-resolver: support unambiguous omission of 'source' and 'version'
When there is only one distribution for a particular package name, any
dependencies (the edges in the resolution graph) that reference that
package name are completely unambiguous. Therefore, we can actually omit
their version and source information and instead derive it from the
distribution entry.

We add some tests to check the success and error cases. That is, when
`source` or `version` are omitted and there are more than one
corresponding distribution for the package name (i.e., it's ambiguous),
then lock deserialization should fail.
2024-06-26 05:18:23 -07:00
Andrew Gallant 4cb1595136 uv-resolver: refactor lock data type deserialization
This commit prepares to make the `source` and `version` fields optional
in a `distribution.dependency` based on whether they have an unambiguous
value. e.g., When there is exactly one distribution with a matching
package name.

This refactor effectively defines "wire" types for most of the lock data
types (repeating the `WheelWire` and `LockWire` pattern) with one key
difference: we don't use serde's `TryFrom` integration. In this
refactor, we could have, and it would have worked. But in a subsequent
commit, we're going to be adding state to the `unwire()` calls that is
impossible to thread through a `TryFrom` implementation. This state will
tell us how to populate the `source` and `version` values on a
`Dependency` when they're missing.

The duplication of types here is unfortunate, but compiler should catch
any deviations. And the wire types are unexported, so they have a
limited blast radius on complexity.
2024-06-26 05:18:23 -07:00
konsti d9dbb8a4af
Support conflicting URL in separate forks (#4435)
Downstack PR: #4481

## Introduction

We support forking the dependency resolution to support conflicting
registry requirements for different platforms, say on package range is
required for an older python version while a newer is required for newer
python versions, or dependencies that are different per platform. We
need to extend this support to direct URL requirements.

```toml
dependencies = [
  "iniconfig @ 62565a6e1ceac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl ; python_version >= '3.12'",
  "iniconfig @ b3c12c6d70988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl ; python_version < '3.12'"
]
```

This did not work because `Urls` was built on the assumption that there
is a single allowed URL per package. We collect all allowed URL ahead of
resolution by following direct URL dependencies (including path
dependencies) transitively, i.e. a registry distribution can't require a
URL.

## The same package can have Registry and URL requirements

Consider the following two cases:

requirements.in:
```text
werkzeug==2.0.0
werkzeug @ 960bb4017c4aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl
```
pyproject.toml:
```toml
dependencies = [
  "iniconfig == 1.1.1 ; python_version < '3.12'",
  "iniconfig @ git+https://github.com/pytest-dev/iniconfig@93f5930e668c0d1ddf4597e38dd0dea4e2665e7a ; python_version >= '3.12'",
]
```

In the first case, we want the URL to override the registry dependency,
in the second case we want to fork and have one branch use the registry
and the other the URL. We have to know about this in
`PubGrubRequirement::from_registry_requirement`, but we only fork after
the current method.

Consider the following case too:

a:
```
c==1.0.0
b @ https://b.zip
```
b:
```
c @ https://c_new.zip ; python_version >= '3.12'",
c @ https://c_old.zip ; python_version < '3.12'",
```

When we convert the requirements of `a`, we can't know the url of `c`
yet. The solution is to remove the `Url` from `PubGrubPackage`: The
`Url` is redundant with `PackageName`, there can be only one url per
package name per fork. We now do the following: We track the urls from
requirements in `PubGrubDependency`. After forking, we call
`add_package_version_dependencies` where we apply override URLs, check
if the URL is allowed and check if the url is unique in this fork. When
we request a distribution, we ask the fork urls for the real URL. Since
we prioritize url dependencies over registry dependencies and skip
packages with `Urls` entries in pre-visiting, we know that when fetching
a package, we know if it has a url or not.

## URL conflicts

pyproject.toml (invalid):
```toml
dependencies = [
  "iniconfig @ e96292c7f723f1fa332fe4ed6dfbec/iniconfig-1.1.0.tar.gz",
  "iniconfig @ b3c12c6d70988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl ; python_version < '3.12'",
  "iniconfig @ 62565a6e1ceac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl ; python_version >= '3.12'",
]
```

On the fork state, we keep `ForkUrls` that check for conflicts after
forking, rejecting the third case because we added two packages of the
same name with different URLs.

We need to flatten out the requirements before transformation into
pubgrub requirements to get the full list of other requirements which
may contain a URL, which was changed in a previous PR: #4430.

## Complex Example

a:
```toml
dependencies = [
  # Force a split
  "anyio==4.3.0 ; python_version >= '3.12'",
  "anyio==4.2.0 ; python_version < '3.12'",
  # Include URLs transitively
  "b"
]
```
b:
```toml
dependencies = [
  # Only one is used in each split.
  "b1 ; python_version < '3.12'",
  "b2 ; python_version >= '3.12'",
  "b3 ; python_version >= '3.12'",
]
```
b1:
```toml
dependencies = [
  "iniconfig @ b3c12c6d70988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl",
]
```
b2:
```toml
dependencies = [
  "iniconfig @ 62565a6e1ceac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl",
]
```
b3:
```toml
dependencies = [
  "iniconfig @ e96292c7f723f1fa332fe4ed6dfbec/iniconfig-1.1.0.tar.gz",
]
```

In this example, all packages are url requirements (directory
requirements) and the root package is `a`. We first split on `a`, `b`
being in each split. In the first fork, we reach `b1`, the fork URLs are
empty, we insert the iniconfig 1.1.1 URL, and then we skip over `b2` and
`b3` since the mark is disjoint with the fork markers. In the second
fork, we skip over `b1`, visit `b2`, insert the iniconfig 2.0.0 URL into
the again empty fork URLs, then visit `b3` and try to insert the
iniconfig 1.1.0 URL. At this point we find a conflict for the iniconfig
URL and error.

## Closing

The git tests are slow, but they make the best example for different URL
types i could find.

Part of #3927. This PR does not handle `Locals` or pre-releases yet.
2024-06-26 13:58:23 +02:00
konsti c28a2c7583
Small `lock.rs` improvements (#4239)
Small improvements i made reading through `lock.rs`.
2024-06-25 22:19:00 +00:00
konsti ff2f927579
Replace `PubGrubDependencies` by `PubGrubDependency` (#4481)
In the last PR (#4430), we flatten the requirements. In the next PR
(#4435), we want to pass `Url` around next to `PubGrubPackage` and
`Range<Version>` to keep track of which `Requirement`s added a url
across forking. This PR is a refactoring split out from #4435 that rolls
the dependency conversion into a single iterator and introduces a new
`PubGrubDependency` struct as abstraction over `(PubGrubPackage,
Range<Version>)` (or `(PubGrubPackage, Range<Version>,
VerbatimParsedUrl)` in the next PR), and it removes the now unnecessary
`PubGrubDependencies` abstraction.
2024-06-25 22:11:52 +00:00
Charlie Marsh e1708689a9
Add a universal resolution mode to `pip compile` (#4505)
## Summary

This needs more tests and a few more changes, but checkpointing it for
now.
2024-06-25 21:28:50 +00:00
konsti f2f48d339e
Flatten requirements eagerly in `get_dependencies` (#4430)
Downstack PR: #4515 Upstack PR: #4481

Consider these two cases:

A:
```
werkzeug==2.0.0
werkzeug @ 960bb4017c4aed12b5ed8b78e0153e/Werkzeug-2.0.0-py3-none-any.whl
```

B:
```toml
dependencies = [
  "iniconfig == 1.1.1 ; python_version < '3.12'",
  "iniconfig @ git+https://github.com/pytest-dev/iniconfig@93f5930e668c0d1ddf4597e38dd0dea4e2665e7a ; python_version >= '3.12'",
]
```

In the first case, `werkzeug==2.0.0` should be overridden by the url. In
the second case `iniconfig == 1.1.1` is in a different fork and must
remain a registry distribution.

That means the conversion from `Requirement` to `PubGrubPackage` is
dependent on the other requirements of the package. We can either look
into the other packages immediately, or we can move the forking before
the conversion to `PubGrubDependencies` instead of after. Either version
requires a flat list of `Requirement`s to use. This refactoring gives us
this list.

I'll add support for both of the above cases in the forking urls branch
before merging this PR. I also have to move constraints over to this.
2024-06-25 21:13:47 +00:00
Charlie Marsh 08bf6fb87c
Deduplicate source edges in annotations (#4530)
## Summary

Not relevant today, but it will be once we support universal resolution,
in which a package can be repeated.
2024-06-25 21:10:09 +00:00
konsti ad42206e50
Unify dependency iteration in `ResolverState::get_dependencies` (#4515)
Upstack PR: #4430

Split out from #4430 according to
https://github.com/astral-sh/uv/pull/4430#discussion_r1650192338.
2024-06-25 23:04:49 +02:00
Charlie Marsh 5732209be3
Add support for `--no-strip-markers` in `pip compile` output (#4503)
## Summary

This is an intermediary change in enabling universal resolution for
`requirements.txt` files. To start, we need to be able to preserve
markers in the `requirements.txt` output _and_ propagate those markers,
such that if you have a dependency that's only included with a given
marker, the transitive dependencies respect that marker too.

Closes #1429.
2024-06-25 20:55:58 +00:00
konsti af1f1369e5
Remove useless `#[allow(clippy::too_many_arguments)]` (#4529)
I went through all `#[allow(clippy::too_many_arguments)]` and removed
the useless ones.
2024-06-25 19:09:59 +00:00
Charlie Marsh ddacede7db
Move requirements.txt distribution into separate file (#4500)
## Summary

No functional changes, but this has outgrown being in the module root.
2024-06-25 00:32:44 +00:00
Charlie Marsh 604be9ed71
Use `Preferences` struct in Manifest API (#4496)
## Summary

This is just a bit more consistent with `Overrides` and `Constraints`.
2024-06-24 23:28:55 +00:00
Charlie Marsh 10ec9c9d0b
Use operations API in `pip compile` (#4493)
## Summary

Closes https://github.com/astral-sh/uv/issues/4235.
2024-06-24 22:20:03 +00:00
Charlie Marsh f6aec0a96c
Remove non-existent extras from lockfile (#4479)
## Summary

Ultimately decided to view this as part of `LockWire` normalization:
removing references to extras that don't exist. I think it would be nice
if the resolver avoided omitting these, but I don't know if it's fully
possible.

Closes https://github.com/astral-sh/uv/issues/4405.
2024-06-24 14:56:56 -04:00
konsti 1984ada57c
Break `PubGrubReportFormatter::hints` into methods (#4478)
I have to add yet another indentation level to the prerelease-available
check in `PubGrubReportFormatter::hints` for #4435, so i've broken the
code into methods and decreased indentation in this split out
refactoring-only change.
2024-06-24 19:14:09 +02:00
konsti 40f852687b
Add context to unregistered task name to error context (#4471)
I caused this error during development and having the name of the task
on it is helpful for debugging.

Split out from #4435
2024-06-24 14:42:55 +00:00
renovate[bot] 3251690327
Update Rust crate rustc-hash to v2 (#4461) 2024-06-24 01:13:05 +00:00
konsti b865341517
Use correct lock path for workspace dependencies (#4421)
Previously, distributions created through `Source::Workspace` would have
the absolute path as lock path. This didn't cause any problems, since in
`Urls` we would later overwrite those urls with the correct one created
from being workspace members by path.

Changing the order surfaced this. This change emits the correct lock
path. I've manually checked the difference with `dbg!`, this is not
observable on main, but on the diverging urls branch it fixes lockfile
creation.
2024-06-20 13:28:47 +00:00
Andrew Gallant 9595a511cd uv-resolver: filter out sibling dependencies in a fork
When a fork is created from a list of dependencies, we were previously
adding all other sibling dependencies to every fork created. But this
isn't actually quite right, since the fork created is always created by
some marker expression. And while it is definitively disjoint from any
directly conflicting dependency specification, it is also possibly
disjoint with other dependencies. For example, as reported in #4414:

```toml
dependencies = [
  "anyio==4.4.0 ; python_version >= '3.12'",
  "anyio==4.3.0 ; python_version < '3.12'",
  "b1 ; python_version >= '3.12'",
  "b2 ; python_version < '3.12'",
]
```

The first two `anyio` requirements are conflicting with non-overlapping
marker expressions, and so a fork is created. Prior to this commit,
*both* `b1` and `b2` would be added to each fork. But of course, `b2` is
impossible in the `anyio==4.4.0` fork because of disjoint marker
expressions.

So in this commit, we specifically filter out any sibling dependencies
that could find their way into a fork that have disjoint markers with
that fork. We are careful to do this both when a new fork is created
from an existing set of dependencies, and when adding new dependencies
to a fork.

Fixes #4414
2024-06-20 07:21:45 -04:00
konsti 3c5b13695e
Move adding dependencies for versions into dedicated method (#4410)
To support diverging urls, we have to check urls when adding
dependencies (after forking). To prepare for this, i've moved adding
dependencies for the current version to
`SolveState::add_package_version_dependencies` and removed the
duplication when checking for self-dependencies.

This changed is joined with a change in pubgrub
(https://github.com/astral-sh/pubgrub/pull/27) that simplifies the same
code path.
2024-06-19 20:19:12 +02:00
konsti e486eb86b7
Log when we fork (#4386)
We currently don't log if or when we split the resolution graphs into
forks. I ran into this when trying to debug missing forking.
2024-06-18 11:47:06 -05:00
Andrew Gallant 20b44f3017 uv-resolver: add some tracing logs for when we filter requirements
Specifically, these are emitted when requirements fail to satisfy
`Requires-Python` or the markers associated with the current fork in the
resolver.

Closes #4373
2024-06-18 11:15:16 -04:00
Charlie Marsh c996e8e3f3
Enable workspace lint configuration in remaining crates (#4329)
## Summary

We didn't have Clippy enabled (to match our workspace settings) in a few
crates.
2024-06-18 03:02:28 +00:00
Andrew Gallant dbb12bcfe4 uv-resolver: fix bug in marker disjointness checking
I found this while testing the tracking of marker expressions across
resolver forks. Namely, given

    sys_platform == 'darwin' and implementation_name == 'pypy'

And:

    sys_platform == 'bar' or implementation_name == 'foo'

These should be disjoint, but the disjointness checker was reporting
them as overlapping. I fixed this by giving handling of disjunctions
higher precedence than conjunctions, although I am not 100% confident
that this is correct for all cases.
2024-06-17 09:30:37 -04:00
Andrew Gallant 407f1e370b uv-resolver: filter dependencies that can't exist in a fork
This commit adds marker expressions to our `Fork` type, which are in
turn passed down into `PubGrubDependencies::from_requirements` to filter
our any dependencies with markers that are disjoint from the fork's
marker expression.

This is necessary to avoid visiting packages in the dependency graph
that can never actually be installed. This is because when a fork is
created in the resolver, it always happens when there are two sibling
dependency specifications on a package with the same name, but with
non-overlapping marker expressions. Each fork corresponds to each
such conflicting dependency specification, and each fork assumes the
the corresponding marker expression as a pre-condition for any future
dependencies considered by it. That is, since the fork represents an
installation path that can only be taken when the corresponding
dependency specification (and its marker expression) is actually used,
it also therefore follows that the marker expression is true. Therefore,
any dependency visited in that fork with a marker expression that cannot
possibly be true when the markers of the fork are true can and ought to
be completely ignored.
2024-06-17 09:30:37 -04:00
Andrew Gallant 07db2b167f uv-resolver: document some of our intermediate data structures
There are some key invariants that I had to re-learn by reading the
code. This hopefully makes those invariants easier to discover by future
me (and others).
2024-06-17 09:30:37 -04:00
Charlie Marsh b7fb0b445f
Use portable slash paths in lockfile (#4324)
## Summary

This would be a lightweight solution to
https://github.com/astral-sh/uv/issues/4307 that doesn't fully engage
with all the possibilities in the design space (but would unblock
cross-platform for now).
2024-06-14 09:05:14 -04:00
Charlie Marsh 83067c1802
Reduce some `anyhow` usages (#4323) 2024-06-14 04:15:53 +00:00
Zanie Blue 5a007b6b9f
Add `BuildOptions` for centralized combination of `NoBuild` and `NoBinary` (#4284)
As requested in review of https://github.com/astral-sh/uv/pull/4067
2024-06-12 21:33:33 +00:00
Zanie Blue 1ab4041baa
Allow specific `--only-binary` and `--no-binary` packages to override `:all:` (#4067)
Updates `--no-binary <package>` to take precedence over `--only-binary
:all:` and `--only-binary <package>` to take precedence over
`--no-binary :all:`.

I'm not entirely sure about this behavior, e.g. maybe I provided
`--only-binary :all:` later on the command line and really want it to
override those earlier arguments of `--no-binary <package>` for safety.
Right now we just fail to solve though since we can't satisfy the
overlapping requests.

Closes https://github.com/astral-sh/uv/issues/4063
2024-06-12 15:47:45 -05:00
Charlie Marsh d8f1de6134
Use separate path types for directories and files (#4285)
## Summary

This is what I consider to be the "real" fix for #8072. We now treat
directory and path URLs as separate `ParsedUrl` types and
`RequirementSource` types. This removes a lot of `.is_dir()` forking
within the `ParsedUrl::Path` arms and makes some states impossible
(e.g., you can't have a `.whl` path that is editable). It _also_ fixes
the `direct_url.json` for direct URLs that refer to files. Previously,
we wrote out to these as if they were installed as directories, which is
just wrong.
2024-06-12 15:59:21 -04:00
Andrew Gallant 75b323232d uv-resolver: use Requires-Python to filter dependencies during universal resolution
In the time before universal resolving, we would always pass a
`MarkerEnvironment`, and this environment would capture any relevant
`Requires-Python` specifier (including if `-p/--python` was provided on
the CLI).

But in universal resolution, we very specifically do not use a
`MarkerEnvironment` because we want to produce a resolution that is
compatible across potentially multiple environments. This in turn meant
that we lost `Requires-Python` filtering.

This PR adds it back. We do this by converting our `PythonRequirement`
into a `MarkerTree` that encodes the version specifiers in a
`Requires-Python` specifier. We then ask whether that `MarkerTree` is
disjoint with a dependency specification's `MarkerTree`. If it is, then
we know it's impossible for that dependency specification to every be
true, and we can completely ignore it.
2024-06-12 13:30:47 -04:00
Charlie Marsh baee826517
Use `FxHashMap` for available versions (#4278)
## Summary

I don't think we ever iterate over these in-order so I'd rather use
`FxHash` to avoid creating the appearance that the order matters.
2024-06-12 13:16:37 -04:00
Charlie Marsh 44f1afd6b0
Use registry URL for fetching source distributions from lockfile (#4280)
## Summary

This is just a logic bug with no testing. We were using the registry URL
(like `https://pypi.org/simple`) as the URL from which a source
distribution should be downloaded.

Closes https://github.com/astral-sh/uv/issues/4281.

## Test Plan

`cargo test`
2024-06-12 17:01:29 +00:00
Charlie Marsh 16b4a886a8
Use consistent order for extra groups in lockfile (#4275)
## Summary

Closes #4274.
2024-06-12 11:46:16 -04:00
Charlie Marsh aef74dac2c
Allow normalization to completely eliminate markers (#4271)
## Summary

`normalize` now takes an owned value and returns `Option<MarkerTree>`,
such that if any sub-expression evaluates to `true`, we can normalize
out the entire marker.

Closes https://github.com/astral-sh/uv/issues/4267.
2024-06-12 10:25:43 -04:00
Charlie Marsh 8a8e1af513
Deduplicate markers during normalization (#4263)
## Summary

We need to sort _before_ deduplicating; otherwise, we can't detect
adjacent elements, so we aren't guaranteed to deduplicate anything.
2024-06-12 02:38:15 +00:00
Charlie Marsh 6dae1920af
Make missing `METADATA` file a recoverable error (#4247)
## Summary

I don't have a great way to test it, but this makes the error described
in https://github.com/astral-sh/uv/issues/4246 an incompatibility rather
than a fatal error.

Closes https://github.com/astral-sh/uv/issues/4246.
2024-06-11 19:49:38 +00:00
Charlie Marsh dce913c542
Warn when 'requires-python' does not include a lower bound (#4234)
## Summary

Closes https://github.com/astral-sh/uv/issues/4089.
2024-06-11 18:50:05 +00:00
konsti 44833801b3
Support locking relative paths (#4205)
By splitting `path` into a lockable, relative (or absolute) and an
absolute installable path and by splitting between urls and paths by
dist type, we can store relative paths in the lockfile.
2024-06-11 11:58:03 +00:00
Charlie Marsh 33cf47182f
Migrate lock errors to `thiserror` (#4225)
## Summary

Do we prefer this?
2024-06-11 07:40:48 -04:00
Charlie Marsh 0c1dcb797a
Remove usages of verbatim URL in URL resolver (#4221)
## Summary

Should be no behavior changes, but one piece of technical debt I noticed
left over in the URL resolver. We already have structured paths, so we
shouldn't need to compare verbatim URLs.
2024-06-10 21:55:48 +00:00
Charlie Marsh e31604e38b
Always install as editable when duplicates are requested (#4208)
## Summary

If the user requests a package as both editable and non-editable, the
editable now "wins".

Previously, `pip install -e . .` would install as editable. However,
`pip install -e . -r requirements.txt` would _not_ if `requirements.txt`
contained `.`, because we ignored `editable` when deduplicating and the
order of iteration was just dependent on internals.

Closes https://github.com/astral-sh/uv/issues/4053.
2024-06-10 15:02:17 -04:00
Charlie Marsh 04c4da4e65
Fix `>=` to `==` typo in `is_contained_by` docs (#4196) 2024-06-10 13:28:33 +00:00
Charlie Marsh 72bc739a64
Remove `PubGrubPackage` dependency from `ResolutionGraph` (#4168)
## Summary

Similar to how we abstracted the dependencies into
`ResolutionDependencyNames`, I think it makes sense to abstract the base
packages into a `ResolutionPackage`. This also avoids leaking details
about the various `PubGrubPackage` enum variants to `ResolutionGraph`.
2024-06-10 12:50:32 +00:00
konsti 18b40b0c7d
Don't panic with invalid wheel source (#4191)
Remove the panic when there is an invalid wheel source, instead surface
the error. This error can only occur when manually editing the lock
file, but since it's an external file, we should error and not panic.

This change is helpful since the method needs to be able to error for
relative path support.
2024-06-10 08:44:36 -04:00
Charlie Marsh 763e2d2e84
Add markers to edges rather than distributions (#4166)
## Summary

We've debated this a bit but the thing that tipped me over the edge is
https://github.com/astral-sh/uv/issues/4157. As-is, there's no way to
represent "a package should be installed, but the extra should only be
installed conditionally based on the markers", because the markers sit
on the _distribution_. By placing the markers on the edge, we can now
represent scenarios that weren't previously representable.

Closes https://github.com/astral-sh/uv/issues/4137.
Closes https://github.com/astral-sh/uv/issues/4125.
Closes https://github.com/astral-sh/uv/issues/4157.
2024-06-10 08:40:51 -04:00
Charlie Marsh 5269a0dba8
Ignore tags in universal resolution (#4174)
## Summary

If a package lacks a source distribution, and we can't find a compatible
wheel for the current platform, we need to just _assume_ that the
package will have a valid wheel on all platforms on which it's
requested; if not, we raise an error at install time.

It's possible that we can be smarter about this over time. For example,
if the package was requested _only_ for macOS, we could verify that
there's at least one macOS-compatible wheel. See the linked issue for
more details.

Closes https://github.com/astral-sh/uv/issues/4139.
2024-06-10 08:38:21 -04:00
Charlie Marsh e7c573cfcb
Fix existing typos and enable `typos` in CI (#4184) 2024-06-10 01:50:54 +00:00
Charlie Marsh 8ae5c2aee3
Skip version iteration for non-base packages (#4167) 2024-06-08 18:46:08 +00:00
Charlie Marsh ab0f8afe1f
Remove version from graph edge (#4165)
## Summary

We're actually not using the edge data, so lets remove it.
2024-06-08 17:39:02 +00:00
Charlie Marsh eb239ff640
Cap `Requires-Python` comparisons at the patch version (#4150)
## Summary

See the long comment inline. I think this is debatable but probably
right for now. The other options have their own problems, but there are
a few alternate ideas in the comment.

Closes https://github.com/astral-sh/uv/issues/4132.
2024-06-08 01:22:57 +00:00
Andrew Gallant c46fa74e65
make universal resolver fork only when markers are disjoint (#4135)
The basic idea here is to make it so forking can only ever result in a
resolution that, for a particular marker environment, will only install
at most one version of a package. We can guarantee this by ensuring we
only fork on conflicting dependency specifications only when their
corresponding markers are completely disjoint. If they aren't, then
resolution _must_ find a single version of the package in the
intersection of the two dependency specifications.

A test for this case has been added to packse here:
https://github.com/astral-sh/packse/pull/182. Previously, that test
would result in a resolution with two different unconditional versions
of the same package. With this change, resolution fails (as it should).

A commit-by-commit review should be helpful here, since the first commit
is a refactor to make the second commit a bit more digestible.
2024-06-07 23:40:55 +00:00
Charlie Marsh 0db1bf4df7
Avoid pre-fetching for unbounded minimum versions (#4149)
## Summary

I think we should be able to model PubGrub such that this isn't
necessary (at least for the case described in the issue), but for now,
let's just avoid attempting to build very old distributions in
prefetching.

Closes https://github.com/astral-sh/uv/issues/4136.
2024-06-07 22:05:14 +00:00
Ibraheem Ahmed 7232c53718
Simplify marker expressions in lockfile (#4066)
## Summary

Simplify and normalize marker expressions in the lockfile. Right now
this does a simple analysis by only looking at related operators at the
same level of precedence. I think anything more complex would be out of
scope.

Resolves https://github.com/astral-sh/uv/issues/4002.
2024-06-07 16:14:24 -04:00
Charlie Marsh bcfe88dfdc
Track `Markers` via a PubGrub package variant (#4123)
## Summary

This PR adds a lowering similar to that seen in
https://github.com/astral-sh/uv/pull/3100, but this time, for markers.
Like `PubGrubPackageInner::Extra`, we now have
`PubGrubPackageInner::Marker`. The dependencies of the `Marker` are
`PubGrubPackageInner::Package` with and without the marker.

As an example of why this is useful: assume we have `urllib3>=1.22.0` as
a direct dependency. Later, we see `urllib3 ; python_version > '3.7'` as
a transitive dependency. As-is, we might (for some reason) pick a very
old version of `urllib3` to satisfy `urllib3 ; python_version > '3.7'`,
then attempt to fetch its dependencies, which could even involve
building a very old version of `urllib3 ; python_version > '3.7'`. Once
we fetch the dependencies, we would see that `urllib3` at the same
version is _also_ a dependency (because we tack it on). In the new
scheme though, as soon as we "choose" the very old version of `urllib3 ;
python_version > '3.7'`, we'd then see that `urllib3` (the base package)
is also a dependency; so we see a conflict before we even fetch the
dependencies of the old variant.

With this, I can successfully resolve the case in #4099.

Closes https://github.com/astral-sh/uv/issues/4099.
2024-06-07 19:57:02 +00:00
Zanie Blue 325982c418
Rename `uv-interpreter` crate to `uv-toolchain` (#4120)
In preparation for managed toolchains #2607, just renames the crate to
something broader.

See #4121 and https://github.com/astral-sh/uv/pull/4138 to see the final
intent.
2024-06-07 13:59:14 -05:00
Charlie Marsh 2803a8c475
Omit URL dependencies from pre-release hints (#4140)
## Summary

Closes https://github.com/astral-sh/uv/issues/4127.
2024-06-07 18:43:50 +00:00
Charlie Marsh 7d1b7b99d9
Rename `Dependency.id` to `Dependency.distribution_id` (#4114)
## Summary

I think this makes clearer that the `Dependency.id` is not an identifier
for the dependency itself.

No functional changes.
2024-06-07 18:28:54 +00:00
Charlie Marsh fa10679275
Add extra and dev dependency validation to lockfile (#4112)
## Summary

Closes https://github.com/astral-sh/uv/issues/4106.

Closes https://github.com/astral-sh/uv/issues/4115.
2024-06-07 14:18:31 -04:00
Charlie Marsh cc7c780523
Remove PubGrub dependency from `uv` (#4116)
## Summary

Encapsulates more of the details are `Requires-Python` and PubGrub.

Closes https://github.com/astral-sh/uv/issues/4110.
2024-06-06 23:45:58 +00:00
Charlie Marsh 677a7f157b
Avoid showing dev hints for Python requirements (#4111)
Closes https://github.com/astral-sh/uv/issues/4096.
2024-06-06 19:58:30 +00:00
konsti e4e2590076
Use union of `requires-python` in workspace (#4041)
Follow-up to #4016.

This exposes `Range` and `PubGrubSpecifier` from outside the resolver to
use pubgrub's union creating a dependency edge we don't really want.
2024-06-06 19:21:02 +00:00
konsti a6f53e2aa4
Lock all packages in workspace (#4016)
When creating a lockfile, lock the combined dependencies for all
packages in a workspace. This make the lockfile independent of where you
are in the workspace.

Fixes #3983
2024-06-06 19:09:44 +00:00
Charlie Marsh 31bb01f0be
Ignore upper-bounds on `Requires-Python` (#4086)
## Summary

This PR modifies our `Requires-Python` handling to treat
`Requires-Python` as a lower bound. There's extensive discussion around
this in https://github.com/astral-sh/uv/issues/4022 and the references
linked therein. I think it's an experiment worth trying. Even in my own
small projects, I'm running into issues whereby I'm being "forced" to
add a `<4` upper bound to my `Requires-Python` due to these caps.

Separately, we should explore adding a mechanism that's distinct from
`Requires-Python` to enable users to declare a supported range for
locking.

Closes https://github.com/astral-sh/uv/issues/4022.
2024-06-06 13:52:57 -04:00
Charlie Marsh 30e73a60de
Avoid enforcing distribution ID uniqueness for extras (#4104)
## Summary

The condition enforced here isn't quite right. The same dependency can
appear multiple times, as long as the extra is different.

Closes https://github.com/astral-sh/uv/issues/4101.
2024-06-06 15:21:31 +00:00
Charlie Marsh 8798e91dd5
Avoid extra-only filtering for constraints (#4095)
## Summary

The "only include if relevant for the extra" filtering should _not_ be
applied to constraints. Otherwise, we'd only constrain when the extra
was included in the constraints file itself, which is incorrect.

Closes https://github.com/astral-sh/uv/issues/4091.
2024-06-06 13:58:46 +00:00
Charlie Marsh 120148f0a1
Remove integration tests from `uv-resolver` (#4083)
## Summary

I don't think it's worth maintaining this separate test harness for ~18
tests, when they can all be tested in the `uv` package itself. Let's
drop the maintenance burden.
2024-06-06 01:48:42 +00:00
Charlie Marsh 0acae9bd9c
Add support for development dependencies (#4036)
## Summary

Externally, development dependencies are currently structured as a flat
list of PEP 580-compatible requirements:

```toml
[tool.uv]
dev-dependencies = ["werkzeug"]
```

When locking, we lock all development dependencies; when syncing, users
can provide `--dev`.

Internally, though, we model them as dependency groups, similar to
Poetry, PDM, and [PEP 735](https://peps.python.org/pep-0735). This
enables us to change out the user-facing frontend without changing the
internal implementation, once we've decided how these should be exposed
to users.

A few important decisions encoded in the implementation (which we can
change later):

1. Groups are enabled globally, for all dependencies. This differs from
extras, which are enabled on a per-requirement basis. Note, however,
that we'll only discover groups for uv-enabled packages anyway.
2. Installing a group requires installing the base package. We rely on
this in PubGrub to ensure that we resolve to the same version (even
though we only expect groups to come from workspace dependencies anyway,
which are unique). But anyway, that's encoded in the resolver right now,
just as it is for extras.
2024-06-06 01:40:17 +00:00
Charlie Marsh 0b6d5b37dc
Enforce `Requires-Python` when syncing (#4068)
## Summary

Ensures that we raise if the user attempts to use a Python version that
wasn't included in the locked range.

Closes https://github.com/astral-sh/uv/issues/4052.
2024-06-05 16:22:08 -04:00
Charlie Marsh 642cef0dad
Track supported Python range in lockfile (#4065)
## Summary

This PR adds the `Requires-Python` range to the user's lockfile. This
will enable us to validate it when installing.

For now, we repeat the `Requires-Python` back to the user;
alternatively, though, we could detect the supported Python range
automatically.

See: https://github.com/astral-sh/uv/issues/4052
2024-06-05 16:21:59 -04:00
konsti 8596525d97
Update pubgrub to new `add_incompatibility_from_dependencies` (#4062)
We had previously changed the signature of
`DependencyProvider::get_dependencies` to return an iterator instead of
a hashmap to avoid the conversion cost from our dependencies `Vec` to
the pubgrub's hashmap. These changes are difficult to make in pubgrub
since they complicate the public api. But we don't actually use
`DependencyProvider::get_dependencies`, so we rolled those
customizations back in https://github.com/pubgrub-rs/pubgrub/pull/226
and instead opted to change only the internal
`add_incompatibility_from_dependencies` method that we exposed in our
fork. This aligns us closer with upstream, removes the design questions
about `DependencyProvider` from our concerns and reduces our diff (not
counting the github action) to +36 -12.
2024-06-05 20:46:00 +02:00
Charlie Marsh ae9610104a
Avoid dropping `pip sync` requirements with markers (#4051)
## Summary

Thankfully this is pretty rare since `pip sync` is usually run on `pip
compile` output, and `pip compile` never outputs markers.

Closes https://github.com/astral-sh/uv/issues/4044
2024-06-05 16:05:46 +00:00
Charlie Marsh 6d148da0ab
Preserve fragments when applying verbatim redirects (#4038)
## Summary

`echo
"git+https://github.com/pypa/sample-namespace-packages.git#subdirectory=pkg_resources/pkg_a"
| cargo run pip compile -` now resolves to a compliant URL.

Closes https://github.com/astral-sh/uv/issues/4037.
2024-06-05 03:53:23 +00:00
Charlie Marsh 27e5bfe397
Remove unnecessary requirements conversions (#4035) 2024-06-05 01:52:16 +00:00
Charlie Marsh 8de3e38b94
Read all extras directly from lockfile (#4033)
## Summary
We now pass `ExtrasSpecification` to the lock routine.
2024-06-05 01:03:55 +00:00
Charlie Marsh 57ea55d218
Add a hint for `Requires-Python` (#4021)
## Summary

As requested in the originating PR.
2024-06-04 16:52:15 -04:00
Charlie Marsh 420333a40e
Avoid 'are incompatible' for singular bounded versions (#4003)
## Summary

Not sure if this is worth the complexity, but it does read better.
2024-06-04 19:17:36 +00:00
Andrew Gallant 5c30b39fe3
uv-resolver: normalize marker expressions (#4017)
This is a quick fix for some flaky tests where the output in the lock
file isn't stable because marker expressions can be combined in a
non-deterministic order.

I believe there is ongoing work to simplify marker expressions which
will help here, but I think some kind of normalization is still
ultimately needed to guarantee consistent output.

I first noticed the flaky test in:
https://github.com/astral-sh/uv/pull/4015
2024-06-04 13:45:54 -04:00
Charlie Marsh 6afb659c9a
Respect `Requires-Python` in universal resolution (#3998)
## Summary

Closes #3982.
2024-06-04 13:56:08 +00:00
konsti 36f7fa3917
`impl TryFrom<&VersionSpecifiers> for PubGrubSpecifier` (#4010)
Add a missing utility conversion method from PEP 440 versions specifiers
to a pubgrub range.
2024-06-04 08:00:07 +00:00
Charlie Marsh 77e93157fb
Make target Python version an optional field (#4000)
## Summary

Instead of checking if the target and installed version are the same, we
model the data such that the target version is only present if it was
specified by the user. This also means that we correctly say "requested
version" even if the two happen to be the same.
2024-06-03 22:37:15 +00:00
Charlie Marsh ef43bcb233
Remove Python from available versions (#3996)
## Summary

I believe this is no longer necessary. Part of the problem here is that
we can't _know_ the full set of available Python versions, especially
once we start resolving against a `Requires-Python` rather than a fixed
set of two versions.
2024-06-03 20:11:45 +00:00
Charlie Marsh 10cd6b94c9
Normalize extras in lockfile (#3958)
## Summary

Previously, when we locked something like `flask[dotenv]`, we created
two separate distributions in the lockfile: one for `flask`, which
included the base dependencies, and one for `flask[dotenv]`, which
included the base dependencies _and_ the `dotenv` dependencies. This was
easy to implement, but it meant that we were duplicating all of the
distribution files for every extra, and duplicating all of the base
dependencies for every extra.

This PR normalizes the data such that we now have one entry per
distribution (i.e., `ExtraName` was removed from `DistributionId`), with
an optional dependencies table with an entry per extra, like:

```toml
[[distribution]]
name = "project"
version = "0.1.0"
source = "editable+file://[TEMP_DIR]/"
sdist = { url = "file://[TEMP_DIR]/" }

[[distribution.dependencies]]
name = "anyio"
version = "3.7.0"
source = "registry+https://pypi.org/simple"

[distribution.optional-dependencies]

[[distribution.optional-dependencies.test]]
name = "iniconfig"
version = "2.0.0"
source = "registry+https://pypi.org/simple"
```

This requires a bit more work upfront, because we now need to merge
multiple packages from the `PetGraph` representation when creating the
lockfile.

Closes https://github.com/astral-sh/uv/issues/3916.
2024-06-03 19:00:35 +00:00
Charlie Marsh 362b00cc12
Remove need to return Python version in `get_dependencies` (#3993)
## Summary

Once we use a _range_ rather than a precise version, it won't actually
make sense to return a version here. It's no longer required, so I'm
removing it.
2024-06-03 18:42:38 +00:00
Charlie Marsh 1a60368ce4
Use `PubGrubPython` type in Python incompatibility reporting (#3992)
## Summary

Rather than re-testing compatibility, I think we can just rely on the
types directly.
2024-06-03 14:32:22 -04:00
Charlie Marsh a23ca5bab7
Use universal resolution in `uv lock` (#3969)
## Summary

Wires up the optional markers in resolution, and adds
respecting-the-markers to `Lock:: to_resolution`.
2024-06-02 21:33:18 -04:00
Charlie Marsh c500b78936
Avoid re-adding solutions to forked state (#3967)
## Summary

Running a resolution that required forking was failing due to breaking
an invariant in PubGrub. It looks like we were adding the same
incompatibility multiple times, or something like that. The issue
appears to be that when forking, we modify the current state, then clone
it as the "next state", then push to the "forked states" -- but that
means we're cloning the _modified_ state.

This PR changes the order of operations such that we clone, then modify.
It shouldn't introduce any additional clones though.
2024-06-02 17:58:25 -04:00
Charlie Marsh 11324646cb
Remove some `anyhow` usages (#3962) 2024-06-01 20:11:23 +00:00
Charlie Marsh a70e33d947
Move reference check into `uv-git` (#3961) 2024-06-01 16:02:25 -04:00
Charlie Marsh c04a95e037
Respect resolved Git SHAs in `uv lock` (#3956)
## Summary

This PR ensures that if a lockfile already contains a resolved reference
(e.g., you locked with `main` previously, and it locked to a specific
commit), and you run `uv lock`, we use the same SHA, even if it's not
the latest SHA for that tag. This avoids upgrading Git dependencies
without `--upgrade`.

Closes #3920.
2024-06-01 12:40:11 +00:00
Charlie Marsh b7d77c04cc
Add Git resolver in lieu of static hash map (#3954)
## Summary

This PR removes the static resolver map:

```rust
static RESOLVED_GIT_REFS: Lazy<Mutex<FxHashMap<RepositoryReference, GitSha>>> =
    Lazy::new(Mutex::default);
```

With a `GitResolver` struct that we now pass around on the
`BuildContext`. There should be no behavior changes here; it's purely an
internal refactor with an eye towards making it cleaner for us to
"pre-populate" the list of resolved SHAs.
2024-05-31 22:44:42 -04:00
konsti 081f20c53e
Add support for `tool.uv` into distribution building (#3904)
With the change, we remove the special casing of workspace dependencies
and resolve `tool.uv` for all git and directory distributions. This
gives us support for non-editable workspace dependencies and path
dependencies in other workspaces. It removes a lot of special casing
around workspaces. These changes are the groundwork for supporting
`tool.uv` with dynamic metadata.

The basis for this change is moving `Requirement` from
`distribution-types` to `pypi-types` and the lowering logic from
`uv-requirements` to `uv-distribution`. This changes should be split out
in separate PRs.

I've included an example workspace `albatross-root-workspace2` where
`bird-feeder` depends on `a` from another workspace `ab`. There's a
bunch of failing tests and regressed error messages that still need
fixing. It does fix the audited package count for the workspace tests.
2024-05-31 02:42:03 +00:00
Ibraheem Ahmed 85183c1c36
Custom lock-file serialization (#3909)
## Summary

This PR changes the lock-file format to use inline tables for wheels and
source distributions, which currently use separate tables that make the
file harder to follow.

```diff
[[distribution]]
name = "typing-extensions"
version = "4.10.0"
source = "registry+https://pypi.org/simple"

- [distribution.sdist]
- url = "0d26ce356c7c323176620b7b483e44/typing_extensions-4.10.0.tar.gz"
- hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"
- size = 77558
-
- [[distribution.wheel]]
- url = "dc04a3ea60b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl"
- hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"
- size = 33926

+ sdist = { url = "0d26ce356c7c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 }
+ wheel = [{ url = "dc04a3ea60b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }]
```

The downside is that the inline-tables end up quite long and TOML
doesn't support line breaks in inline tables, yet.

Part of https://github.com/astral-sh/uv/issues/3611.
2024-05-30 19:08:29 +00:00
Andrew Gallant d3b7d800ea uv-resolver: fix perf regression
We significantly regressed performance in some cases because we were
cloning the resolver state one more time than we needed to. That doesn't
sound like a lot, but in the case where there are no forks, it implies
we were cloning the state for every `get_dependencies` called when we
shouldn't have been cloning it at all.

Avoiding the clone results in somewhat tortured code. This can probably
be refactored by moving bits out to a helper routine, but that also
seemed non-trivial. So we let this suffice for now.
2024-05-30 14:23:14 -04:00
Andrew Gallant 17c043536b uv-resolver: thread markers through the resolver and into the lock file
This addresses the lack of marker support in prior commits.
Specifically, we add them as a new field to `AnnotatedDist`, and from
there, they get added to a `Distribution` in a `Lock`.
2024-05-30 14:23:14 -04:00
Andrew Gallant f865406ab4 uv-resolver: implement merging of forked resolutions
This commit is a pretty invasive change that implements the merging
of resolutions created by each fork of the resolver.

The main idea here is that each `SolveState` is converted into a
`Resolution` (a new type) and stored on the heap after its fork
completes. When all forks complete, they are all merged into a single
`Resolution`. This `Resolution` is then used to build a `ResolutionGraph`.

Construction of `ResolutionGraph` mostly stays the same (despite the
gnarly diff due to an indent change) with one exception: the code to
extract dependency edges out of PubGrub's state has been moved to
`SolveState::into_resolution`. The idea here is that once a fork
completes, we extract what we need from the PubGrub state and then
throw it away. We store these edges in our own intermediate type which
is then converted into petgraph edges in the `ResolutionGraph`
constructor.

One interesting change we make here is that our edge
data is now a `Version` instead of a `Range<Version>`. I don't think
`Range<Version>` was actually being used anywhere, so this seems okay?
In any case, I think `Version` here is correct because a resolution
corresponds to specific dependencies of each package. Moreover, I didn't
see an easy way to make things work with `Range<Version>`. Notably,
since we no longer have the guarantee that there is only one version of
each package, we need to use `(PackageName, Version)` instead of just
`PackageName` for inverted lookups in `ResolutionGraph::from_state`.

Finally, the main resolver loop itself is changed a bit to track all
forked resolutions and then merge them at the end.

Note that we don't really have any dealings with markers in this commit.
We'll get to that in a subsequent commit.
2024-05-30 14:23:14 -04:00
Andrew Gallant 9e977aa1be uv-resolver: slightly simplify ResolutionGraph::from_state
This changes the constructor to just take an `InMemoryIndex`
directly instead of the constituent parts. No real reason other
than it seems a little simpler.
2024-05-30 14:23:14 -04:00
Andrew Gallant 6f76a66510 uv-resolver: implement basic resolver forking
There are still some TODOs/FIXMEs here, but this makes represents a
chunk of the resolver refactoring to enable forking. We don't do any
merging of resolutions yet, so crucially, this code is broken when no
marker environment is provided. But when a marker environment is
provided, this should behave the same as a non-forking resolver. In
particular, `get_dependencies_forking` is just `get_dependencies`
whenever there's a marker environment.
2024-05-30 14:23:14 -04:00
Charlie Marsh 144566907e
Use lockfile versions as resolution preferences (#3921)
## Summary

Ensures that we avoid upgrading packages unless `--upgrade` or similar
is passed.

For now, the resolver only respects these for registry distributions.

Closes https://github.com/astral-sh/uv/issues/3918.
2024-05-30 17:59:53 +00:00
Charlie Marsh 502e04200d
Remove `from_registry_built_dist2` (#3922)
## Summary

I think this was left over from a prior refactor. (The existing
`from_registry_built_dist` was unused.)
2024-05-30 01:57:19 +00:00
Charlie Marsh 4859a27948
Add extra dependency annotations to lockfile and sync commands (#3913)
## Summary

This PR adds extras to the lockfile, and enables users to selectively
sync extras in `uv sync` and `uv run`. The end result here was fairly
simple, though it required a few refactors to get here. The basic idea
is that `DistributionId` now includes `extra: Option<ExtraName>`, so we
effectively treat extras as separate packages. Generating the lockfile,
and generating the resolution from the lockfile, fall out of this
naturally with no special-casing or additional changes.

The main downside here is that it bloats the lockfile significantly.
Specifically:

- We include _all_ distribution URLs and hashes for _every_ extra
variant.
- We include all dependencies for the extra variant, even though that
are dependencies of the base package.

We could normalize this representation by changing each distribution
have an `optional-dependencies` hash map that keys on extras, but we
actually don't have the information we need to create that right now
(specifically, we can't differentiate between dependencies that
_require_ the extra and dependencies on the base package).

Closes #3700.
2024-05-29 19:25:58 +00:00
Charlie Marsh 1bd5d8bc34
Include all extras when generating lockfile (#3912)
## Summary

This PR just ensures that when running `uv lock` (or `uv run`), we lock
with all extras. When we later install, we'll also _install_ with all
extras, but that will be changed in a future PR.
2024-05-29 15:08:20 -04:00
Charlie Marsh ed7f55606d
Split `requirements.txt`-style resolution distribution into its own type (#3911) 2024-05-29 16:48:28 +00:00
Charlie Marsh 19c91e7dac
Create distinct graph nodes for each package extra (#3908)
## Summary

Today, we represent each package as a single node in the graph, and
combine all the extras. This is helpful for the `requirements.txt`-style
resolution, in which we want to show each a single line for each package
with the extras combined into a single array.

This PR modifies the representation to instead use a separate node for
each (package, extra) pair. We then reduce into the previous format when
printing in the `requirements.txt`-style format, so there shouldn't be
any user-facing changes here.
2024-05-29 15:42:49 +00:00
Charlie Marsh 42b1ba04ec
Remove unused `PetGraph` weight (#3906) 2024-05-29 15:16:25 +00:00
Charlie Marsh cedd18e4c6
Remove some unused `pub` functions (#3872)
## Summary

I wrote a bad Python script to find these.
2024-05-28 15:58:13 +00:00
Charlie Marsh 1fc6a59707
Remove special-casing for editable requirements (#3869)
## Summary

There are a few behavior changes in here:

- We now enforce `--require-hashes` for editables, like pip. So if you
use `--require-hashes` with an editable requirement, we'll reject it. I
could change this if it seems off.
- We now treat source tree requirements, editable or not (e.g., both `-e
./black` and `./black`) as if `--refresh` is always enabled. This
doesn't mean that we _always_ rebuild them; but if you pass
`--reinstall`, then yes, we always rebuild them. I think this is an
improvement and is close to how editables work today.

Closes #3844.

Closes #2695.
2024-05-28 15:49:34 +00:00
Charlie Marsh 14fa49b7ba
Move availability enums into their own module (#3858) 2024-05-27 00:12:53 -04:00
Ibraheem Ahmed 7dc322665c
Concurrent progress bars (#3252)
## Summary

Implements concurrent progress bars. Resolves
https://github.com/astral-sh/uv/issues/1209.

## Test Plan

https://github.com/astral-sh/uv/assets/34988408/b21bdfbb-8817-4873-a65c-16c9e8c7c460
2024-05-27 01:21:07 +00:00
Charlie Marsh 6682765630
Require pinned version in `Preference` type (#3828)
## Summary

This PR makes a variety of invalid states unrepresentable by changing
`Preference` to require a `PackageName` and `Version`, rather than
accepting a generic `Requirement`. There should be no meaningful
behavior changes.
2024-05-24 18:48:36 +00:00
Charlie Marsh 688f4f43d4
Ignore unnamed requirements in preferences (#3826)
## Summary

We actually _already_ ignore these (preferences only apply to versions,
not URLs), it just happens later on. This PR thus just avoids crashing.
The behavior is unchanged.

Closes #3822.
2024-05-24 17:32:20 +00:00
Charlie Marsh 999d072ae9
Always include package names for Git and HTTPS dependencies (#3821)
## Summary

Related to https://github.com/astral-sh/uv/issues/3818. We should
_always_ include the package name if we know it's not a file path, even
if it starts with an environment variable.
2024-05-24 14:01:38 +00:00
Charlie Marsh a9d9a6c13f
Incorporate build tag into wheel prioritization (#3781)
## Summary

It turns out that in the
[spec](https://packaging.python.org/en/latest/specifications/binary-distribution-format/#file-name-convention),
if a wheel filename includes a build tag, then we need to use it to
break ties. This PR implements that behavior. (Previously, we dropped
the build tag entirely.)

Closes #3779.

## Test Plan

Run: `cargo run pip install -i https://pypi.anaconda.org/intel/simple
mkl_fft==1.3.8 --python-platform linux --python-version 3.10`. This now
resolves without error. Previously, we selected build tag 63 of
`mkl_fft==1.3.8`, which led to an incompatibility with NumPy. Now, we
select build tag 70.
2024-05-23 21:12:53 +00:00
konsti 4db468e27f
Use `VerbatimParsedUrl` in `pep508_rs` (#3758)
When parsing requirements from any source, directly parse the url parts
(and reject unsupported urls) instead of parsing url parts at a later
stage. This removes a bunch of error branches and concludes the work
parsing url parts once and passing them around everywhere.

Many usages of the assembled `VerbatimUrl` remain, but these can be
removed incrementally.

Please review commit-by-commit.
2024-05-23 19:52:47 +00:00
Charlie Marsh 79fecdf251
Add a diagnostic trait (#3777) 2024-05-22 19:44:37 -04:00
Charlie Marsh 74c494d7dd
Report yanks for cached and resolved packages (#3772)
## Summary

We now show yanks as part of the resolution diagnostics, so they now
appear for `sync`, `install`, `compile`, and any other operations.
Further, they'll also appear for cached packages (but not packages that
are _already_ installed).

Closes https://github.com/astral-sh/uv/issues/3768.

Closes #3766.
2024-05-22 21:21:37 +00:00
Charlie Marsh fe28b2c278
Remove unused methods from `Resolution` (#3754) 2024-05-22 18:48:44 +00:00
Charlie Marsh 0313e7d78b
Use common routines for `pip install` and `pip sync` (#3737)
## Summary

This PR takes the functions used in `pip install`, moves them into a
common module, and then replaces all the `pip sync` logic with calls
into those functions. The net effect is that `pip install` and `pip
sync` share far more code and demonstrate much more consistent behavior.

Closes https://github.com/astral-sh/uv/issues/3555.
2024-05-22 12:15:17 -04:00
Charlie Marsh e398444f2f
Track editable requirements in lockfile (#3725)
## Summary

This PR adds editables using a new source type (`editable+...`), and
then extracts the editables from the lockfile in `uv sync`.

Closes https://github.com/astral-sh/uv/issues/3695.
2024-05-22 09:06:18 -04:00
Zanie Blue c14a7dbef3
Improve display of root package in range errors (#3711)
Instead of saying 

> we can conclude that you require==0a0.dev0 and
pandas-stubs==2.0.3.230814 are incompatible.

we'll say

> we can conclude that your requirements and pandas-stubs==2.0.3.230814
are incompatible.

Closes #3710 

I'm not sure how to get unit test coverage for this, might look into
that. Ideally we'd skip this branch entirely?
2024-05-21 19:28:23 +00:00
konsti 95af1db0bb
Let `RequirementSource::Path.editable` be `bool`, not `Option<bool>` (#3693)
Small refactoring of the internal representation. This does not change
`tool.uv.sources`.
2024-05-21 14:34:43 +00:00
konsti 76418f5bdf
Arc-wrap `PubGrubPackage` for cheap cloning in pubgrub (#3688)
Pubgrub stores incompatibilities as (package name, version range)
tuples, meaning it needs to clone the package name for each
incompatibility, and each non-borrowed operation on incompatibilities.
https://github.com/astral-sh/uv/pull/3673 made me realize that
`PubGrubPackage` has gotten large (expensive to copy), so like `Version`
and other structs, i've added an `Arc` wrapper around it.

It's a pity clippy forbids `.deref()`, it's less opaque than `&**` and
has IDE support (clicking on `.deref()` jumps to the right impl).

## Benchmarks

It looks like this matters most for complex resolutions which, i assume
because they carry larger `PubGrubPackageInner::Package` and
`PubGrubPackageInner::Extra` types.

```bash
hyperfine --warmup 5 "./uv-main pip compile -q ./scripts/requirements/jupyter.in" "./uv-branch pip compile -q ./scripts/requirements/jupyter.in"
hyperfine --warmup 5 "./uv-main pip compile -q ./scripts/requirements/airflow.in" "./uv-branch pip compile -q ./scripts/requirements/airflow.in"
hyperfine --warmup 5 "./uv-main pip compile -q ./scripts/requirements/boto3.in" "./uv-branch pip compile -q ./scripts/requirements/boto3.in"
```

```
Benchmark 1: ./uv-main pip compile -q ./scripts/requirements/jupyter.in
  Time (mean ± σ):      18.2 ms ±   1.6 ms    [User: 14.4 ms, System: 26.0 ms]
  Range (min … max):    15.8 ms …  22.5 ms    181 runs

Benchmark 2: ./uv-branch pip compile -q ./scripts/requirements/jupyter.in
  Time (mean ± σ):      17.8 ms ±   1.4 ms    [User: 14.4 ms, System: 25.3 ms]
  Range (min … max):    15.4 ms …  23.1 ms    159 runs

Summary
  ./uv-branch pip compile -q ./scripts/requirements/jupyter.in ran
    1.02 ± 0.12 times faster than ./uv-main pip compile -q ./scripts/requirements/jupyter.in
```

```
Benchmark 1: ./uv-main pip compile -q ./scripts/requirements/airflow.in
  Time (mean ± σ):     153.7 ms ±   3.5 ms    [User: 165.2 ms, System: 157.6 ms]
  Range (min … max):   150.4 ms … 163.0 ms    19 runs

Benchmark 2: ./uv-branch pip compile -q ./scripts/requirements/airflow.in
  Time (mean ± σ):     123.9 ms ±   4.6 ms    [User: 152.4 ms, System: 133.8 ms]
  Range (min … max):   118.4 ms … 138.1 ms    24 runs

Summary
  ./uv-branch pip compile -q ./scripts/requirements/airflow.in ran
    1.24 ± 0.05 times faster than ./uv-main pip compile -q ./scripts/requirements/airflow.in
```

```
Benchmark 1: ./uv-main pip compile -q ./scripts/requirements/boto3.in
  Time (mean ± σ):     327.0 ms ±   3.8 ms    [User: 344.5 ms, System: 71.6 ms]
  Range (min … max):   322.7 ms … 334.6 ms    10 runs

Benchmark 2: ./uv-branch pip compile -q ./scripts/requirements/boto3.in
  Time (mean ± σ):     311.2 ms ±   3.1 ms    [User: 339.3 ms, System: 63.1 ms]
  Range (min … max):   307.8 ms … 317.0 ms    10 runs

Summary
  ./uv-branch pip compile -q ./scripts/requirements/boto3.in ran
    1.05 ± 0.02 times faster than ./uv-main pip compile -q ./scripts/requirements/boto3.in
```

<!--
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?
-->
2024-05-21 13:49:35 +02:00
Charlie Marsh 49f0e84f3d
Write relative paths with unnamed requirement syntax (#3682)
## Summary

This PR falls back to writing an unnamed requirement if it appears to be
a relative URL. pip is way more flexible when providing an unnamed
requirement than when providing a PEP 508 requirement. For example,
_only_ this works:

```
black @ file:///Users/crmarsh/workspace/uv/scripts/packages/black_editable
```

Any other form will fail.

Meanwhile, _all_ of these work:

```
file:///Users/crmarsh/workspace/uv/scripts/packages/black_editable
scripts/packages/black_editable
./scripts/packages/black_editable
file:./scripts/packages/black_editable
file:scripts/packages/black_editable
```

Closes https://github.com/astral-sh/uv/issues/3180.
2024-05-20 21:22:06 -04:00
Charlie Marsh 0362918196
Evaluate arbitrary markers to `false` (#3681)
## Summary

See: https://github.com/astral-sh/uv/pull/3679#issuecomment-2121387428.

Closes: https://github.com/astral-sh/uv/issues/3675 (although I think we
have another improvement to make there -- will file separately).
2024-05-21 01:01:11 +00:00
Andrew Gallant 776a7e47f3 uv-resolver: add `Option<MarkerTree>` to `PubGrubPackage`
This just adds the field to the type and always sets it to `None`. There
are semantic changes in this commit.

Closes #3359
2024-05-20 19:56:24 -04:00
Andrew Gallant 1ed3555bf0 uv-resolver: sort in `format_terms`
This makes use of the newly added `Ord` impl on `PubGrubPackage` to make
the output of `format_terms` independent of hashmap iteration order.

This was already collecting the terms into an intermediate `Vec`, so
sorting probably isn't going to add any significant overhead here.
(Plus, this is only running when formatting an error message after a
solution could not be found, so an extra sort doesn't seem like a big
deal here.)

Note that some tests are updated in this commit as a result of this
change. As far as I can tell, the semantic meaning of the output remains
the same. But the order of the listed packages does not.

Specific thing motivating this change is, in a subsequent, I added
`Option<MarkerTree>` to `PubGrubPackage::Package`, and this caused
similar changes in test output. So I backtracked and isolated this
change from the addition of `Option<MarkerTree>`.
2024-05-20 19:56:24 -04:00
Andrew Gallant 976bc9ba0e uv-resolver: make PubGrubPackage orderable
It turns out that we use PubGrubPackage as the key in hashmaps in a fair
few places. And when we iterate over hashmaps, the order is unspecified.
This can in turn result in changes in output as a result of changes in
the PubGrubPackage definition, purely as a function of its changing
hash. This is confusing as there should be no semantic difference.

Thus, this is a precursor to introducing some more determinism to places
I found in the error reporting whose output depending on hashmap
iteration order.
2024-05-20 19:56:24 -04:00
Andrew Gallant 9f109f243c uv-resolver: remove 'derive(Derivative)' from PubGrubPackage
It looks like the last vestiges of `Derivative` were removed in commit
7eaed07f6c, but the then rendered
superfluous `derive(Derivative)` wasn't removed.
2024-05-20 19:56:24 -04:00
Andrew Gallant eac8221718 uv-resolver: use named fields for some PubGrubPackage variants
I'm planning to add another field here (markers), which puts a lot of
stress on the positional approach. So let's just switch over to named
fields.
2024-05-20 19:56:24 -04:00
konsti 95c9621541
Refactor editables for supporting them in bluejay commands (#3639)
This is split out from workspaces support, which needs editables in the
bluejay commands. It consists mainly of refactorings:

* Move the `editable` module one level up.
* Introduce a `BuiltEditableMetadata` type for `(LocalEditable,
Metadata23, Requirements)`.
* Add editables to `InstalledPackagesProvider` so we can use
`EmptyInstalledPackages` for them.
2024-05-20 16:22:12 +00:00
Charlie Marsh f3965fef5e
Use filename trait for `WheelWire` conversion (#3651)
## Summary

The main motivation here is that the `.filename()` method that we
implement on `Url` will do URL decoding for the last segment, which we
were missing here.

The errors are a bit awkward, because in
`crates/uv-resolver/src/lock.rs`, we wrap in `failed to extract filename
from URL: {url}`, so in theory we want the underlying errors to _omit_
the URL? But sometimes they use `#[error(transparent)]`?
2024-05-20 09:25:31 -04:00
Charlie Marsh 657eebd50b
Remove `SourceDistFilename` from `RegistrySourceDist` (#3650)
## Summary

Uncertain about this, but we don't actually need the full
`SourceDistFilename`, only the name and version -- and we often have
that information already (as in the lockfile routines). So by flattening
the fields onto `RegistrySourceDist`, we can avoid re-parsing for
information we already have.
2024-05-20 09:25:23 -04:00
Charlie Marsh 1124df9bc5
Remove subdirectory from direct wheel URL type (#3667)
## Summary

Closes #3665.
2024-05-20 02:01:57 +00:00
Charlie Marsh 0718705c21
Track parsed Git URL components in `GitSourceUrl` (#3656)
## Summary

Closes https://github.com/astral-sh/uv/issues/3571.
2024-05-20 00:43:30 +00:00
Charlie Marsh a0745d0d9d
Add registry file size to lockfile (#3652)
## Summary

Mentioned in #3611.
2024-05-19 02:27:05 +00:00
Charlie Marsh 18b095ce28
Make `from_rev` take an owned value (#3631)
## Summary

We always clone internally, and in most case we're already passing
`&String`.
2024-05-18 17:26:15 +00:00
Charlie Marsh 47f4114a1b
Add registry source distribution support to lockfile (#3649)
## Summary

One TODO but going to resolve that separately.
2024-05-18 16:54:27 +00:00
Charlie Marsh fe2bc079bc
Use `ParsedGitUrl` to recreate Git source kind URL (#3647) 2024-05-17 21:45:46 +00:00
Charlie Marsh e2d7d2026b
Add direct URL conversion to lockfile (#3633)
## Summary

Similar to #3630, but for direct URL distributions (for both wheels and
source distributions).
2024-05-17 21:32:42 +00:00
Charlie Marsh 0d512be46c
Support lossless serialization for Git dependencies in lockfile (#3630)
## Summary

This PR adds lossless deserialization for `GitSourceDist` distributions
in the lockfile. Specifically, we now properly preserve the requested
revision, the subdirectory, and the precise Git commit SHA.

## Test Plan

`cargo test`
2024-05-17 21:23:40 +00:00
Ibraheem Ahmed 0f67a6ceea
Use `FxHasher` in resolver (#3641)
## Summary

We can use `FxHasher` in a few more places for string and version keys.
This gives a consistent ~2% improvement to warm resolves.
2024-05-17 15:04:22 -04:00
Ibraheem Ahmed 39af09f09b
Parallelize resolver (#3627)
## Summary

This PR introduces parallelism to the resolver. Specifically, we can
perform PubGrub resolution on a separate thread, while keeping all I/O
on the tokio thread. We already have the infrastructure set up for this
with the channel and `OnceMap`, which makes this change relatively
simple. The big change needed to make this possible is removing the
lifetimes on some of the types that need to be shared between the
resolver and pubgrub thread.

A related PR, https://github.com/astral-sh/uv/pull/1163, found that
adding `yield_now` calls improved throughput. With optimal scheduling we
might be able to get away with everything on the same thread here.
However, in the ideal pipeline with perfect prefetching, the resolution
and prefetching can run completely in parallel without depending on one
another. While this would be very difficult to achieve, even with our
current prefetching pattern we see a consistent performance improvement
from parallelism.

This does also require reverting a few of the changes from
https://github.com/astral-sh/uv/pull/3413, but not all of them. The
sharing is isolated to the resolver task.

## Test Plan

On smaller tasks performance is mixed with ~2% improvements/regressions
on both sides. However, on medium-large resolution tasks we see the
benefits of parallelism, with improvements anywhere from 10-50%.

```
./scripts/requirements/jupyter.in
Benchmark 1: ./target/profiling/baseline (resolve-warm)
  Time (mean ± σ):      29.2 ms ±   1.8 ms    [User: 20.3 ms, System: 29.8 ms]
  Range (min … max):    26.4 ms …  36.0 ms    91 runs
 
Benchmark 2: ./target/profiling/parallel (resolve-warm)
  Time (mean ± σ):      25.5 ms ±   1.0 ms    [User: 19.5 ms, System: 25.5 ms]
  Range (min … max):    23.6 ms …  27.8 ms    99 runs
 
Summary
  ./target/profiling/parallel (resolve-warm) ran
    1.15 ± 0.08 times faster than ./target/profiling/baseline (resolve-warm)
```
```
./scripts/requirements/boto3.in   
Benchmark 1: ./target/profiling/baseline (resolve-warm)
  Time (mean ± σ):     487.1 ms ±   6.2 ms    [User: 464.6 ms, System: 61.6 ms]
  Range (min … max):   480.0 ms … 497.3 ms    10 runs
 
Benchmark 2: ./target/profiling/parallel (resolve-warm)
  Time (mean ± σ):     430.8 ms ±   9.3 ms    [User: 529.0 ms, System: 77.2 ms]
  Range (min … max):   417.1 ms … 442.5 ms    10 runs
 
Summary
  ./target/profiling/parallel (resolve-warm) ran
    1.13 ± 0.03 times faster than ./target/profiling/baseline (resolve-warm)
```
```
./scripts/requirements/airflow.in 
Benchmark 1: ./target/profiling/baseline (resolve-warm)
  Time (mean ± σ):     478.1 ms ±  18.8 ms    [User: 482.6 ms, System: 205.0 ms]
  Range (min … max):   454.7 ms … 508.9 ms    10 runs
 
Benchmark 2: ./target/profiling/parallel (resolve-warm)
  Time (mean ± σ):     308.7 ms ±  11.7 ms    [User: 428.5 ms, System: 209.5 ms]
  Range (min … max):   287.8 ms … 323.1 ms    10 runs
 
Summary
  ./target/profiling/parallel (resolve-warm) ran
    1.55 ± 0.08 times faster than ./target/profiling/baseline (resolve-warm)
```
2024-05-17 11:47:30 -04:00
Ahmed Ilyas e3e7895605
Fix source annotation in pip compile `annotation-style=line` output (#3637)
## Summary

Fixes a small discrepancy between the pip compile outputs for
`annotation-style=split` and `annotation-style=line` commands.

### Problem

Consider the following `pyproject.toml` file.
```sh
$ cat pyproject.toml
[project]
name = "uv_test"
dynamic = ["version"]
dependencies = ["click"]
```

Running uv pip compile with annotation-style=split on uv 0.1.44 version
yields the following:
```sh
❯ uv pip compile pyproject.toml --annotation-style=split
Resolved 1 package in 2ms
# This file was autogenerated by uv via the following command:
#    uv pip compile pyproject.toml --annotation-style=split
click==8.1.7
    # via uv-test (pyproject.toml)
```

However, running uv pip compile with annotation-style=line doesn't
include source info for root level dependencies.
```sh
❯ uv pip compile pyproject.toml --annotation-style=line
Resolved 1 package in 1ms
# This file was autogenerated by uv via the following command:
#    uv pip compile pyproject.toml --annotation-style=line
click==8.1.7
```

With this PR:
```sh
❯ ../target/debug/uv pip compile --annotation-style=line pyproject.toml
Resolved 1 package in 6ms
# This file was autogenerated by uv via the following command:
#    uv pip compile --annotation-style=line pyproject.toml
click==8.1.7              # via uv-test (pyproject.toml)
```

This also now matches `pip-tools` output:
```sh
❯ pip-compile --annotation-style=line pyproject.toml
#
# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
#    pip-compile --annotation-style=line pyproject.toml
#
click==8.1.7              # via uv_test (pyproject.toml)

```

## Test Plan

`cargo test`
2024-05-17 09:57:11 -04:00
Andrew Gallant 0a055b7942
distribution-types: allow RegistrySourceDist to have wheels attached to it (#3610)
Following from #3595, we'd like wheels to make their way into the lock
file even if the current environment selects an sdist. With #3595, this
didn't happen:

$ cargo run -p uv -- pip compile -p3.10 <(echo psycopg2)
--unstable-uv-lock-file
    $ cat uv.lock
    version = 1

    [[distribution]]
    name = "psycopg2"
    version = "2.9.9"
    source = "registry+https://pypi.org/simple"

    [distribution.sdist]
url =
"dc6acaf46d76fce95daac5e0f0301b/psycopg2-2.9.9.tar.gz"
hash =
"sha256:d1454bde93fb1e224166811694d600e746430c006fbb031ea06ecc2ea41bf156"

The above example uses `psycopg2`, which has an sdist and wheels only on
Windows. Since I ran the above on Linux, an sdist was selected. But no
wheels appeared in the lock file.

With this PR, wheels are now correctly plumbed through:

$ cargo run -p uv -- pip compile -p3.10 <(echo psycopg2)
--unstable-uv-lock-file
    $ cat uv.lock
    version = 1

    [[distribution]]
    name = "psycopg2"
    version = "2.9.9"
    source = "registry+https://pypi.org/simple"

    [distribution.sdist]
url =
"dc6acaf46d76fce95daac5e0f0301b/psycopg2-2.9.9.tar.gz"
hash =
"sha256:d1454bde93fb1e224166811694d600e746430c006fbb031ea06ecc2ea41bf156"

    [[distribution.wheel]]
url =
"2767d96391f5cde90b82cb3e8c2a12/psycopg2-2.9.9-cp310-cp310-win32.whl"
hash =
"sha256:38a8dcc6856f569068b47de286b472b7c473ac7977243593a288ebce0dc89516"

    [[distribution.wheel]]
url =
"6572dec6831f85491a5e4dda606a98/psycopg2-2.9.9-cp310-cp310-win_amd64.whl"
hash =
"sha256:426f9f29bde126913a20a96ff8ce7d73fd8a216cfb323b1f04da402d452853c3"

    [[distribution.wheel]]
url =
"1fc5b9d33c858a602868a592cdc1b0/psycopg2-2.9.9-cp311-cp311-win32.whl"
hash =
"sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372"

    [[distribution.wheel]]
url =
"5133dd3183e671b278ce248810b7f7/psycopg2-2.9.9-cp311-cp311-win_amd64.whl"
hash =
"sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981"

    [[distribution.wheel]]
url =
"f74ffe6b6fe119ccb8a6546c3fb893/psycopg2-2.9.9-cp312-cp312-win32.whl"
hash =
"sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024"

    [[distribution.wheel]]
url =
"c4a26e1918ab7ee854fb5247f16c40/psycopg2-2.9.9-cp312-cp312-win_amd64.whl"
hash =
"sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693"

    [[distribution.wheel]]
url =
"ffeb9ac356ce0d6c4f2f34e396dbc0/psycopg2-2.9.9-cp37-cp37m-win32.whl"
hash =
"sha256:5e0d98cade4f0e0304d7d6f25bbfbc5bd186e07b38eac65379309c4ca3193efa"

    [[distribution.wheel]]
url =
"0a39176d36fd7105774e57996f63cd/psycopg2-2.9.9-cp37-cp37m-win_amd64.whl"
hash =
"sha256:7e2dacf8b009a1c1e843b5213a87f7c544b2b042476ed7755be813eaf4e8347a"

    [[distribution.wheel]]
url =
"86b90d30c4420cc3c0f6da2b8f3a9a/psycopg2-2.9.9-cp38-cp38-win32.whl"
hash =
"sha256:ff432630e510709564c01dafdbe996cb552e0b9f3f065eb89bdce5bd31fabf4c"

    [[distribution.wheel]]
url =
"c439b378ef79997a935f10374f3c0d/psycopg2-2.9.9-cp38-cp38-win_amd64.whl"
hash =
"sha256:bac58c024c9922c23550af2a581998624d6e02350f4ae9c5f0bc642c633a2d5e"

    [[distribution.wheel]]
url =
"5080c0e61ad5f08b9503e508aac116/psycopg2-2.9.9-cp39-cp39-win32.whl"
hash =
"sha256:c92811b2d4c9b6ea0285942b2e7cac98a59e166d59c588fe5cfe1eda58e72d59"

    [[distribution.wheel]]
url =
"ec73fe66d4d65f5bbe54efb191d9e6/psycopg2-2.9.9-cp39-cp39-win_amd64.whl"
hash =
"sha256:de80739447af31525feddeb8effd640782cf5998e1a4e9192ebdf829717e3913"

Ref #3351
2024-05-15 15:16:00 -04:00
Andrew Gallant 018a7150d6
uv-distribution: include all wheels in distribution types (#3595)
Our current flow of data from "simple registry package" to "final
resolved distribution" goes through a number of types:

* `SimpleMetadata` is the API response from a registry that includes all
published versions for a package. Each version has an assortment of
metadata
associated with it.
* `VersionFiles` is the aforementioned metadata. It is split in two: a
group of files for source distributions and a group of files for wheels.
* `PrioritizedDist` collects a subset of the files from `VersionFiles`
to form a selection of the "best" sdist and the "best" wheel for the
current environment.
* `CompatibleDist` is created from a borrowed `PrioritizedDist` that,
perhaps among other things, encapsulates the decision of whether to pick
an sdist or a wheel. (This decision depends both on compatibility and
the action being performed. e.g., When doing installation, a
`CompatibleDist` will sometimes select an sdist over a wheel.)
* `ResolvedDistRef` is like a `ResolvedDist`, but borrows a `Dist`.
* `ResolvedDist` is the almost-final-form of a distribution in a
resolution and is created from a `ResolvedDistRef`.
* `AnnotatedResolvedDist` is a new data type that is the actual final
form of a distribution that a universal lock file cares about. It
bundles a `ResolvedDist` with some metadata needed to generate a lock
file.

One of the requirements of a universal lock file is that we include all
wheels (and maybe all source distributions? but at least one if it's
present) associated with a distribution. But the above flow of data (in
the step from `VersionFiles` to `PrioritizedDist`) drops all wheels
except for the best one.

To remedy this, in this PR, we rejigger `PrioritizedDist`,
`CompatibleDist` and `ResolvedDistRef` so that all wheel data is
preserved. And when a `ResolvedDistRef` is finally turned into a
`ResolvedDist`, we copy all of the wheel data. And finally, we adjust
the `Lock` constructor to read this new data and include it in the lock
file. To make this work, we also modify `RegistryBuiltDist` so that it
can contain one or more wheels instead of just one.

One shortcoming here (called out in the code as a FIXME) is that if a
source distribution is selected as the "best" thing to use (perhaps
there are no compatible wheels), then the wheels won't end up in the
lock file. I plan to fix this in a follow-up PR.

We also aren't totally consistent on source distribution naming.
Sometimes we use `sdist`. Sometimes `source`. Sometimes `source_dist`.
I think it'd be nice to just use `sdist` everywhere, but I do prefer
the type names to be `SourceDist`. And sometimes you want function
names to match the type names (i.e., `from_source_dist`), which in turn
leads to an appearance of inconsistency. I'm open to ideas.

Closes #3351
2024-05-15 15:07:28 -04:00
Ibraheem Ahmed a962a65302
Add routine for verifying two marker trees are disjoint (#3583)
## Summary

Implements https://github.com/astral-sh/uv/issues/3355.
2024-05-15 13:01:05 -04:00
Charlie Marsh d0c6b0a93e
Add local path conversions from lockfile (#3609)
## Summary

Just does the most basic thing to convert from `Distribution` back to
the installable type.
2024-05-15 12:46:45 -04:00
Charlie Marsh 647f38be31
Add missing `"directory"` branch in source match (#3608)
## Summary

Just my oversight.
2024-05-15 16:25:55 +00:00
Charlie Marsh d76b023cd6
Remove `resolve_cli.rs` and `resolve_many.rs` (#3606)
## Summary

These depend on some APIs that I want to be internal-only for the
resolver crate, and we're no longer using them.
2024-05-15 16:04:47 +00:00
Charlie Marsh 85c71d6987
Rename `pinned_package` to `dist` (#3598) 2024-05-15 00:42:27 +00:00
Charlie Marsh 9a18d4ff46
Split `resolution.rs` into multiple files (#3597)
## Summary

No code changes.
2024-05-15 00:16:04 +00:00
Charlie Marsh 8971944a01
Move `extras` onto annotated distribution (#3592)
## Summary

Like hashes, we can now store these on `AnnotatedDist` rather than
creating a parallel hash map and performing lookups later on.
2024-05-14 23:25:06 +00:00
Charlie Marsh c7348589fa
Remove some editable branches in resolution (#3591)
## Summary

Editables should always go down the `Some(url)` branch.
2024-05-14 23:16:06 +00:00
Charlie Marsh 4a42730cae
Add hashes and versions to all distributions (#3589)
## Summary

In `ResolutionGraph::from_state`, we have mechanisms to grab the hashes
and metadata for all distributions -- but we then throw that information
away. This PR preserves it on a new `AnnotatedDist` (yikes, open to
suggestions) that wraps `ResolvedDist` and includes (1) the hashes
(computed or from the registry) and (2) the `Metadata23`, which lets us
extract the version.

Closes https://github.com/astral-sh/uv/issues/3356.

Closes https://github.com/astral-sh/uv/issues/3357.
2024-05-14 23:07:24 +00:00
Charlie Marsh 7363f31ceb
Rename `sourcedist` to `sdist` in lockfile (#3590)
## Summary

I think this is more consistent with Brett's proposal and looks more
natural to me as a user. What do you think, @BurntSushi?
2024-05-14 16:56:00 -04:00
Charlie Marsh f4cd7d627a
Split extra validation from graph construction (#3586)
## Summary

Splits this into two loops that each handle independent cases, to make
the code a little easier to reason about. No behavioral or logic changes
-- just splitting the `match` across two loops.
2024-05-14 16:41:16 -04:00
Charlie Marsh 27c8c5ad44
Explain hash tie-breaking in distribution comparisons (#3581) 2024-05-14 13:34:32 -04:00
Ibraheem Ahmed 8ce9051296
Parse marker tree before evaluation (#3520)
## Summary

Parse `MarkerTree` expressions upfront, instead of lazily during
evaluation.

This makes implementing https://github.com/astral-sh/uv/issues/3355 a
lot easier.
2024-05-14 11:02:30 -04:00
konsti 025368965e
Reduce `GitSourceDist` URL usage (#3458)
Refactor the easy-to-remove usage of the `VerbatimUrl` on
`GitSourceDist`
2024-05-14 02:03:55 +00:00