Make version selection wheel-vs.-sdist-agnostic (#232)

Closes https://github.com/astral-sh/puffin/issues/231.
This commit is contained in:
Charlie Marsh 2023-10-30 08:21:10 -07:00 committed by GitHub
parent 8d992dca3f
commit 0be20a41a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 98 deletions

View file

@ -156,96 +156,63 @@ impl CandidateSelector {
range: &Range<PubGrubVersion>, range: &Range<PubGrubVersion>,
allow_prerelease: AllowPreRelease, allow_prerelease: AllowPreRelease,
) -> Option<Candidate> { ) -> Option<Candidate> {
// We prefer a stable wheel, followed by a prerelease wheel, followed by a stable sdist, #[derive(Debug)]
// followed by a prerelease sdist. enum PreReleaseCandidate<'a> {
let mut sdist = None; NotNecessary,
let mut prerelease_sdist = None; IfNecessary(&'a PubGrubVersion, &'a DistributionFile),
let mut prerelease_wheel = None; }
let mut prerelease = None;
for (version, file) in versions { for (version, file) in versions {
if range.contains(version) { if version.any_prerelease() {
match file { if range.contains(version) {
DistributionFile::Wheel(_) => { match allow_prerelease {
if version.any_prerelease() { AllowPreRelease::Yes => {
match allow_prerelease { // If pre-releases are allowed, treat them equivalently
AllowPreRelease::Yes => { // to stable distributions.
// If prereleases are allowed, treat them equivalently
// to stable wheels.
return Some(Candidate {
package_name: package_name.clone(),
version: version.clone(),
file: file.clone(),
});
}
AllowPreRelease::IfNecessary => {
// If prereleases are allowed as a fallback, store the
// first-matching prerelease wheel.
if prerelease_wheel.is_none() {
prerelease_wheel = Some(Candidate {
package_name: package_name.clone(),
version: version.clone(),
file: file.clone(),
});
}
}
AllowPreRelease::No => {
continue;
}
}
} else {
// Always return the first-matching stable wheel.
return Some(Candidate { return Some(Candidate {
package_name: package_name.clone(), package_name: package_name.clone(),
version: version.clone(), version: version.clone(),
file: file.clone(), file: file.clone(),
}); });
} }
} AllowPreRelease::IfNecessary => {
DistributionFile::Sdist(_) => { // If pre-releases are allowed as a fallback, store the
if version.any_prerelease() { // first-matching prerelease.
match allow_prerelease { if prerelease.is_none() {
AllowPreRelease::Yes => { prerelease = Some(PreReleaseCandidate::IfNecessary(version, file));
// If prereleases are allowed, treat them equivalently to
// stable sdists.
if sdist.is_none() {
sdist = Some(Candidate {
package_name: package_name.clone(),
version: version.clone(),
file: file.clone(),
});
}
}
AllowPreRelease::IfNecessary => {
// If prereleases are allowed as a fallback, store the
// first-matching prerelease sdist.
if prerelease_sdist.is_none() {
prerelease_sdist = Some(Candidate {
package_name: package_name.clone(),
version: version.clone(),
file: file.clone(),
});
}
}
AllowPreRelease::No => {
continue;
}
}
} else {
// Store the first-matching stable sdist.
if sdist.is_none() {
sdist = Some(Candidate {
package_name: package_name.clone(),
version: version.clone(),
file: file.clone(),
});
} }
} }
AllowPreRelease::No => {
continue;
}
} }
} }
} else {
// If we have at least one stable release, we shouldn't allow the "if-necessary"
// pre-release strategy, regardless of whether that stable release satisfies the
// current range.
prerelease = Some(PreReleaseCandidate::NotNecessary);
// Always return the first-matching stable distribution.
if range.contains(version) {
return Some(Candidate {
package_name: package_name.clone(),
version: version.clone(),
file: file.clone(),
});
}
} }
} }
match prerelease {
sdist.or(prerelease_wheel).or(prerelease_sdist) None => None,
Some(PreReleaseCandidate::NotNecessary) => None,
Some(PreReleaseCandidate::IfNecessary(version, file)) => Some(Candidate {
package_name: package_name.clone(),
version: version.clone(),
file: file.clone(),
}),
}
} }
} }

View file

@ -515,11 +515,10 @@ impl<'a, Context: BuildContext + Sync> Resolver<'a, Context> {
if let Ok(name) = WheelFilename::from_str(file.filename.as_str()) { if let Ok(name) = WheelFilename::from_str(file.filename.as_str()) {
if name.is_compatible(self.tags) { if name.is_compatible(self.tags) {
let version = PubGrubVersion::from(name.version); let version = PubGrubVersion::from(name.version);
match version_map.entry(version) { match version_map.entry(version) {
std::collections::btree_map::Entry::Occupied(mut entry) => { std::collections::btree_map::Entry::Occupied(mut entry) => {
if let DistributionFile::Sdist(_) = entry.get() { if matches!(entry.get(), DistributionFile::Sdist(_)) {
// Wheels get precedence over source distributions // Wheels get precedence over source distributions.
entry.insert(DistributionFile::from(WheelFile::from( entry.insert(DistributionFile::from(WheelFile::from(
file, file,
))); )));

View file

@ -134,7 +134,7 @@ async fn black_mypy_extensions() -> Result<()> {
let manifest = Manifest::new( let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()], vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![Requirement::from_str("mypy-extensions<1").unwrap()], vec![Requirement::from_str("mypy-extensions<0.4.4").unwrap()],
vec![], vec![],
ResolutionMode::default(), ResolutionMode::default(),
PreReleaseMode::default(), PreReleaseMode::default(),
@ -158,7 +158,7 @@ async fn black_mypy_extensions_extra() -> Result<()> {
let manifest = Manifest::new( let manifest = Manifest::new(
vec![Requirement::from_str("black<=23.9.1").unwrap()], vec![Requirement::from_str("black<=23.9.1").unwrap()],
vec![Requirement::from_str("mypy-extensions[extra]<1").unwrap()], vec![Requirement::from_str("mypy-extensions[extra]<0.4.4").unwrap()],
vec![], vec![],
ResolutionMode::default(), ResolutionMode::default(),
PreReleaseMode::default(), PreReleaseMode::default(),
@ -321,7 +321,7 @@ async fn black_allow_prerelease_if_necessary() -> Result<()> {
); );
let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext); let resolver = Resolver::new(manifest, &MARKERS_311, &TAGS_311, &client, &DummyContext);
let resolution = resolver.resolve().await?; let resolution = resolver.resolve().await.unwrap_err();
insta::assert_display_snapshot!(resolution); insta::assert_display_snapshot!(resolution);

View file

@ -2,19 +2,4 @@
source: crates/puffin-resolver/tests/resolver.rs source: crates/puffin-resolver/tests/resolver.rs
expression: resolution expression: resolution
--- ---
appdirs==1.4.4 No solution
# via black
attrs==23.1.0
# via black
black==19.10b0
click==8.1.7
# via black
pathspec==0.11.2
# via black
regex==2023.10.3
# via black
toml==0.10.2
# via black
typed-ast==1.5.5
# via black