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

fix: make rip handle pre-releases more similar to pip #140

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 27 additions & 10 deletions crates/rattler_installs_packages/src/resolve/dependency_provider.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::solve::PreReleaseResolution;
use super::SDistResolution;
use crate::artifacts::SDist;
use crate::artifacts::Wheel;
Expand Down Expand Up @@ -165,27 +166,38 @@ impl<'db, 'i> PypiDependencyProvider<'db, 'i> {
fn filter_candidates<'a>(
&self,
artifacts: &'a [ArtifactInfo],
all_pre_release: bool,
) -> Result<Vec<&'a ArtifactInfo>, &'static str> {
// Filter only artifacts we can work with
if artifacts.is_empty() {
// If there are no wheel artifacts, we're just gonna skip it
return Err("there are no packages available");
}

let mut artifacts = artifacts
.iter()
.filter(|a| a.filename.version().pre.is_none() && a.filename.version().dev.is_none())
.collect::<Vec<_>>();
let mut artifacts = artifacts.iter().collect::<Vec<_>>();
// Filter yanked artifacts
artifacts.retain(|a| !a.yanked.yanked);

if artifacts.is_empty() {
// Skip all prereleases
return Err("prereleases are not allowed");
return Err("it is yanked");
}

// Filter based on pre-release resolution
let remove_pre_releases = match self.options.pre_release_resolution {
PreReleaseResolution::Disallow => true,
PreReleaseResolution::AllowIfNoOtherVersions => !all_pre_release,
PreReleaseResolution::Allow => false,
};

if remove_pre_releases {
artifacts.retain(|a| {
a.filename.version().pre.is_none() && a.filename.version().dev.is_none()
})
}

// Filter yanked artifacts
artifacts.retain(|a| !a.yanked.yanked);
if artifacts.is_empty() {
return Err("it is yanked");
// Skip all prereleases
return Err("prereleases are not allowed");
}

// This should keep only the wheels
Expand Down Expand Up @@ -366,6 +378,11 @@ impl<'p> DependencyProvider<PypiVersionSet, PypiPackageName>
let mut candidates = Candidates::default();
let locked_package = self.locked_packages.get(package_name.base());
let favored_package = self.favored_packages.get(package_name.base());

let all_pre_release = artifacts
.iter()
.all(|(version, _)| version.pre.is_some() || version.dev.is_some());

for (version, artifacts) in artifacts.iter() {
// Skip this version if a locked or favored version exists for this version. It will be
// added below.
Expand All @@ -382,7 +399,7 @@ impl<'p> DependencyProvider<PypiVersionSet, PypiPackageName>
candidates.candidates.push(solvable_id);

// Determine the candidates
match self.filter_candidates(artifacts) {
match self.filter_candidates(artifacts, all_pre_release) {
Ok(artifacts) => {
self.cached_artifacts.insert(solvable_id, artifacts);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/rattler_installs_packages/src/resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
mod dependency_provider;
mod solve;

pub use solve::{resolve, PinnedPackage, ResolveOptions, SDistResolution};
pub use solve::{resolve, PinnedPackage, PreReleaseResolution, ResolveOptions, SDistResolution};
19 changes: 19 additions & 0 deletions crates/rattler_installs_packages/src/resolve/solve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,21 @@ pub enum SDistResolution {
OnlySDists,
}

/// Defines how to pre-releases are handled during package resolution.
#[derive(Default, Debug, Clone, Copy, Eq, PartialOrd, PartialEq)]
pub enum PreReleaseResolution {
/// Don't allow pre-releases to be selected during resolution
Disallow,

/// Allow pre-releases to be selected during resolution but only if there are no other versions
/// available (default)
#[default]
AllowIfNoOtherVersions,

/// Allow pre-releases to be selected during resolution
Allow,
}

impl SDistResolution {
/// Returns true if sdists are allowed to be selected during resolution
pub fn allow_sdists(&self) -> bool {
Expand All @@ -150,6 +165,10 @@ pub struct ResolveOptions {
/// Defines what python interpreter to use for resolution. By default the python interpreter
/// from the system is used. This is only used during resolution and building of wheel files
pub python_location: PythonLocation,

/// Defines whether pre-releases are allowed to be selected during resolution. By default
/// pre-releases are not allowed (only if there are no other versions available for a given dependency).
pub pre_release_resolution: PreReleaseResolution,
}

/// Resolves an environment that contains the given requirements and all dependencies of those
Expand Down
12 changes: 12 additions & 0 deletions crates/rip_bin/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use rattler_installs_packages::resolve::PreReleaseResolution;
use rip_bin::{global_multi_progress, IndicatifWriter};
use std::collections::HashMap;
use std::io::Write;
Expand Down Expand Up @@ -40,6 +41,10 @@ struct Args {

#[clap(flatten)]
sdist_resolution: SDistResolution,

/// Prefer pre-releases over normal releases
#[clap(long)]
pre: bool,
}

#[derive(Parser)]
Expand Down Expand Up @@ -128,8 +133,15 @@ async fn actual_main() -> miette::Result<()> {
compatible_tags.tags().format(", ")
);

let pre_release_resolution = if args.pre {
PreReleaseResolution::Allow
} else {
PreReleaseResolution::AllowIfNoOtherVersions
};

let resolve_opts = ResolveOptions {
sdist_resolution: args.sdist_resolution.into(),
pre_release_resolution,
..Default::default()
};
// Solve the environment
Expand Down