Skip to content

Introduce a --fork-strategy preference mode #9868

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

Merged
merged 2 commits into from
Dec 13, 2024
Merged
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
56 changes: 55 additions & 1 deletion crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use uv_normalize::{ExtraName, GroupName, PackageName};
use uv_pep508::Requirement;
use uv_pypi_types::VerbatimParsedUrl;
use uv_python::{PythonDownloads, PythonPreference, PythonVersion};
use uv_resolver::{AnnotationStyle, ExcludeNewer, PrereleaseMode, ResolutionMode};
use uv_resolver::{AnnotationStyle, ExcludeNewer, ForkStrategy, PrereleaseMode, ResolutionMode};
use uv_static::EnvVars;

pub mod comma;
Expand Down Expand Up @@ -4045,6 +4045,24 @@ pub struct ToolUpgradeArgs {
#[arg(long, hide = true)]
pub pre: bool,

/// The strategy to use when selecting multiple versions of a given package across Python
/// versions and platforms.
///
/// By default, uv will optimize for selecting the latest version of each package for each
/// supported Python version (`requires-python`), while minimizing the number of selected
/// versions across platforms.
///
/// Under `fewest`, uv will minimize the number of
/// selected versions for each package, preferring older versions that are compatible with a
/// wider range of supported Python versions or platforms.
#[arg(
long,
value_enum,
env = EnvVars::UV_FORK_STRATEGY,
help_heading = "Resolver options"
)]
pub fork_strategy: Option<ForkStrategy>,

/// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs.
#[arg(
long,
Expand Down Expand Up @@ -4834,6 +4852,24 @@ pub struct ResolverArgs {
#[arg(long, hide = true, help_heading = "Resolver options")]
pub pre: bool,

/// The strategy to use when selecting multiple versions of a given package across Python
/// versions and platforms.
///
/// By default, uv will optimize for selecting the latest version of each package for each
/// supported Python version (`requires-python`), while minimizing the number of selected
/// versions across platforms.
///
/// Under `fewest`, uv will minimize the number of
/// selected versions for each package, preferring older versions that are compatible with a
/// wider range of supported Python versions or platforms.
#[arg(
long,
value_enum,
env = EnvVars::UV_FORK_STRATEGY,
help_heading = "Resolver options"
)]
pub fork_strategy: Option<ForkStrategy>,

/// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs.
#[arg(
long,
Expand Down Expand Up @@ -5006,6 +5042,24 @@ pub struct ResolverInstallerArgs {
#[arg(long, hide = true)]
pub pre: bool,

/// The strategy to use when selecting multiple versions of a given package across Python
/// versions and platforms.
///
/// By default, uv will optimize for selecting the latest version of each package for each
/// supported Python version (`requires-python`), while minimizing the number of selected
/// versions across platforms.
///
/// Under `fewest`, uv will minimize the number of
/// selected versions for each package, preferring older versions that are compatible with a
/// wider range of supported Python versions or platforms.
#[arg(
long,
value_enum,
env = EnvVars::UV_FORK_STRATEGY,
help_heading = "Resolver options"
)]
pub fork_strategy: Option<ForkStrategy>,

/// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs.
#[arg(
long,
Expand Down
8 changes: 8 additions & 0 deletions crates/uv-cli/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl From<ResolverArgs> for PipOptions {
resolution,
prerelease,
pre,
fork_strategy,
config_setting,
no_build_isolation,
no_build_isolation_package,
Expand All @@ -58,6 +59,7 @@ impl From<ResolverArgs> for PipOptions {
index_strategy,
keyring_provider,
resolution,
fork_strategy,
prerelease: if pre {
Some(PrereleaseMode::Allow)
} else {
Expand Down Expand Up @@ -126,6 +128,7 @@ impl From<ResolverInstallerArgs> for PipOptions {
resolution,
prerelease,
pre,
fork_strategy,
config_setting,
no_build_isolation,
no_build_isolation_package,
Expand All @@ -150,6 +153,7 @@ impl From<ResolverInstallerArgs> for PipOptions {
} else {
prerelease
},
fork_strategy,
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
no_build_isolation: flag(no_build_isolation, build_isolation),
Expand Down Expand Up @@ -235,6 +239,7 @@ pub fn resolver_options(
resolution,
prerelease,
pre,
fork_strategy,
config_setting,
no_build_isolation,
no_build_isolation_package,
Expand Down Expand Up @@ -291,6 +296,7 @@ pub fn resolver_options(
} else {
prerelease
},
fork_strategy,
dependency_metadata: None,
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
Expand Down Expand Up @@ -324,6 +330,7 @@ pub fn resolver_installer_options(
resolution,
prerelease,
pre,
fork_strategy,
config_setting,
no_build_isolation,
no_build_isolation_package,
Expand Down Expand Up @@ -392,6 +399,7 @@ pub fn resolver_installer_options(
} else {
prerelease
},
fork_strategy,
dependency_metadata: None,
config_settings: config_setting
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
Expand Down
4 changes: 3 additions & 1 deletion crates/uv-python/src/python_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ impl schemars::JsonSchema for PythonVersion {
..schemars::schema::StringValidation::default()
})),
metadata: Some(Box::new(schemars::schema::Metadata {
description: Some("A Python version specifier, e.g. `3.7` or `3.8.0`.".to_string()),
description: Some(
"A Python version specifier, e.g. `3.11` or `3.12.4`.".to_string(),
),
..schemars::schema::Metadata::default()
})),
..schemars::schema::SchemaObject::default()
Expand Down
23 changes: 23 additions & 0 deletions crates/uv-resolver/src/fork_strategy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum ForkStrategy {
/// Optimize for selecting the fewest number of versions for each package. Older versions may
/// be preferred if they are compatible with a wider range of supported Python versions or
/// platforms.
Fewest,
/// Optimize for selecting latest supported version of each package, for each supported Python
/// version.
#[default]
RequiresPython,
}

impl std::fmt::Display for ForkStrategy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fewest => write!(f, "fewest"),
Self::RequiresPython => write!(f, "requires-python"),
}
}
}
2 changes: 2 additions & 0 deletions crates/uv-resolver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub use error::{NoSolutionError, NoSolutionHeader, ResolveError, SentinelRange};
pub use exclude_newer::ExcludeNewer;
pub use exclusions::Exclusions;
pub use flat_index::{FlatDistributions, FlatIndex};
pub use fork_strategy::ForkStrategy;
pub use lock::{
InstallTarget, Lock, LockError, LockVersion, PackageMap, RequirementsTxtExport,
ResolverManifest, SatisfiesResult, TreeDisplay, VERSION,
Expand Down Expand Up @@ -41,6 +42,7 @@ mod exclude_newer;
mod exclusions;
mod flat_index;
mod fork_indexes;
mod fork_strategy;
mod fork_urls;
mod graph_ops;
mod lock;
Expand Down
16 changes: 16 additions & 0 deletions crates/uv-resolver/src/lock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::sync::{Arc, LazyLock};
use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value};
use url::Url;

use crate::fork_strategy::ForkStrategy;
pub use crate::lock::map::PackageMap;
pub use crate::lock::requirements_txt::RequirementsTxtExport;
pub use crate::lock::target::InstallTarget;
Expand Down Expand Up @@ -239,6 +240,7 @@ impl Lock {
let options = ResolverOptions {
resolution_mode: resolution.options.resolution_mode,
prerelease_mode: resolution.options.prerelease_mode,
fork_strategy: resolution.options.fork_strategy,
exclude_newer: resolution.options.exclude_newer,
};
let lock = Self::new(
Expand Down Expand Up @@ -548,6 +550,11 @@ impl Lock {
self.options.prerelease_mode
}

/// Returns the multi-version mode used to generate this lock.
pub fn fork_strategy(&self) -> ForkStrategy {
self.options.fork_strategy
}

/// Returns the exclude newer setting used to generate this lock.
pub fn exclude_newer(&self) -> Option<ExcludeNewer> {
self.options.exclude_newer
Expand Down Expand Up @@ -675,6 +682,12 @@ impl Lock {
value(self.options.prerelease_mode.to_string()),
);
}
if self.options.fork_strategy != ForkStrategy::default() {
options_table.insert(
"fork-strategy",
value(self.options.fork_strategy.to_string()),
);
}
if let Some(exclude_newer) = self.options.exclude_newer {
options_table.insert("exclude-newer", value(exclude_newer.to_string()));
}
Expand Down Expand Up @@ -1317,6 +1330,9 @@ struct ResolverOptions {
/// The [`PrereleaseMode`] used to generate this lock.
#[serde(default)]
prerelease_mode: PrereleaseMode,
/// The [`ForkStrategy`] used to generate this lock.
#[serde(default)]
fork_strategy: ForkStrategy,
/// The [`ExcludeNewer`] used to generate this lock.
exclude_newer: Option<ExcludeNewer>,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/uv-resolver/src/lock/tests.rs
source: crates/uv-resolver/src/lock/mod.rs
expression: result
---
Ok(
Expand Down Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/uv-resolver/src/lock/tests.rs
source: crates/uv-resolver/src/lock/mod.rs
expression: result
---
Ok(
Expand Down Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/uv-resolver/src/lock/tests.rs
source: crates/uv-resolver/src/lock/mod.rs
expression: result
---
Ok(
Expand Down Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/uv-resolver/src/lock/tests.rs
source: crates/uv-resolver/src/lock/mod.rs
expression: result
---
Ok(
Expand Down Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/uv-resolver/src/lock/tests.rs
source: crates/uv-resolver/src/lock/mod.rs
expression: result
---
Ok(
Expand Down Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/uv-resolver/src/lock/tests.rs
source: crates/uv-resolver/src/lock/mod.rs
expression: result
---
Ok(
Expand Down Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
source: crates/uv-resolver/src/lock/tests.rs
source: crates/uv-resolver/src/lock/mod.rs
expression: result
---
Ok(
Expand Down Expand Up @@ -33,6 +33,7 @@ Ok(
options: ResolverOptions {
resolution_mode: Highest,
prerelease_mode: IfNecessaryOrExplicit,
fork_strategy: RequiresPython,
exclude_newer: None,
},
packages: [
Expand Down
Loading
Loading