diff --git a/crates/rattler_installs_packages/src/resolve/dependency_provider.rs b/crates/rattler_installs_packages/src/resolve/dependency_provider.rs index d9f53dfc..a7304553 100644 --- a/crates/rattler_installs_packages/src/resolve/dependency_provider.rs +++ b/crates/rattler_installs_packages/src/resolve/dependency_provider.rs @@ -1,3 +1,4 @@ +use super::solve::PreReleaseResolution; use super::SDistResolution; use crate::artifacts::SDist; use crate::artifacts::Wheel; @@ -165,6 +166,7 @@ impl<'db, 'i> PypiDependencyProvider<'db, 'i> { fn filter_candidates<'a>( &self, artifacts: &'a [ArtifactInfo], + all_pre_release: bool, ) -> Result, &'static str> { // Filter only artifacts we can work with if artifacts.is_empty() { @@ -172,20 +174,30 @@ impl<'db, 'i> PypiDependencyProvider<'db, 'i> { 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::>(); + let mut artifacts = artifacts.iter().collect::>(); + // 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 @@ -366,6 +378,11 @@ impl<'p> DependencyProvider 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. @@ -382,7 +399,7 @@ impl<'p> DependencyProvider 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); } diff --git a/crates/rattler_installs_packages/src/resolve/mod.rs b/crates/rattler_installs_packages/src/resolve/mod.rs index c8f8d974..ff90cd6a 100644 --- a/crates/rattler_installs_packages/src/resolve/mod.rs +++ b/crates/rattler_installs_packages/src/resolve/mod.rs @@ -11,4 +11,4 @@ mod dependency_provider; mod solve; -pub use solve::{resolve, PinnedPackage, ResolveOptions, SDistResolution}; +pub use solve::{resolve, PinnedPackage, PreReleaseResolution, ResolveOptions, SDistResolution}; diff --git a/crates/rattler_installs_packages/src/resolve/solve.rs b/crates/rattler_installs_packages/src/resolve/solve.rs index c23abb4d..10357388 100644 --- a/crates/rattler_installs_packages/src/resolve/solve.rs +++ b/crates/rattler_installs_packages/src/resolve/solve.rs @@ -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 { @@ -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 diff --git a/crates/rip_bin/src/main.rs b/crates/rip_bin/src/main.rs index 8d0b4b26..6cd63f01 100644 --- a/crates/rip_bin/src/main.rs +++ b/crates/rip_bin/src/main.rs @@ -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; @@ -40,6 +41,10 @@ struct Args { #[clap(flatten)] sdist_resolution: SDistResolution, + + /// Prefer pre-releases over normal releases + #[clap(long)] + pre: bool, } #[derive(Parser)] @@ -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