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

Allow conflicting locals when forking #5104

Merged
merged 2 commits into from
Jul 16, 2024
Merged

Conversation

ibraheemdev
Copy link
Member

@ibraheemdev ibraheemdev commented Jul 16, 2024

Summary

Currently, the Locals type relies on there being a single local version for a given package. With marker expressions this may not be true, a similar problem to #4435. This changes the Locals type to ForkLocals, which tracks locals for a given fork. Local versions are now tracked on PubGrubRequirement before forking.

Resolves #4580.

@ibraheemdev ibraheemdev added the lock Related to universal resolution and locking label Jul 16, 2024
Copy link
Member

@BurntSushi BurntSushi left a comment

Choose a reason for hiding this comment

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

w00t!

@@ -23,23 +23,26 @@ pub(crate) struct PubGrubDependency {
/// even if this field is None where there is an override with a URL or there is a different
/// requirement or constraint for the same package that has a URL.
pub(crate) url: Option<VerbatimParsedUrl>,
/// The local version for this requirement, if specified.
pub(crate) local: Option<Version>,
Copy link
Member

Choose a reason for hiding this comment

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

Is it true that if local is Some, then Version::is_local will always be true?

If so, then perhaps this could just be a Version, and case analysis uses Version::is_local or Version::local.

Copy link
Member

Choose a reason for hiding this comment

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

After reading through the PR, I retract my suggestion. I see how this is very convenient because the acquisition of a local-only Version is pretty naturally expressed via Option<Version>. And I don't think you ever look at a local and try to access its local segments, so there isn't really any redundant case analysis.

Copy link
Member Author

Choose a reason for hiding this comment

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

Is it true that if local is Some, then Version::is_local will always be true?

This is actually not true, because we also extract local versions from direct URL/path requirements.

crates/uv-resolver/src/resolver/locals.rs Show resolved Hide resolved
# via torch
torch==2.0.0+cpu ; platform_machine != 'x86_64'
# via -r requirements.in
torch==2.0.0+cu118 ; platform_machine == 'x86_64'
Copy link
Member

Choose a reason for hiding this comment

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

Sweet

crates/uv/tests/pip_compile.rs Show resolved Hide resolved
@ibraheemdev ibraheemdev enabled auto-merge (squash) July 16, 2024 16:49
@ibraheemdev ibraheemdev merged commit d583847 into main Jul 16, 2024
51 checks passed
@ibraheemdev ibraheemdev deleted the ibraheem/fork-locals branch July 16, 2024 16:57
zanieb pushed a commit that referenced this pull request Jul 19, 2024
## Summary

This fixes a few bugs introduced by
#5104. I previously thought we could
track conflicting locals the same way we track conflicting URLs in
forks, but it turns out that ends up being very tricky. URL forks work
because we prioritize directly URL requirements. We can't prioritize
locals in the same way without conflicting with the URL prioritization
(this may be possible but it's not trivial), so we run into issues where
a correct resolution depends on the order in which dependencies are
traversed.

Instead, we track local versions across all forks in `Locals`. When
applying a local version, we apply all locals with markers that
intersect with the current fork. This way we end up applying some local
versions without creating a fork. For example, given:
```
// pyproject.toml
dependencies = [
    "torch==2.0.0+cu118 ; platform_machine == 'x86_64'",
]

// requirements.in
torch==2.0.0
.
```

We choose `2.0.0+cu118` in all cases. However, if a disjoint fork is
created based on local versions, the resolver will choose the most
compatible local when it narrows to a specific fork. Thus we correctly
respect local versions when forking:
```
// pyproject.toml
dependencies = [
    "torch==2.0.0+cu118 ; platform_machine == 'x86_64'",
    "torch==2.0.0+cpu ; platform_machine != 'x86_64'"
]

// requirements.in
torch==2.0.0
.
``` 

We should also be able to use a similar strategy for
#5150.

## Test Plan

This fixes #5220 locally for me,
as well as a few other bugs that were not reported yet.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lock Related to universal resolution and locking
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow different locals in forks
2 participants