add support for specifying conflicting extras (#8976)

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 #6981

Fixes #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.
This commit is contained in:
Andrew Gallant 2024-11-13 09:52:28 -05:00 committed by GitHub
parent 926660aea0
commit 15ef807c80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 2393 additions and 171 deletions

View file

@ -11,7 +11,7 @@ use uv_distribution_types::Index;
use uv_fs::{Simplified, CWD};
use uv_normalize::{GroupName, PackageName, DEV_DEPENDENCIES};
use uv_pep508::{MarkerTree, RequirementOrigin, VerbatimUrl};
use uv_pypi_types::{Requirement, RequirementSource, SupportedEnvironments};
use uv_pypi_types::{ConflictingGroupList, Requirement, RequirementSource, SupportedEnvironments};
use uv_static::EnvVars;
use uv_warnings::{warn_user, warn_user_once};
@ -392,6 +392,15 @@ impl Workspace {
.and_then(|uv| uv.environments.as_ref())
}
/// Returns the set of conflicts for the workspace.
pub fn conflicting_groups(&self) -> ConflictingGroupList {
let mut conflicting = ConflictingGroupList::empty();
for member in self.packages.values() {
conflicting.append(&mut member.pyproject_toml.conflicting_groups());
}
conflicting
}
/// Returns the set of constraints for the workspace.
pub fn constraints(&self) -> Vec<Requirement> {
let Some(constraints) = self