Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make universal resolver fork only when markers are disjoint #4135

Merged
merged 3 commits into from
Jun 7, 2024

Conversation

BurntSushi
Copy link
Member

@BurntSushi BurntSushi commented Jun 7, 2024

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: astral-sh/packse#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.

Closes #3926

Copy link
Member

@charliermarsh charliermarsh left a comment

Choose a reason for hiding this comment

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

This looks good to me! I'd like to see some tests eventually for combinations of forks and markers, like...

flask
flask[dotenv] ; python_version > '3.7'
flask[async] ; python_version <= '3.7'

@@ -1,5 +1,7 @@
//! Given a set of requirements, find a set of compatible packages.

#![allow(warnings)]
Copy link
Member

Choose a reason for hiding this comment

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

Why this?

Copy link
Member

Choose a reason for hiding this comment

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

I removed it and fixed the lint issues, hope that's ok.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ug yeah. Sometimes I add this while writing new code (to silence lots of "unused code" warnings) and forget to remove it. Yes, it's okay!

// *only* when the markers between two identically named packages are disjoint.
// So if there are, say, multiple `Extra` packages for the same name, they should
// all have the same markers (I believe), and thus they are not disjoint and thus
// they should not provoke a fork.
Copy link
Member

Choose a reason for hiding this comment

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

We can probably remove these comments then.

Copy link
Member

Choose a reason for hiding this comment

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

I removed them...

@charliermarsh
Copy link
Member

I'm gonna merge this so that I can build atop it if need be.

…ypes

This commit does not change any behavior, but instead changes
`get_dependencies_forking` (and its caller) to use more structured data
types describing the result of forking (or not).

This commit is a precursor to forking based only on whether markers
between conflicting dependency specifications are disjoint.
The previous way we did forking was merely by looking at whether there were
duplicate dependency specifications (with respect to package name), and if
their version constraints were different, we forked. But this is
incorrect (and was known to be, it was a shortcut). We only want to fork
when we can prove that the resolution produced will never result in
installing two different versions of the same package. That in turn can
only happen when there are marker expressions attached to conflicting
dependency specifications that never overlap.

This does slightly change the test output, but only the ordering of
packages. This might be because of the fork refactoring. We may want to
investigate making the test output more stable, but we punt on that for
now.
PubGrubPackageInner::Package { name, marker, .. }
| PubGrubPackageInner::Extra { name, marker, .. }
| PubGrubPackageInner::Dev { name, marker, .. } => (name, marker.as_ref()),
PubGrubPackageInner::Marker { name, marker, .. } => (name, Some(marker)),
Copy link
Member

Choose a reason for hiding this comment

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

I removed the ref name and ref marker here and now use marker.as_ref(), because the PubGrubPackageInner::Marker arm needs needs to return Option<&Marker> not &Option<Marker>.

@charliermarsh charliermarsh enabled auto-merge (squash) June 7, 2024 23:33
@charliermarsh charliermarsh added the preview Experimental behavior label Jun 7, 2024
@charliermarsh charliermarsh merged commit c46fa74 into main Jun 7, 2024
46 checks passed
@charliermarsh charliermarsh deleted the ag/fork-markers branch June 7, 2024 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
preview Experimental behavior
Projects
None yet
Development

Successfully merging this pull request may close these issues.

universal-lock: check that markers are disjoint before forking
2 participants