This allows you to specify requires-python on individual dependency-groups,
with the intended usecase being "oh my dev-dependencies have a higher
requires-python than my actual project".
This includes a large driveby move of the RequiresPython type to
uv-distribution-types to allow us to generate the appropriate markers at
this point in the code. It also migrates RequiresPython from
pubgrub::Range to version_ranges::Ranges, and makes several pub(crate)
items pub, as it's no longer defined in uv_resolver.
Fixes#11606
By default, uv uses only a lower bound in `uv add`, which avoids
dependency conflicts due to upper bounds. With this PR, this cna be
changed by setting a different bound kind. The bound kind can be
configured in `uv.toml`, as a user preference, in `pyproject.toml`, as a
project preference, or on the CLI, when adding a specific project.
We add two options that add an upper bound on the constraint, one for
SemVer (`>=1.2.3,<2.0.0`, dubbed "major", modeled after the SemVer
caret) and another one for dependencies that make breaking changes in
minor version (`>=1.2.3,<1.3.0`, dubbed "minor", modeled after the
SemVer tilde). Intuitively, the major option bumps the most significant
version component, while the minor option bumps the second most
significant version component. There is also an exact bounds option
(`==1.2.3`), though generally we recommend setting a wider bound and
using the lockfile for pinning.
Versions can have leading zeroes, such as `0.1` or `0.0.1`. For a single
leading 0, we shift the the meaning of major and minor similar to cargo.
For two or more leading zeroes, the difference between major and minor
becomes inapplicable, instead both bump the most significant component:
- major: `0.1` -> `>=0.1,<0.2`
- major: `0.0.1` -> `>=0.0.1,<0.0.2`
- major: `0.0.1.1` -> `>=0.0.1.1,<0.0.2.0`
- major: `0.0.0.1` -> `>=0.0.0.1,<0.0.0.2`
- minor: `0.1` -> `>=0.1,<0.1.1`
- minor: `0.0.1` -> `>=0.0.1,<0.0.2`
- minor: `0.0.1.1` -> `>=0.0.1.1,<0.0.2.0`
- minor: `0.0.0.1` -> `>=0.0.0.1,<0.0.0.2`
For a consistent appearance, we try to preserve the number of components
in the upper bound. For example, adding a version `2.17` with the major
option is stored as `>=2.17,<3.0`. If a version uses three components
and is greater than 0, both bounds will also use three components
(SemVer versions always have three components). Of the top 100 PyPI
packages, 8 use a non-three-component version (docutils, idna, pycparser
and soupsieve with two components, packaging, pytz and tzdata with two
component, CalVer and trove-classifiers with four component CalVer).
Example `pyproject.toml` files with the top 100 packages: [`--bounds
major`](https://gist.github.com/konstin/0aaffa9ea53c4834c22759e8865409f4)
and [`--bounds
minor`](https://gist.github.com/konstin/e77f5e990a7efe8a3c8a97c5c5b76964).
While many projects follow version scheme that roughly or directly
matches the major or minor options, these compatibility ranges are
usually not applicable for the also popular CalVer versioning.
For pre-release versions, there are two framings we could take: One is
that pre-releases generally make no guarantees about compatibility
between them and are used to introduce breaking changes, so we should
pin them exactly. In many cases however, pre-release specifiers are used
because a project needs a bugfix or a feature that hasn't made it into a
stable release, or because a project is compatible with the next version
before a final version for that release is published. In those cases,
compatibility with other packages that depend on the same library is
more important, so the desired bound is the same as it would be for the
stable release, except with the lower bound lowered to include
pre-release.
The names of the bounds and the name of the flag is up for bikeshedding.
Currently, the option is call `tool.uv.bounds`, but we could also move
it under `tool.uv.edit.bounds`, where it would be the first/only entry.
Fixes#6783
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
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.
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.
This follows on from #13334 to fix another case.
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:
- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->
## Summary
If a dependency group contained any `{ include-group = "..." }` entries,
the sort detection would bail out. The root cause of the problem was
gating the sort detection behind `deps.iter().all(Value::is_str)`.
A public code search reveals that keeping include-groups at the top is
by far the most common, but keeping them at the bottom isn't uncommon.
In both of these cases, uv will now preserve the convention that is in
use.
Unless I've missed it, I don't think uv supports `uv add`ing an
include-group, and so that wasn't tested here.
## Test Plan
cargo test
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
The prior implementation only looks for dependencies which are sorted by
name then specifier.
I knew uv was meant to preserve sorted dependencies, but it never seemed
to work for me.
I've always used the "sort lines" feature of PyCharm/Sublime to sort
these lists, and I guess I'm not the only one. In such a case,
`flask-wtf>=1.2.1` is sorted before `flask>=3.0.2`.
After digging into the code I realised what was happening, hence this
merge request.
Maybe there's a tool I'm not aware of that people are using to sort
dependencies "properly", or are doing it by hand, but I think this is
worth supporting.
Relevant issues: https://github.com/astral-sh/uv/issues/9076,
https://github.com/astral-sh/uv/issues/10738
## Test Plan
`cargo test`
This is a reimplementation of #7248 with a new CLI interface.
The old `uv version` is now `uv self version` (also it has gained a
`--short` flag for parity).
The new `uv version` is now an interface for getting/setting the project
version.
To give a modicum of support for migration, if `uv version` is run and
we fail to find/read a `pyproject.toml` we will fallback to `uv self
version`. `uv version --project .` prevents this fallback from being
allowed.
The new API of `uv version` is as follows:
* pass nothing to read the project version
* pass a version to set the project version
* `--bump major|minor|patch` to semver-bump the project version
* `--dry-run` to show the result but not apply it
* `--short` to have the final printout contain only the final version
* `--output-format json` to get the final printout as json
```
$ uv version
myfast 0.1.0
$ uv version --bump major --dry-run
myfast 0.1.0 => 1.0.0
$ uv version 1.2.3 --dry-run
myfast 0.1.0 => 1.2.3
$ uv version 1.2.3
myfast 0.1.0 => 1.2.3
$ uv version --short
1.2.3
$ uv version --output-format json
{
"package_name": "myfast",
"version": "1.2.3",
"commit_info": null
}
```
Fixes#6298
Closes: #4567
## Summary
When adding a package with Git reference options (`--rev`, `--tag`,
`--branch`) that already has a Git source defined, use the existing Git
URL with the new reference instead of reporting an error.
This allows commands like `uv add requests --branch main` to work when
requests is already defined with a Git source in the project
configuration.
Previously, you would need to provide the whole Git url again for this
to work:
```bash
uv add git+https://github.com/psf/requests --branch main
```
<!-- What's the purpose of the change? What does it do, and why? -->
## Test Plan
- [x] Add unit tests for project
- [x] Add unit tests for script
- [x] Tested locally for project and script environments like below
### Testing Project
In a directory using the `uv` executable from this PR (via replacing
every `uv` with `cargo run --`) initialize a project and virtual
environment
```bash
uv init
uv venv
```
move into the environment
```bash
# on mac
source .venv/bin/activate
```
and add a dependency with a git url
```bash
uv add git+https://github.com/Textualize/rich --branch master
```
Then change the branch of the project to see that the branch can be
changed without need of the whole git url:
```bash
uv add rich --branch py310
```
### Testing Script
Create the following file, e.g. `script.py`:
```python
import time
from rich.progress import track
print("Starting")
for i in track(range(20), description="For example:"):
time.sleep(0.05)
print("Done")
```
Now using `uv` (referencing the executable of this PR) add the
dependency
```bash
uv add --script script.py 'git+https://github.com/Textualize/rich' --branch master
```
and check we can execute the script:
```bash
uv run script.py
```
To test the change update the branch
```bash
uv add --script script.py rich --branch py310
```
and check that the dependency is updated and the script is executed:
```bash
uv run script.py
```
<!-- How was it tested? -->
----
This is my first time contributing to `uv` (yay, 🤗) so let me know if
there is something obvious i am missing.
Unit tests will follow soon.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
This PR errors out when an Unknown Dependency Object Specifier is used
in dependency groups.
Fixes#12638
## Test Plan
The current behaviour is as follows:
```bash
➜ example git:(12638/dependency-object-specifier) ✗ cargo run -- sync
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
Running `/home/luna/Documents/uv/target/debug/uv sync`
error: Failed to generate package metadata for `example==0.1.0 @ virtual+.`
Caused by: Group `bar` contains a Dependency Object Specifier, which is not supported by uv
```
And the pyproject.toml to produce this is:
```toml
[project]
name = "example"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13.2"
dependencies = []
[dependency-groups]
foo = ["pyparsing"]
bar = [{set-phasers-to = "stun"}]
```
## Summary
See the test cases. Previously, you could end up with something like:
```toml
[tool.uv.workspace]
members = [
"foo",
"bar",
"baz", "bop",
]
```
## Summary
I don't know if I actually want to commit this, but I did it on the
plane last time and just polished it off (got it to compile) while
waiting to board.
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:
- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->
## Summary
<!-- What's the purpose of the change? What does it do, and why? -->
This fixes a case described in #12333, where trailing comments in
dependencies can be unexpectedly shifted when a new dependency is added.
Fixes#12333.
## Test Plan
<!-- How was it tested? -->
`cargo test` (Added a snapshot test)
## Summary
This crate is for standards-compliant types, but this is explicitly a
type that's custom to uv. It's also strange because we kind of want to
reference `IndexUrl` on the registry type, but that's in a crate that
_depends_ on `uv-pypi-types`, which to me is a sign that this is off.
Reduce the overhead of `uv run` in large workspaces. Instead of
re-discovering the entire workspace each time we resolve the metadata of
a member, we can the discovered set of workspace members. Care needs to
be taken to not cache the discovery for `uv init`, `uv add` and `uv
remove`, which change the definitions of workspace members.
Below is apache airflow e3fe06382df4b19f2c0de40ce7c0bdc726754c74 `uv run
python` with a minimal payload. With this change, we avoid a ~350ms
overhead of each `uv run` invocation.
```
$ hyperfine --warmup 2 \
"uv run --no-dev python -c \"print('hi')\"" \
"uv-profiling run --no-dev python -c \"print('hi')\""
Benchmark 1: uv run --no-dev python -c "print('hi')"
Time (mean ± σ): 492.6 ms ± 7.0 ms [User: 393.2 ms, System: 97.1 ms]
Range (min … max): 482.3 ms … 501.5 ms 10 runs
Benchmark 2: uv-profiling run --no-dev python -c "print('hi')"
Time (mean ± σ): 129.7 ms ± 2.5 ms [User: 105.4 ms, System: 23.2 ms]
Range (min … max): 126.0 ms … 136.1 ms 22 runs
Summary
uv-profiling run --no-dev python -c "print('hi')" ran
3.80 ± 0.09 times faster than uv run --no-dev python -c "print('hi')"
```
The profile after those change below. We still spend a large chunk in
toml parsing (both `uv.lock` and `pyproject.toml`), but it's not
excessive anymore.

Thank you for uv, it has game-changer capabilities in the field of
Python package and environment maangement!
## Summary
This is a small PR adding the option `module-name`
(`tool.uv.build-backend.module-name`) to the uv build backend (
https://github.com/astral-sh/uv/issues/8779 ).
Currently, the uv build backend will assume that the module name matches
the (dash to underdash-transformed) package name. In some packaging
scenarios this is not the case, and currently there exists no
possibility to override it, which this PR addresses.
From the main issue ( https://github.com/astral-sh/uv/issues/8779 ) I
could not tell if there is any extensive roadmap or plans how to
implement more complex scenarios, hence this PR as a suggestion for a
small feature with a big impact for certain scenarios.
I am new to Rust, I hope the borrow/reference usage is correct.
## Test Plan
So far I tested this at an example, if desired I can look into extending
the tests.
Fixes#11428
---------
Co-authored-by: konstin <konstin@mailbox.org>
This PR is in support of #12005, where we need to import
`DependencyGroups` in the `uv-pypi-types` crate without a circular
dependency on `uv-workspace`.
## Summary
In https://github.com/astral-sh/uv/issues/11998, a user is attempting to
vendor `pydantic-core`. But when they add `pydantic-core = { path =
"src/foo/vendor/pydantic-core" } `, we're installing it as a virtual
package, since `pydantic-core/pyproject.toml` contains `package =
false`.
This PR allows users to mark dependencies as "explicitly a package" or
"explicitly not a package" (i.e., virtual), as a workaround.
Closes https://github.com/astral-sh/uv/issues/11998.
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.
Fixes#11793
On Windows, trying to read a file inside what is not a directory but
another file results in a not found error, while on Unix we get a not a
directory error. We check explicitly if something included in a
workspace glob is a non-directory to fix the behavior on Windows.
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:
- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->
## Summary
This pull request introduces validation for unique index names in the
`tool.uv.index` field and adds corresponding tests to ensure the
functionality. The most important changes include adding a custom
deserializer function, updating the `ToolUv` struct to use the new
deserializer, and adding tests to verify the behavior.
Validation and deserialization:
*
[`crates/uv-workspace/src/pyproject.rs`](diffhunk://#diff-e12cd255985adfd45ab06f398cb420d2f543841ccbeea4175ccf827aa9215b9dR283-R311):
Added a custom deserializer function `deserialize_index_vec` to validate
that index names in the `tool.uv.index` field are unique.
*
[`crates/uv-workspace/src/pyproject.rs`](diffhunk://#diff-e12cd255985adfd45ab06f398cb420d2f543841ccbeea4175ccf827aa9215b9dR374):
Updated the `ToolUv` struct to use the `deserialize_index_vec` function
for the `index` field.
Testing:
*
[`crates/uv/tests/it/lock.rs`](diffhunk://#diff-82edd36151736f44055f699a34c8b19a63ffc4cf3c86bf5fb34d69f8ac88a957R15336):
Added a test `lock_repeat_named_index` to verify that duplicate index
names result in an error.
[[1]](diffhunk://#diff-82edd36151736f44055f699a34c8b19a63ffc4cf3c86bf5fb34d69f8ac88a957R15336)
[[2]](diffhunk://#diff-82edd36151736f44055f699a34c8b19a63ffc4cf3c86bf5fb34d69f8ac88a957R15360-R15402)
*
[`crates/uv/tests/it/lock.rs`](diffhunk://#diff-82edd36151736f44055f699a34c8b19a63ffc4cf3c86bf5fb34d69f8ac88a957R15360-R15402):
Added a test `lock_unique_named_index` to verify that unique index names
result in successful lock file generation.
Schema update:
*
[`uv.schema.json`](diffhunk://#diff-c669473b258a19ba6d3557d0369126773b68b27171989f265333a77bc5cb935bR205):
Updated the schema to set the default value of the `index` field to
`null`.
Fixes#11804
## Test Plan
### Steps to reproduce and verify the fix:
1. Clone the repository and checkout the feature branch
```bash
git clone https://github.com/astral-sh/uv.git
cd uv
git checkout feature/warn-duplicate-index-names
```
2. Build the modified binary
```bash
cargo build
```
3. Create a test project using the system installed uv
```bash
uv init uv-test
cd uv-test
```
4. Manually edit pyproject.toml to add duplicate index names
```toml
[[tool.uv.index]]
name = "alpha_b"
url = "<omitted>"
[[tool.uv.index]]
name = "alpha_b"
url = "<omitted>"
```
5. Try to add a package using the modified binary
```bash
../target/debug/uv add numpy
```
### Results
Before: use release binary

After: use self build binary

Now when attempting to use a pyproject.toml with duplicate index names,
the modified binary correctly detects the issue and produces an error
message:
```
error: Failed to parse: `pyproject.toml`
Caused by: TOML parse error at line 9, column 1
|
9 | [[tool.uv.index]]
| ^^^^^^^^^^^^^^^^^
duplicate index name `alpha_b`
```
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
This is the pattern I see in a variety of crates, and I believe this is
preferred if you don't _need_ an owned `String`, since you can avoid the
allocation. This could be pretty impactful for us?
## Summary
Since we use `SmallString` internally, there's no benefit to passing an
owned string to the `PackageName` constructor (same goes for
`ExtraName`, etc.). I've kept them for now (maybe that will change in
the future, so it's useful to have clients passed own values if they
_can_), but removed a bunch of usages where we were casting from `&str`
to `String` needlessly to use the constructor.
## 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
This change keeps dependency group keys sorted when adding new ones.
If earlier dependency group keys were not sorted, we just append the new
group key to avoid churn in `pyproject.toml`. See discussion on #11447.
I've added a new snapshot test to capture this case.
Closes#11447.
Initially, we were limiting Git schemes to HTTPS and SSH as only
supported schemes. We lost this validation in #3429. This incidentally
allowed file schemes, which apparently work with Git out of the box.
A caveat for this is that in tool.uv.sources, we parse the git field
always as URL. This caused a problem with #11425: repo = { git =
'c:\path\to\repo', rev = "xxxxx" } was parsed as a URL where c: is the
scheme, causing a bad error message down the line.
This PR:
* Puts Git URL validation back in place. It bans everything but HTTPS,
SSH, and file URLs. This could be a breaking change, if users were using
a git transport protocol were not aware of, even though never
intentionally supported.
* Allows file: URL in Git: This seems to be supported by Git and we were
supporting it albeit unintentionally, so it's reasonable to continue to
support it.
* It does not allow relative paths in the git field in tool.uv.sources.
Absolute file URLs are supported, whether we want relative file URLs for
Git too should be discussed separately.
Closes#3429: We reject the input with a proper error message, while
hinting the user towards file:. If there's still desire for relative
path support, we can keep it open.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
## Summary
Resolves#6913.
Add `tool.uv.build-constraint-dependencies` to pyproject.toml.
The changes are analogous to the constraint-dependencies feature
implemented in #5248.
Add documentation for `build-constraint-dependencies`
## Test Plan
Add tests for `uv lock`, `uv add`, `uv pip install` and `uv pip
compile`.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
We want to build `uv-build` without depending on the network crates. In
preparation for that, we split uv-git into uv-git and uv-git-types,
where only uv-git depends on reqwest, so that uv-build can use
uv-git-types.
## Summary
This PR revives https://github.com/astral-sh/uv/pull/10017, which might
be viable now that we _don't_ enforce any platforms by default.
The basic idea here is that users can mark certain platforms as required
(empty, by default). When resolving, we ensure that the specified
platforms have wheel coverage, backtracking if not.
For example, to require that we include a version of PyTorch that
supports Intel macOS:
```toml
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["torch>1.13"]
[tool.uv]
required-platforms = [
"sys_platform == 'darwin' and platform_machine == 'x86_64'"
]
```
Other than that, the forking is identical to past iterations of this PR.
This would give users a way to resolve the tail of issues in #9711, but
with manual opt-in to supporting specific platforms.
I think `UV_PROJECT_ENVIRONMENT` is too complicated for use-cases where
the user wants to sync to the active environment. I don't see a
compelling reason not to make opt-in easier. I see a lot of questions
about how to deal with this warning in the issue tracker, but it seems
painful to collect them here for posterity.
A notable behavior here — we'll treat this as equivalent to
`UV_PROJECT_ENVIRONMENT` so... if you point us to a valid virtual
environment that needs to be recreated for some reason (e.g., new Python
version request), we'll happily delete it and start over.
## Summary
When a `pyproject.toml` `[tool.uv.sources.(package)]` section specifies
`workspace` and one or more of (`index`, `git`, `url`, `path`, `rev`,
`tag`, `branch`, `editable`), running `uv` to build or sync the package
gives the error:
```
cannot specify both `index` and `(parameter name)`
```
The error should actually say:
```
cannot specify both `workspace` and `(parameter name)`
```
## Test Plan
I ran `cargo test`, and all tests still passed.
## Summary
If members define disjoint Python requirements, we should error. Right
now, it seems that it maps to unbounded and leads to weird behavior.
Closes https://github.com/astral-sh/uv/issues/10835.
## Summary
This PR modifies the lockfile to omit versions for source trees that use
`dynamic` versioning, thereby enabling projects to use dynamic
versioning with `uv.lock`.
Prior to this change, dynamic versioning was largely incompatible with
locking, especially for popular tools like `setuptools_scm` -- in that
case, every commit bumps the version, so every commit invalidates the
committed lockfile.
Closes https://github.com/astral-sh/uv/issues/7533.
## Summary
Fixes a bug when there are only comments in the dependencies section.
Basically, after one removes all dependencies, if there are remaining
comments then the value unwrapped here
c198e2233e/crates/uv-workspace/src/pyproject_mut.rs (L1309)
is never properly initialized.
It's initialized to `None`, here
c198e2233e/crates/uv-workspace/src/pyproject_mut.rs (L1256),
but doesn't get set to `Some(...)` until the first dependency here
c198e2233e/crates/uv-workspace/src/pyproject_mut.rs (L1276)
and since we remove them all... there are none.
## Test Plan
Manually induced bug with
```
[project]
name = "t1"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"duct>=0.6.4",
"minilog>=2.3.1",
# comment
]
```
Then running
```
$ RUST_LOG=trace RUST_BACKTRACE=full uv remove duct minilog
DEBUG uv 0.5.8
DEBUG Found project root: `/home/bnorick/dev/workspace/t1`
DEBUG No workspace root found, using project root
thread 'main' panicked at crates/uv-workspace/src/pyproject_mut.rs:1294:73:
called `Option::unwrap()` on a `None` value
stack backtrace:
0: 0x5638d7bed6ba - <unknown>
1: 0x5638d783760b - <unknown>
2: 0x5638d7bae232 - <unknown>
3: 0x5638d7bf0f07 - <unknown>
4: 0x5638d7bf215c - <unknown>
5: 0x5638d7bf1972 - <unknown>
6: 0x5638d7bf1909 - <unknown>
7: 0x5638d7bf18f4 - <unknown>
8: 0x5638d75087d2 - <unknown>
9: 0x5638d750896b - <unknown>
10: 0x5638d7508d68 - <unknown>
11: 0x5638d8dcf1bb - <unknown>
12: 0x5638d76be271 - <unknown>
13: 0x5638d75ef1f9 - <unknown>
14: 0x5638d75fc3cd - <unknown>
15: 0x5638d772d9de - <unknown>
16: 0x5638d8476812 - <unknown>
17: 0x5638d83e1894 - <unknown>
18: 0x5638d84722d3 - <unknown>
19: 0x5638d83e1372 - <unknown>
20: 0x7f851cfc7d90 - <unknown>
21: 0x7f851cfc7e40 - __libc_start_main
22: 0x5638d758e992 - <unknown>
23: 0x0 - <unknown>
```
## Summary
This PR introduces a `LockTarget`, which is peer to `InstallTarget` and
enables us to capture the common functionality necessary to support
locking.
For now, to minimize changes, only the `Workspace` target is
implemented. In a future PR, I'll add a `Script` target for both locking
and installing.
## Summary
This is necessary for some future improvements to non-`[project]`
workspaces and PEP 723 scripts. It's not "breaking", but it will
invalidate lockfiles for non-`[project]` workspaces. I think that's
okay, since we consider those legacy right now, and they're really rare.
## Summary
We had the right logic for determining whether the list is already
sorted, but we forgot to apply the same logic when deciding where to
insert the requirement, which made the list _unsorted_ for future
operations.
Closes https://github.com/astral-sh/uv/issues/10076.
## Summary
This now looks like:
```
error: Failed to parse: `pyproject.toml`
Caused by: TOML parse error at line 1, column 1
|
1 | [project]
| ^^^^^^^^^
`pyproject.toml` is using the `[project]` table, but the required `project.version` field is neither set nor present in the `project.dynamic` list
```
Closes https://github.com/astral-sh/uv/issues/9910.
## Summary
If you look at Ed's reply
[here](https://github.com/toml-rs/toml/issues/818#issuecomment-2532626305),
it sounds like we're being too heavy-handed in applying `.fmt()`. I
think I added this to handle an issue with inline tables whereby we were
inserting a space after a trailing comma? So now I'm just applying
`.fmt()` to inline tables, which don't allow comments between elements
anyway.
Closes https://github.com/astral-sh/uv/issues/9758.
## Summary
Today, our dependency group implementation is a little awkward... For
each package `P`, we check if `P` contains dependencies for each enabled
group, then add a dependency on `P` with the group enabled. There are a
few issues here:
1. It's sort of backwards... We add a dependency from the base package
`P` to `P` with the group enabled. Then `P` with the group enabled adds
a dependency on the base package.
2. We can't, e.g., enable different groups for different packages. (We
don't have a way for users to specify this on the CLI, but there's no
reason that it should be _impossible_ in the resolver.)
3. It's inconsistent with how extras work, which leads to confusing
differences in the resolver.
Instead, our internal requirement type can now include dependency
groups, which makes dependency groups look much, much more like extras
in the resolver.
## 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.
## Summary
We still only respect overrides and constraints in the workspace root --
which we may want to change -- but overrides and constraints are now
correctly lowered.
Closes https://github.com/astral-sh/uv/issues/8148.
- Adds a collapsible section for the project concept
- Splits the project concept document into several child documents.
- Moves the workspace and dependencies documents to under the project
section
- Adds a mkdocs plugin for redirects, so links to the moved documents
still work
I attempted to make the minimum required changes to the contents of the
documents here. There is a lot of room for improvement on the content of
each new child document. For review purposes, I want to do that work
separately. I'd prefer if the review focused on this structure and idea
rather than the content of the files.
I expect to do this to other documentation pages that would otherwise be
very nested.
The project concept landing page and nav (collapsed by default) looks
like this now:
<img width="1507" alt="Screenshot 2024-11-14 at 11 28 45 AM"
src="https://github.com/user-attachments/assets/88288b09-8463-49d4-84ba-ee27144b62a5">
## 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.
## 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.
## Summary
Align uv's workspace discovery with red knots (see
https://github.com/astral-sh/ruff/pull/14308#issuecomment-2474296308)
* Detect nested workspace inside the current workspace rather than
testing if the current workspace is a member of any outer workspace.
* Detect packages with identical names.
## Test Plan
I added two integration tests. I also back ported the tests to main to
verify that both these invalid workspaces aren't catched by uv today.
That makes this, technically, a breaking change but I would consider the
change a bug fix.
---------
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Since this is intended to support _both_ groups and extras, it doesn't
make sense to just name it for groups. And since there isn't really a
word that encapsulates both "extra" and "group," we just fall back to
the super general "conflicts."
We'll rename the variables and other things in the next commit.
## Summary
This PR ensures that `pylint>=3.2.6` followed by
`pylint-module-boundaries>=1.3.1` is considered sorted, despite the fact
that `>` is later in the alphabetic than `-`. By purely comparing
strings, they would _not_ be sorted; but by considering the name, then
the specifiers, they are.
Closes https://github.com/astral-sh/uv/issues/9076.
This PR adds support for conflicting extras. For example, consider
some optional dependencies like this:
```toml
[project.optional-dependencies]
project1 = ["numpy==1.26.3"]
project2 = ["numpy==1.26.4"]
```
These dependency specifications are not compatible with one another.
And if you ask uv to lock these, you'll get an unresolvable error.
With this PR, you can now add this to your `pyproject.toml` to get
around this:
```toml
[tool.uv]
conflicting-groups = [
[
{ package = "project", extra = "project1" },
{ package = "project", extra = "project2" },
],
]
```
This will make the universal resolver create additional forks
internally that keep the dependencies from the `project1` and
`project2` extras separate. And we make all of this work by reporting
an error at **install** time if one tries to install with two or more
extras that have been declared as conflicting. (If we didn't do this,
it would be possible to try and install two different versions of the
same package into the same environment.)
This PR does *not* add support for conflicting **groups**, but it is
intended to add support in a follow-up PR.
Closes#6981Fixes#8024
Ref #6729, Ref #6830
This should also hopefully unblock
https://github.com/dagster-io/dagster/pull/23814, but in my testing, I
did run into other problems (specifically, with `pywin`). But it does
resolve the problem with incompatible dependencies in two different
extras once you declare `test-airflow-1` and `test-airflow-2` as
conflicting for `dagster-airflow`.
NOTE: This PR doesn't make `conflicting-groups` public yet. And in a
follow-up PR, I plan to switch the name to `conflicts` instead of
`conflicting-groups`, since it will be able to accept conflicting extras
_and_ conflicting groups.
Uses #6369 for test coverage.
Updates version file discovery to search up into parent directories.
Also refactors Python request determination to avoid duplicating the
user request / version file / workspace lookup logic in every command
(this supersedes the work started in
https://github.com/astral-sh/uv/pull/6372).
There is a bit of remaining work here, mostly around documentation.
There are some edge-cases where we don't use the refactored request
utility, like `uv build` — I'm not sure how I'm going to handle that yet
as it needs a separate root directory.
These settings can only be defined in `pyproject.toml`, since they're
project-centric, and not _configuration_.
Closes https://github.com/astral-sh/uv/issues/8539.
---------
Co-authored-by: Zanie Blue <contact@zanie.dev>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
Co-authored-by: konsti <konstin@mailbox.org>
## Summary
This PR improves the interaction of `--frozen` such that we reduce the
dependency on the `pyproject.toml` and increase the dependency on the
`uv.lock`. Specifically, we now read the list of workspace members from
the `uv.lock` rather than the `pyproject.toml`, which means we don't
need to discover the member `pyproject.toml` files in order to perform a
`uv sync --frozen --all-packages`.
## Summary
This PR enables `uv sync --all-packages` to sync all packages in a
workspace. It removes a common use-case for the legacy non-`[project]`
packages that we're trying to move away from.
Closes https://github.com/astral-sh/uv/issues/8724.
## Summary
It turns out that when locking, we were only taking the groups from the
root `pyproject.toml` into account, and ignoring groups that were only
defined in a workspace member.
## Summary
We already support `tool.uv.dev-dependencies` in the legacy
non-`[project]` projects. This adds equivalent support for
`[dependency-groups]`, e.g.:
```toml
[tool.uv.workspace]
[dependency-groups]
lint = ["ruff"]
```
## Summary
`uv add --dev` now updates the `dependency-groups.dev` section, rather
than `tool.uv.dev-dependencies` -- unless the dependency is already
present in `tool.uv.dev-dependencies`.
`uv remove --dev` now removes from both `dependency-groups.dev` and
`tool.uv.dev-dependencies`.
`--dev` and `--group dev` are now treated equivalently in `uv add` and
`uv remove`.
This PR adds support for `tool.uv.default-groups`, which defaults to
`["dev"]` for backwards-compatibility. These represent the groups we
sync by default.