Commit Graph

66 Commits

Author SHA1 Message Date
John Mumm 2a66349e96
Check if relative URL is valid directory before treating as index (#13917)
As per #13874, passing a relative URL like `test` to `--index` for `uv
add` causes unexpected behavior if the directory does not exist. The
non-existent index is effectively ignored and uv falls back to PyPI. If
a package is found there, the spurious index is then written to
`pyproject.toml`. This doesn't happen for `--default-index` since
resolution will fail without fallback to PyPI.

This PR adds a validation step for indexes provided on the command line.
If a directory does not exist, uv will fail with an error.

Closes #13874
2025-06-09 19:28:39 +02:00
John Mumm 410dc33574
Make `DisplaySafeUrlRef` Copy and other minor PR follow-ups (#13683)
This PR implements a few review follow-ups from #13560. In particular,
it
* Makes `DisplaySafeUrlRef` implement `Copy` so that it can be passed by
value.
* Updates `to_string_with_credentials` methods with
`displayable_with_credentials`, returning an `impl Display` instead of
`String` for greater flexibility.
* Updates the `DisplaySafeUrl` and `DisplaySafeUrlRef` `Debug`
implementations to match the underlying `Url` `Debug` implementation
(with the exception that credentials are masked).
* Replaces an unnecessary `DisplaySafeUrl::from(Url::from_file_path`
with `DisplaySafeUrl::from_file_path`
2025-05-28 06:36:18 -04:00
John Mumm c19a294a48
Add `DisplaySafeUrl` newtype to prevent leaking of credentials by default (#13560)
Prior to this PR, there were numerous places where uv would leak
credentials in logs. We had a way to mask credentials by calling methods
or a recently-added `redact_url` function, but this was not secure by
default. There were a number of other types (like `GitUrl`) that would
leak credentials on display.

This PR adds a `DisplaySafeUrl` newtype to prevent leaking credentials
when logging by default. It takes a maximalist approach, replacing the
use of `Url` almost everywhere. This includes when first parsing config
files, when storing URLs in types like `GitUrl`, and also when storing
URLs in types that in practice will never contain credentials (like
`DirectorySourceUrl`). The idea is to make it easy for developers to do
the right thing and for the compiler to support this (and to minimize
ever having to manually convert back and forth). Displaying credentials
now requires an active step. Note that despite this maximalist approach,
the use of the newtype should be zero cost.

One conspicuous place this PR does not use `DisplaySafeUrl` is in the
`uv-auth` crate. That would require new clones since there are calls to
`request.url()` that return a `&Url`. One option would have been to make
`DisplaySafeUrl` wrap a `Cow`, but this would lead to lifetime
annotations all over the codebase. I've created a separate PR based on
this one (#13576) that updates `uv-auth` to use `DisplaySafeUrl` with
one new clone. We can discuss the tradeoffs there.

Most of this PR just replaces `Url` with `DisplaySafeUrl`. The core is
`uv_redacted/lib.rs`, where the newtype is implemented. To make it
easier to review the rest, here are some points of note:

* `DisplaySafeUrl` has a `Display` implementation that masks
credentials. Currently, it will still display the username when there is
both a username and password. If we think is the wrong choice, it can
now be changed in one place.
* `DisplaySafeUrl` has a `remove_credentials()` method and also a
`.to_string_with_credentials()` method. This allows us to use it in a
variety of scenarios.
* `IndexUrl::redacted()` was renamed to
`IndexUrl::removed_credentials()` to make it clearer that we are not
masking.
* We convert from a `DisplaySafeUrl` to a `Url` when calling `reqwest`
methods like `.get()` and `.head()`.
* We convert from a `DisplaySafeUrl` to a `Url` when creating a
`uv_auth::Index`. That is because, as mentioned above, I will be
updating the `uv_auth` crate to use this newtype in a separate PR.
* A number of tests (e.g., in `pip_install.rs`) that formerly used
filters to mask tokens in the test output no longer need those filters
since tokens in URLs are now masked automatically.
* The one place we are still knowingly writing credentials to
`pyproject.toml` is when a URL with credentials is passed to `uv add`
with `--raw`. Since displaying credentials is no longer automatic, I
have added a `to_string_with_credentials()` method to the `Pep508Url`
trait. This is used when `--raw` is passed. Adding it to that trait is a
bit weird, but it's the simplest way to achieve the goal. I'm open to
suggestions on how to improve this, but note that because of the way
we're using generic bounds, it's not as simple as just creating a
separate trait for that method.
2025-05-27 00:05:30 +02:00
Charlie Marsh c5032aee80
Bump MSRV to 1.85 and Edition 2024 (#13516)
## Summary

Builds on https://github.com/astral-sh/uv/pull/11724.

Closes https://github.com/astral-sh/uv/issues/13476.
2025-05-18 19:38:43 -04:00
konsti 5d37c7ecc5
Apply first set of Rustfmt edition 2024 changes (#13478)
Rustfmt introduces a lot of formatting changes in the 2024 edition. To
not break everything all at once, we split out the set of formatting
changes compatible with both the 2021 and 2024 edition by first
formatting with the 2024 style, and then again with the currently used
2021 style.

Notable changes are the formatting of derive macro attributes and lines
with overly long strings and adding trailing semicolons after statements
consistently.
2025-05-16 20:19:02 -04:00
konsti 09129031a4
Fix panic with invalid last char in PEP 508 name (#13105)
Fixes #13102
2025-04-25 14:56:46 +02:00
Charlie Marsh 42dcea0ee2
Bump MSRV to 1.84 (#12670)
## Summary

Closes https://github.com/astral-sh/uv/issues/12649.
2025-04-04 11:49:26 -04:00
Charlie Marsh 149102a4e7
Use a boxed slice for extras and groups (#12391)
## Summary

A very common struct, and these are immutable. Easy to optimize.
2025-03-22 11:53:36 -04:00
Aria Desires ba73231164
add pip-compatible `--group` flag to `uv pip install` and `uv pip compile` (#11686)
This is a minimal redux of #10861 to be compatible with `uv pip`.

This implements the interface described in:
https://github.com/pypa/pip/pull/13065#issuecomment-2544000876 for `uv
pip install` and `uv pip compile`. Namely `--group <[path:]name>`, where
`path` when not defined defaults to `pyproject.toml`.

In that interface they add `--group` to `pip install`, `pip download`,
and `pip wheel`. Notably we do not define `uv pip download` and `uv pip
wheel`, so for parity we only need to implement `uv pip install`.
However, we also support `uv pip compile` which is not part of pip
itself, and `--group` makes sense there too.

----

The behaviour of `--group` for `uv pip` commands makes sense for the
cases upstream pip supports, but has confusing meanings in cases that
only we support (because reading pyproject.tomls is New Tech to them but
heavily supported by us). **Specifically case (h) below is a concerning
footgun, and case (e) below may get complaints from people who aren't
well-versed in dependency-groups-as-they-pertain-to-wheels.**


## Only Group Flags

Group flags on their own work reasonably and uncontroversially, except
perhaps that they don't do very clever automatic project discovery.

a) `uv pip install --group path/to/pyproject.toml:mygroup` pulls up
`path/to/project.toml` and installs all the packages listed by its
`mygroup` dependency-group (essentially treating it like another kind of
requirements.txt). In this regard it functions similarly to
`--only-group` in the rest of uv's interface.

b) `uv pip install --group mygroup` is just sugar for `uv pip install
--group pyproject.toml:mygroup` (**note that no project discovery
occurs**, upstream pip simply hardcodes the path "pyproject.toml" here
and we reproduce that.)

c) `uv pip install --group a/pyproject.toml:groupx --group
b/pyproject.toml:groupy`, and any other instance of multiple `--group`
flags, can be understood as completely independent requests for the
given groups at the given files.


## Groups With Named Packages

Groups being mixed with named packages also work in a fairly
unsurprising way, especially if you understand that things like
dependency-groups are not really supposed to exist on pypi, they're just
for local development.

d) `uv pip install mypackage --group path/to/pyproject.toml:mygroup`
much like multiple instances of `--group` the two requests here are
essentially completely independent: pleases install `mypackage`, and
please also install `path/to/pyproject.toml:mygroup`.

e) `uv pip install mypackage --group mygroup` is exactly the same, but
this is where it becomes possible for someone to be a little confused,
as you might think `mygroup` is supposed to refer to `mypackage` in some
way (it can't). But no, it's sourcing `pyproject.toml:mygroup` from the
current working directory.


## Groups With Requirements/Sourcetrees/Editables

Requirements and sourcetrees are where I expect users to get confused.
It behaves *exactly* the same as it does in the previous sections but
you would absolutely be forgiven for expecting a different behaviour.
*Especially* because `--group` with the rest of uv *does* do something
different.

f) `uv pip install -r a/pyproject.toml --group b/pyproject.toml:mygroup`
is again just two independent requests (install `a/pyproject.toml`'s
dependencies, and `b/pyproject.toml`'s `mygroup`).

g) `uv pip install -r pyproject.toml --group mygroup` is exactly like
the previous case but *incidentally* the two requests refer to the same
file. What the user wanted to happen is almost certainly happening, but
they are likely getting "lucky" here that they're requesting something
simple.

h) `uv pip install -r a/pyproject.toml --group mygroup` is again exactly
the same but the user is likely to get surprised and upset as this
invocation actually sources two different files (install
`a/pyproject.toml`'s dependencies, and `pyproject.toml`'s `mygroup`)! I
would expect most people to assume the `--group` flag here is covering
all applicable requirements/sourcetrees/editables, but no, it continues
to be a totally independent reference to a file with a hardcoded
relative path.

------

Fixes https://github.com/astral-sh/uv/issues/8590
Fixes https://github.com/astral-sh/uv/issues/8969
2025-03-17 14:44:11 -04:00
Charlie Marsh 3188d99f39
Use consistent commas around i.e. and e.g. (#12157)
## Summary

Only in user-facing docs -- I didn't bother with the rustdoc. (This is
in the style guide already.)
2025-03-13 23:42:10 +00:00
konsti d712ff243e
Edition 2024 prep: Escape `r#gen` and remove redundant ref (#11922)
Three edition 2021 compatible sets of changes in preparation for the
edition 2025 split out from #11724.

In edition 2025, `gen` is a keyword, so we escape it as `r#gen`. `ref`
and `ref mut` are not allowed anymore for `&T` and `&mut T`, so we
remove them. `cargo fmt` now formats inside of macros, which the 2021
formatter doesn't undo.
2025-03-03 11:13:56 +00:00
Charlie Marsh 3017b82ecc
Fix typo in unsatisfiable (#11874) 2025-03-01 03:22:27 +00:00
Charlie Marsh c37af945b3
Avoid using owned `String` in deserializers (#11764)
## Summary

This is the pattern I see in a variety of crates, and I believe this is
preferred if you don't _need_ an owned `String`, since you can avoid the
allocation. This could be pretty impactful for us?
2025-02-25 14:28:16 +00:00
Charlie Marsh 76c3caf24f
Avoid using owned `String` for package name constructors (#11768)
## Summary

Since we use `SmallString` internally, there's no benefit to passing an
owned string to the `PackageName` constructor (same goes for
`ExtraName`, etc.). I've kept them for now (maybe that will change in
the future, so it's useful to have clients passed own values if they
_can_), but removed a bunch of usages where we were casting from `&str`
to `String` needlessly to use the constructor.
2025-02-24 23:06:15 -08:00
Charlie Marsh 6080dcbf7b
Remove stale comment on `MarkerEnvironment` (#11769) 2025-02-24 23:05:59 -08:00
Charlie Marsh 27de145610
Use matches over contains for extra value parsing (#11770) 2025-02-24 23:05:43 -08:00
samypr100 878497a014
Upgrade Rust toolchain to 1.85 (#11720)
## Summary

* Upgrade the rust toolchain to 1.85.0. This does not increase the MSRV.
* Update windows trampoline to 1.86 nightly beta (previously in 1.85
nightly beta).

## Test Plan

Existing tests
2025-02-23 16:52:34 +01:00
Andrew Gallant b48e6b9862 uv-pep508: add routine for visiting all extras in a marker tree
We'll need this to be able to decode conflict markers back into
a series of inclusion/exclusion rules used during resolution.
2025-02-18 07:44:12 -05:00
konsti 6e5479f5db
Optimize flattening in apache airflow workspace (#11313)
## Motivation

No-op `uv lock` in apache airflow
(891c67f210ab7c877d1f00ea6ea3d3cdbb0e96ef) is slow, which makes `uv run`
slow, too.

Reference project:

```
$ hyperfine "uv run python -c \"print('hi')\""
Benchmark 1: uv run python -c "print('hi')"
Time (mean ± σ):      16.3 ms ±   1.5 ms    [User: 9.8 ms, System: 6.4 ms]
Range (min … max):    13.0 ms …  20.0 ms    186 runs
```

Apache airflow before:

```
$ hyperfine "uv run python -c \"print('hi')\""
Benchmark 1: uv run python -c "print('hi')"
Time (mean ± σ):     161.0 ms ±   5.2 ms    [User: 135.3 ms, System: 24.1 ms]
Range (min … max):   155.0 ms … 176.3 ms    18 runs
```

## Optimization

`FlatRequiresDist::from_requirements` is taking 50% of main thread
runtime.

Before:


![image](https://github.com/user-attachments/assets/10ea76eb-d1e9-477c-b400-39e653eb8f3a)

After both commits:


![image](https://github.com/user-attachments/assets/5c578ff6-f80b-46bb-9b5f-8be8435c3d85)

Apache airflow after the first commit:

```
$ hyperfine "uv-profiling run python -c \"print('hi')\""
Benchmark 1: uv-profiling run python -c "print('hi')"
  Time (mean ± σ):     122.3 ms ±   5.4 ms    [User: 96.1 ms, System: 24.7 ms]
  Range (min … max):   114.0 ms … 133.2 ms    23 runs
```

Apache airflow after the second commit:

```
$ hyperfine "uv-profiling run python -c \"print('hi')\""
Benchmark 1: uv-profiling run python -c "print('hi')"
  Time (mean ± σ):     108.5 ms ±   3.4 ms    [User: 83.2 ms, System: 24.2 ms]
  Range (min … max):   103.6 ms … 119.9 ms    28 runs
```
2025-02-07 17:08:40 -05:00
Charlie Marsh cf366a557b
Correct environment variable expansion in rustdoc (#11327)
## Summary

These comments are incorrect.
2025-02-07 19:51:40 +00:00
Charlie Marsh cca1d34432
Optimize exclusion computation for markers (#11158)
## Summary

Oddly this showed up in a trace. I think the lack of memoization was
making it fairly expensive.
2025-02-02 08:21:31 -05:00
Charlie Marsh 07e1e85c5d
Avoid deserialization error for paths above the root (#10789)
## Summary

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

## Test Plan

Copied over this lockfile:

```toml
version = 1
requires-python = ">=3.12"
resolution-markers = [
    "sys_platform == 'win32'",
    "sys_platform != 'win32'",
]

[[package]]
name = "pyasn1"
version = "0.6.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "01f1a642459aae6ee7b159a6c4c018/pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034", size = 145322 }
wheels = [
    { url = "d6a797abb18c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", size = 83135 },
]

[[package]]
name = "pyasn1-modules"
version = "0.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "pyasn1" },
]
sdist = { url = "6afbf0d507a72057e9c23797a737c9/pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c", size = 310028 }
wheels = [
    { url = "bc88a6711982eaa35a0a47c8032bdc/pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", size = 181537 },
]

[[package]]
name = "python-ldap"
version = "3.4.4"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
    "sys_platform != 'win32'",
]
dependencies = [
    { name = "pyasn1", marker = "sys_platform != 'win32'" },
    { name = "pyasn1-modules", marker = "sys_platform != 'win32'" },
]
sdist = { url = "1eeb4025dc4955b72db5ce7a4dbfbd/python-ldap-3.4.4.tar.gz", hash = "sha256:7edb0accec4e037797705f3a05cbf36a9fde50d08c8f67f2aef99a2628fab828", size = 377889 }

[[package]]
name = "python-ldap"
version = "3.4.4"
source = { path = "../../../uti/Python/python_ldap-3.4.4-cp312-cp312-win_amd64.whl" }
resolution-markers = [
    "sys_platform == 'win32'",
]
dependencies = [
    { name = "pyasn1", marker = "sys_platform == 'win32'" },
    { name = "pyasn1-modules", marker = "sys_platform == 'win32'" },
]
wheels = [
    { filename = "python_ldap-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:94d2ca2b3ced81c9d89aa5c79d4965d03053e1ffdcfae73e9fac85d25b692e85" },
]

[package.metadata]
requires-dist = [
    { name = "pyasn1", specifier = ">=0.3.7" },
    { name = "pyasn1-modules", specifier = ">=0.1.5" },
]

[[package]]
name = "uv-test"
version = "1.0"
source = { virtual = "." }
dependencies = [
    { name = "python-ldap", version = "3.4.4", source = { registry = "https://pypi.org/simple" }, marker = "sys_platform != 'win32'" },
    { name = "python-ldap", version = "3.4.4", source = { path = "../../../uti/Python/python_ldap-3.4.4-cp312-cp312-win_amd64.whl" }, marker = "sys_platform == 'win32'" },
]

[package.metadata]
requires-dist = [
    { name = "python-ldap", marker = "sys_platform != 'win32'" },
    { name = "python-ldap", marker = "sys_platform == 'win32'", path = "../../../../../../../../../../../../uti/Python/python_ldap-3.4.4-cp312-cp312-win_amd64.whl" },
]
```

Verified that `cargo run sync --frozen` installs `python-ldap` from
PyPI, without erroring.
2025-01-20 16:36:18 -05:00
Charlie Marsh c306e46e1d
Remove trailing commas before brackets (#10740) 2025-01-18 19:56:46 +00:00
Mathieu Kniewallner b46c6db317
docs: fix a few typos (#10675)
## Summary

Fixing a few typos found in the documentation and in comments.
2025-01-16 09:53:59 -05:00
Charlie Marsh d8b5e7e7c0
Use `ArcStr` in verbatim URL (#10600)
## Summary

No need to use `String` here.
2025-01-14 16:12:14 +00:00
samypr100 4d3809cc6b
Upgrade Rust toolchain to 1.84.0 (#10533)
## Summary
Upgrade the rust toolchain to 1.84.0. This PR does not bump the MSRV.
2025-01-11 22:19:33 -05:00
Charlie Marsh 8420195aa7
Use `ArcStr` for marker values (#10453)
N.B. After fixing #10430, `ArcStr` became the fastest implementation
(and the gains were significantly reduced, down to 1-2%). See:
https://github.com/astral-sh/uv/pull/10453#issuecomment-2583344414.

## Summary

I tried out a variety of small string crates, but `Arc<str>`
outperformed them, giving a ~10% speed-up:

```console
❯ hyperfine "../arcstr lock" "../flexstr lock" "uv lock" "../arc lock" "../compact_str lock" --prepare "rm -f uv.lock" --min-runs 50 --warmup 20
Benchmark 1: ../arcstr lock
  Time (mean ± σ):     304.6 ms ±   2.3 ms    [User: 302.9 ms, System: 117.8 ms]
  Range (min … max):   299.0 ms … 311.3 ms    50 runs

Benchmark 2: ../flexstr lock
  Time (mean ± σ):     319.2 ms ±   1.7 ms    [User: 317.7 ms, System: 118.2 ms]
  Range (min … max):   316.8 ms … 323.3 ms    50 runs

Benchmark 3: uv lock
  Time (mean ± σ):     330.6 ms ±   1.5 ms    [User: 328.1 ms, System: 139.3 ms]
  Range (min … max):   326.6 ms … 334.2 ms    50 runs

Benchmark 4: ../arc lock
  Time (mean ± σ):     303.0 ms ±   1.2 ms    [User: 301.6 ms, System: 118.4 ms]
  Range (min … max):   300.3 ms … 305.3 ms    50 runs

Benchmark 5: ../compact_str lock
  Time (mean ± σ):     320.4 ms ±   2.0 ms    [User: 318.7 ms, System: 120.8 ms]
  Range (min … max):   317.3 ms … 326.7 ms    50 runs

Summary
  ../arc lock ran
    1.01 ± 0.01 times faster than ../arcstr lock
    1.05 ± 0.01 times faster than ../flexstr lock
    1.06 ± 0.01 times faster than ../compact_str lock
    1.09 ± 0.01 times faster than uv lock
```
2025-01-10 15:15:12 -05:00
Charlie Marsh 7a21b713b4
Avoid allocating for names in the PEP 508 parser (#10476)
## Summary

We can read from the slice directly. I don't think this will affect
performance today, because `from_str` will then allocate, but it
_should_ be a speedup once #10475 merges, since we can then avoid
allocating a `String` and go straight from `str` to `ArcStr`.
2025-01-10 20:12:23 +00:00
konsti 57367ed327
Use faster disjointness check for markers (#10439) 2025-01-09 18:31:51 +00:00
konsti 373e34f5dd
Remove `[u64; 4]` from small version to move `Arc` to full version (#10345) 2025-01-07 14:25:32 +00:00
konsti f5a60d1a18
Fix musl cdylib warning (#10268)
The `cdylib` was used for the pyo3 bindings to uv-pep508, which don't
exist anymore. It was now creating warnings on musl due to musl
(statically linked) no supporting shared libraries.
2025-01-02 09:40:38 -05:00
Charlie Marsh bec8468183
Remove unnecessary prefixes (#10158) 2024-12-25 14:18:01 -05:00
Charlie Marsh dd760ee507
Normalize `platform_system` to `sys_platform` (#9949)
## Summary

A revival of an old idea (#9344) that I have slightly more confidence in
now. I abandoned this idea because (1) it couldn't capture that, e.g.,
`platform_system == 'Windows' and sys_platform == 'foo'` (or some other
unknown value) are disjoint, and (2) I thought that Android returned
`"android"` for one of `sys_platform` or `platform_system`, which
would've made this logic incorrect.

However, it looks like Android... doesn't do that? And the values here
are almost always in a small, known set. So in the end, the tradeoffs
here actually seem pretty good.

Vis-a-vis our current solution, this can (e.g.) _simplify out_
expressions like `sys_platform == 'win32' or platform_system ==
'Windows'`.
2024-12-18 10:29:34 -05:00
Zanie Blue ae25c2f4db
Upgrade minimum Rust version to 1.83 (#9815)
This reverts commit 6cc7a560f7 to reapply
#9511 since we've disabled ppc64le-musl per #9793
2024-12-11 10:06:19 -06:00
Charlie Marsh 57a7f04f9a
Show a dedicated hint for missing `git+` prefixes (#9789)
## Summary

This has been bothering me a bit: `uv pip install "foo @
https://github.com/user/foo"` fails, telling you that it doesn't end in
a supported extension. But we should be able to tell you that it looks
like a Git repo.
2024-12-10 21:29:37 +00:00
Charlie Marsh 508a6bc953
Encode mutually-incompatible pairs of markers (#9444)
## Summary

This is an alternative to #9344. If accepted, I need to audit the
codebase and call sites to apply it everywhere, but the basic idea is:
rather than encoding mutually-incompatible pairs of markers in the
representation itself, we have an additional method on `MarkerTree` that
expands the false-y definition to take into account assumptions about
which markers can be true alongside others. We then check if the the
current marker implies that at least one of them is true.

So, for example, we know that `sys_platform == 'win32'` and
`platform_system == 'Darwin'` are mutually exclusive. When given a
marker expression like `python_version >= '3.7'`, we test if
`python_version >= '3.7'` and `sys_platform != 'win32' or
platform_system != 'Darwin'` are disjoint, i.e., if the following can't
be satisfied:

```
python_version >= '3.7' and (sys_platform != 'win32' or platform_system != 'Darwin')
```

Since, if this can't be satisfied, it implies that the left-hand
expression requires `sys_platform == 'win32'` and `platform_system ==
'Darwin'` to be true at the same time.

I think the main downsides here are:

1. We can't _simplify_ markers based on these implications. So we'd
still write markers like `sys_platform == 'win32' and platform_system !=
'Darwin'`, even though we know the latter expression is redundant.
2. It might be expensive? I'm not sure. I don't think we test for
falseness _that_ often though.

Closes #7760.
Closes #9275.
2024-12-07 01:51:44 +00:00
Zanie Blue 6cc7a560f7
Revert "Upgrade to Rust 1.83 (#9511)" (#9617)
This reverts commit cf20673197 (#9511) due
to failure on powerpc64le in https://github.com/astral-sh/uv/pull/9612
2024-12-03 19:21:05 +00:00
Charlie Marsh 75949f3fec
Avoid cloning `String` in marker evaluation (#9598)
## Summary

A small TODO that I found interesting. See:
https://github.com/astral-sh/pubgrub/pull/35.
2024-12-03 14:28:04 +00:00
Andrew Gallant 659e86efde uv-pep508: add more routines for manipulating extras
In the course of working on #9289, I've had to devise
some additions to our markers. While we are still staying
strictly compatible with the PEP 508 format, we will be
abusing the `extra` expression to carry a lot more
information.

Specifically, we want the following additional
operations:

* Simplify `extra != 'foo'`
* Remove all extra expressions
* Remove everything except extra expressions

My work on #9289 requires all of these (which will be
in a future in PR).
2024-12-02 09:09:35 -05:00
Charlie Marsh 8126a5ed32
Make `MarkerTree` `Copy` (#9542)
## Summary

It's just a `usize`. It seems simpler and perhaps even more performant
(?) to make it `Copy`.
2024-11-30 14:07:07 -05:00
Charlie Marsh cf20673197
Upgrade to Rust 1.83 (#9511)
## Summary

A lot of good new lints, and most importantly, error stabilizations. I
tried to find a few usages of the new stabilizations, but I'm sure there
are more.

IIUC, this _does_ require bumping our MSRV.
2024-11-29 12:04:22 -05:00
Charlie Marsh 8aeaf98f59
Rename from `Lowered` to `Canonical` (#9447)
By request:
https://github.com/astral-sh/uv/pull/9341#pullrequestreview-2460979421.
2024-11-26 13:34:43 -05:00
Charlie Marsh d18753527f
Remove `python_version` from lowered marker representation (#9343)
## Summary

We never construct these -- they should be impossible, since we always
translate to `python_full_version`. This PR encodes that impossibility
in the types.
2024-11-26 14:39:36 +00:00
Charlie Marsh df844e1ec9
Treat deprecated aliases as equivalent in marker algebra (#9342)
## Summary

This PR modifies our lowered representation such that any deprecated
aliases are treated as "the same" marker in the algebra.

So, for example, we now recognize that this is impossible, despite the
marker names being different:

```
typing-extensions ; platform.python_implementation == 'CPython' and python_implementation != 'CPython'
```

Similarly, we now recognize that this is just `sys_platform == 'win32'`,
despite the presence of both markers:

```
anyio ; sys_platform == 'win32' and sys.platform == 'win32'
```
2024-11-26 14:27:24 +00:00
Charlie Marsh 106863069d
Report marker diagnostics during parsing, rather than evaluation (#9338)
## Summary

I want to move towards a more normalized marker representation within
the marker tree, which means that the things we warn against will
disappear by the time we get to evaluation. I think it makes more sense
to show these warnings when we create the tree, rather than when we
evaluate it.
2024-11-26 14:15:33 +00:00
Andrew Gallant 38faae3d07 uv-pep508: add MarkerTree::implies
I think Ibraheem had this routine at some point in the past, but
we ended up dropping it because we didn't have a use for it. Well,
now we do!

It turns out that when we generate "conflict markers," they don't
actually take "world knowledge" into account. In particular, there
is "world knowledge" that a particular set of extras cannot be
enabled simultaneously. This in turn allows us to simplify most
conflict markers. If we didn't do this, it's likely that lock files
would become littered with conflict markers whenever any conflicts
are declared.

This is somewhat (although not completely) analogous to how we
"simplify" markers with respect to `requires-python`. That is,
`requires-python` reflects world knowledge that enables markers
to be written more simply than they otherwise would be without
world knowledge.
2024-11-23 13:14:27 -05:00
Andrew Gallant 42da99ff92 uv-pep508: add a clarifying test
This test demonstrates the difference between `extra != "foo"` and
`sys_platform != "foo"`.

I wrote this test down to test the extra simplification logic was
correct. And I also wanted to test whether we could somehow hackily
encode `group` (as opposed to just `extra`) logic into marker
expressions by reusing another field. But I don't think we can.
2024-11-22 08:21:23 -05:00
Charlie Marsh 3143494ddb
Make marker enums `Copy` (#9305) 2024-11-21 04:45:25 +00:00
Charlie Marsh e4fc875afa
Allow conflicting extras in explicit index assignments (#9160)
## Summary

This PR enables something like the "final boss" of PyTorch setups --
explicit support for CPU vs. GPU-enabled variants via extras:

```toml
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.13.0"
dependencies = []

[project.optional-dependencies]
cpu = [
    "torch==2.5.1+cpu",
]
gpu = [
    "torch==2.5.1",
]

[tool.uv.sources]
torch = [
    { index = "torch-cpu", extra = "cpu" },
    { index = "torch-gpu", extra = "gpu" },
]

[[tool.uv.index]]
name = "torch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

[[tool.uv.index]]
name = "torch-gpu"
url = "https://download.pytorch.org/whl/cu124"
explicit = true

[tool.uv]
conflicts = [
    [
        { extra = "cpu" },
        { extra = "gpu" },
    ],
]
```

It builds atop the conflicting extras work to allow sources to be marked
as specific to a dedicated extra being enabled or disabled.

As part of this work, sources now have an `extra` field. If a source has
an `extra`, it means that the source is only applied to the requirement
when defined within that optional group. For example, `{ index =
"torch-cpu", extra = "cpu" }` above only applies to
`"torch==2.5.1+cpu"`.

The `extra` field does _not_ mean that the source is "enabled" when the
extra is activated. For example, this wouldn't work:

```toml
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.13.0"
dependencies = ["torch"]

[tool.uv.sources]
torch = [
    { index = "torch-cpu", extra = "cpu" },
    { index = "torch-gpu", extra = "gpu" },
]

[[tool.uv.index]]
name = "torch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

[[tool.uv.index]]
name = "torch-gpu"
url = "https://download.pytorch.org/whl/cu124"
explicit = true
```

In this case, the sources would effectively be ignored. Extras are
really confusing... but I think this is correct? We don't want enabling
or disabling extras to affect resolution information that's _outside_ of
the relevant optional group.
2024-11-19 01:06:25 +00:00
Charlie Marsh d08bfee718
Remove separate test files in favor of same-file `mod tests` (#9199)
## Summary

These were moved as part of a broader refactor to create a single
integration test module. That "single integration test module" did
indeed have a big impact on compile times, which is great! But we aren't
seeing any benefit from moving these tests into their own files (despite
the claim in [this blog
post](https://matklad.github.io/2021/02/27/delete-cargo-integration-tests.html),
I see the same compilation pattern regardless of where the tests are
located). Plus, we don't have many of these, and same-file tests is such
a strong Rust convention.
2024-11-18 20:11:46 +00:00