Skip to content

add support for package level conflicts with groups#9130

Closed
BurntSushi wants to merge 22 commits intomainfrom
ag/package-level-conflict
Closed

add support for package level conflicts with groups#9130
BurntSushi wants to merge 22 commits intomainfrom
ag/package-level-conflict

Conversation

@BurntSushi
Copy link
Copy Markdown
Member

This PR adds support for declaring a conflict between a group
and a project's "production" dependencies. For example:

[project]
name = "example"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "anyio>=4.6.2.post1",
]

[dependency-groups]
foo = [
    "anyio<4.6.2.post1",
]

[tool.uv]
conflicts = [
  [
    { group = "foo" },
    { package = "example" },
  ],
]

Before this PR, syntax like { package = "example" } wasn't allowed.
This PR enables that syntax, and also tweaks the forking logic a bit
to support this.

I don't have 100% confidence in this PR. In particular, I'm not sure
if my trick adding a parent package to PubGrubDependency is fully
correct. But I think it works.

@BurntSushi BurntSushi force-pushed the ag/package-level-conflict branch from 112eb32 to 5bc4ca5 Compare November 14, 2024 19:37
@BurntSushi BurntSushi force-pushed the ag/package-level-conflict branch from 5bc4ca5 to 15202ea Compare November 14, 2024 19:41
This makes a lot more sense as a name IMO. And I think also works better
for the immediate future, where I plan to add a `Project` kind.
Basically, this new conflict kind means that the entire project
conflicts with a dependency group or an extra.

This just adds the variant. In the next commit, we'll actually
make it work.
Supporting project level conflicts ends up being pretty tricky, mostly
because depenedency groups are represented as dependencies of the
project you're trying to declare a conflict for. So by filtering out the
project in the fork for the conflicting group, you end up filtering out
the group itself too.

To work-around this, we add a `parent` field to `PubGrubDependency`, and
use this to filter based on project conflicts. This lets us do "delayed"
filtering by one level.

The rest of the changes in this commit are for reporting errors
when you try to activate the group without disabling the project.
@BurntSushi BurntSushi force-pushed the ag/package-level-conflict branch from 15202ea to 7fba6b5 Compare November 15, 2024 12:30
@zanieb
Copy link
Copy Markdown
Member

zanieb commented Nov 16, 2024

(I think the experience makes sense, not feeling confident about reviewing the implementation)

@BurntSushi BurntSushi added the resolver Related to the package resolver label Nov 23, 2024
@zanieb
Copy link
Copy Markdown
Member

zanieb commented Jul 10, 2025

I've done a very dubious update with main.

@zanieb zanieb force-pushed the ag/package-level-conflict branch from c8c89e1 to 3d3a5d8 Compare July 10, 2025 19:18
@zanieb zanieb temporarily deployed to uv-test-registries July 10, 2025 19:20 — with GitHub Actions Inactive
@zanieb zanieb force-pushed the ag/package-level-conflict branch from 3d3a5d8 to b5a631f Compare July 10, 2025 19:23
@zanieb zanieb temporarily deployed to uv-test-registries July 10, 2025 19:25 — with GitHub Actions Inactive
@zanieb zanieb force-pushed the ag/package-level-conflict branch from b5a631f to 63122fe Compare July 10, 2025 19:28
@zanieb zanieb temporarily deployed to uv-test-registries July 10, 2025 19:30 — with GitHub Actions Inactive
@zanieb zanieb force-pushed the ag/package-level-conflict branch from 63122fe to 8025a23 Compare July 10, 2025 19:32
@zanieb zanieb temporarily deployed to uv-test-registries July 10, 2025 19:36 — with GitHub Actions Inactive
@zanieb zanieb force-pushed the ag/package-level-conflict branch from 8025a23 to 4c1f9df Compare July 10, 2025 22:39
@zanieb zanieb temporarily deployed to uv-test-registries July 10, 2025 22:42 — with GitHub Actions Inactive
@zanieb zanieb force-pushed the ag/package-level-conflict branch from 4c1f9df to 5248497 Compare July 11, 2025 12:55
@zanieb zanieb had a problem deploying to uv-test-registries July 11, 2025 12:56 — with GitHub Actions Failure
@zanieb zanieb force-pushed the ag/package-level-conflict branch from 5248497 to 528c2ac Compare July 11, 2025 13:08
@zanieb zanieb temporarily deployed to uv-test-registries July 11, 2025 13:09 — with GitHub Actions Inactive
@zanieb zanieb self-assigned this Jul 18, 2025
@zanieb zanieb force-pushed the ag/package-level-conflict branch from 528c2ac to 39c3ccf Compare July 22, 2025 11:41
@zanieb zanieb temporarily deployed to uv-test-registries July 22, 2025 11:43 — with GitHub Actions Inactive
@BurntSushi
Copy link
Copy Markdown
Member Author

OK, I've pushed up a commit that I think gets this PR into working shape. There was a fair bit missing from UniversalMarker in sections that I think came after this PR was originally written. There was also a test asserting incorrect results (in the same way that the example in the PR description failed).

@BurntSushi does this also enable package <-> package conflicts? I'd expect that for workspace members. I think the use-case there is more important than package <-> group conflicts. (I can investigate this too, we'll want test coverage)

I think all the bones for this are there, but it doesn't seem to be fully working. Here's a simple concrete example. The root pyproject.toml:

[project]
name = "example"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["sortedcontainers==2.3.0"]

[tool.uv.workspace]
members = ["subexample"]

[tool.uv]
conflicts = [
  [
    { package = "example" },
    { package = "subexample" },
  ],
]

[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"

And now subexample/pyproject.toml:

[project]
name = "subexample"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = ["sortedcontainers==2.4.0"]

Without the conflicts declared here, this would normally fail to generate a lock file. But that part works and seems to generate a correct lock. But syncing fails (ignore the bad error messages for now):

$ run-uv -q pr1 -- sync
Resolved 4 packages in 5ms
error: The project and the project are incompatible with the declared conflicts: {the project, the project}

$ run-uv -q pr1 -- sync --package example
Resolved 4 packages in 3ms
error: The project and the project are incompatible with the declared conflicts: {the project, the project}

$ run-uv -q pr1 -- sync --package subexample
Resolved 4 packages in 6ms
error: The project and the project are incompatible with the declared conflicts: {the project, the project}

In order for these package level conflicts to work, there has to be way to sync only a specific package or set of packages.

@konstin
Copy link
Copy Markdown
Member

konstin commented Jul 23, 2025

(I'm still tagged here from the previous review, let me know if you want me to do another round)

@zanieb
Copy link
Copy Markdown
Member

zanieb commented Jul 23, 2025

Thank you for addressing the problems!

there has to be way to sync only a specific package or set of packages.

We do already sync a subset, e.g., I don't think subexample would be installed there without --all-packages since it's not a dependency? I'll look into this.

@zanieb zanieb temporarily deployed to uv-test-registries July 24, 2025 01:26 — with GitHub Actions Inactive
@zanieb zanieb temporarily deployed to uv-test-registries July 24, 2025 21:21 — with GitHub Actions Inactive
@zanieb zanieb temporarily deployed to uv-test-registries July 24, 2025 21:24 — with GitHub Actions Inactive
@zanieb zanieb temporarily deployed to uv-test-registries July 24, 2025 22:17 — with GitHub Actions Inactive
"#,
)?;

uv_snapshot!(context.filters(), context.lock(), @r###"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just insta version churn / noise in this file.

zanieb added a commit that referenced this pull request Jul 24, 2025
@zanieb
Copy link
Copy Markdown
Member

zanieb commented Jul 25, 2025

I'm going to move this to another pull request since it's got a pretty different scope now and it's a pain to keep looking up Andrew's PRs instead of my own :)

See #14906

@zanieb zanieb closed this Jul 25, 2025
zanieb added a commit that referenced this pull request Aug 8, 2025
Revives #9130

Previously, we allowed scoping conflicting extras or groups to specific
packages, e.g. ,`{ package = "foo", extra = "bar" }` for a conflict in
`foo[bar]`. Now, we allow dropping the `extra` or `group` bit and using
`{ package = "foo" }` directly which declares a conflict with `foo`'s
production dependencies.

This means you can declare conflicts between workspace members, e.g.:

```
[tool.uv]
conflicts = [[{ package = "foo" }, { package = "bar" }]]
```

would not allow `foo` and `bar` to be installed at the same time.

Similarly, a conflict can be declared between a package and a group:

```
[tool.uv]
conflicts = [[{ package = "foo" }, { group = "lint" }]]
```

which would mean, e.g., that `--only-group lint` would be required for
the invocation.

As with our existing support for conflicting extras, there are
edge-cases here where the resolver will _not_ fail even if there are
conflicts that render a particular install target unusable. There's test
coverage for some of these. We'll still error at install-time when the
conflicting groups are selected. Due to the likelihood of bugs in this
feature, I've marked it as a preview feature.

I would not recommend reading the commits as there's some slop from not
wanting to rebase Andrew's branch.

---------

Co-authored-by: Andrew Gallant <andrew@astral.sh>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

resolver Related to the package resolver

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants