From f008aa9abccc6575cb3c3ff7a7f5430800fb0e30 Mon Sep 17 00:00:00 2001 From: konstin Date: Wed, 24 Jul 2024 11:43:01 +0200 Subject: [PATCH 1/3] Store the resolution fork markers to the lockfile Add the overall list of forks to the lockfiles, so we can use them as preferences to avoid instabilities in the next step. I've ordered the structs so that metadata is on top, and payload at the bottom, as it is in the lockfile itself. --- crates/uv-resolver/src/lock.rs | 44 ++++++++---- crates/uv-resolver/src/resolution/graph.rs | 30 +++++++- .../src/resolver/resolver_markers.rs | 1 + ...r__lock__tests__hash_optional_missing.snap | 9 +-- ...r__lock__tests__hash_optional_present.snap | 9 +-- ...r__lock__tests__hash_required_present.snap | 9 +-- ...missing_dependency_source_unambiguous.snap | 9 +-- ...dependency_source_version_unambiguous.snap | 9 +-- ...issing_dependency_version_unambiguous.snap | 9 +-- ...lock__tests__source_direct_has_subdir.snap | 9 +-- ..._lock__tests__source_direct_no_subdir.snap | 9 +-- ...solver__lock__tests__source_directory.snap | 9 +-- ...esolver__lock__tests__source_editable.snap | 9 +-- crates/uv/tests/branching_urls.rs | 16 +++++ crates/uv/tests/lock.rs | 18 +++++ crates/uv/tests/lock_scenarios.rs | 71 +++++++++++++++++++ 16 files changed, 217 insertions(+), 53 deletions(-) diff --git a/crates/uv-resolver/src/lock.rs b/crates/uv-resolver/src/lock.rs index cd781e465db3..acc21ce82705 100644 --- a/crates/uv-resolver/src/lock.rs +++ b/crates/uv-resolver/src/lock.rs @@ -53,7 +53,10 @@ const VERSION: u32 = 1; #[serde(try_from = "LockWire")] pub struct Lock { version: u32, - distributions: Vec, + /// If this lockfile was built from a forking resolution with non-identical forks, store the + /// forks in the lockfile so we can recreate them in subsequent resolutions. + #[serde(rename = "environment-markers")] + fork_markers: Option>, /// The range of supported Python versions. requires_python: Option, /// The [`ResolutionMode`] used to generate this lock. @@ -62,6 +65,8 @@ pub struct Lock { prerelease_mode: PreReleaseMode, /// The [`ExcludeNewer`] used to generate this lock. exclude_newer: Option, + /// The actual locked version and their metadata. + distributions: Vec, /// A map from distribution ID to index in `distributions`. /// /// This can be used to quickly lookup the full distribution for any ID @@ -159,6 +164,7 @@ impl Lock { options.resolution_mode, options.prerelease_mode, options.exclude_newer, + graph.fork_markers.clone(), )?; Ok(lock) } @@ -171,6 +177,7 @@ impl Lock { resolution_mode: ResolutionMode, prerelease_mode: PreReleaseMode, exclude_newer: Option, + fork_markers: Option>, ) -> Result { // Put all dependencies for each distribution in a canonical order and // check for duplicates. @@ -324,11 +331,12 @@ impl Lock { } Ok(Self { version, - distributions, + fork_markers, requires_python, resolution_mode, prerelease_mode, exclude_newer, + distributions, by_id, }) } @@ -451,6 +459,11 @@ impl Lock { if let Some(ref requires_python) = self.requires_python { doc.insert("requires-python", value(requires_python.to_string())); } + if let Some(ref fork_markers) = self.fork_markers { + let fork_markers = + each_element_on_its_line_array(fork_markers.iter().map(ToString::to_string)); + doc.insert("environment-markers", value(fork_markers)); + } // Write the settings that were used to generate the resolution. // This enables us to invalidate the lockfile if the user changes @@ -571,31 +584,36 @@ impl Lock { #[serde(rename_all = "kebab-case")] struct LockWire { version: u32, - #[serde(rename = "distribution", default)] - distributions: Vec, #[serde(default)] requires_python: Option, + /// If this lockfile was built from a forking resolution with non-identical forks, store the + /// forks in the lockfile so we can recreate them in subsequent resolutions. + #[serde(rename = "environment-markers")] + fork_markers: Option>, #[serde(default)] resolution_mode: ResolutionMode, #[serde(default)] prerelease_mode: PreReleaseMode, #[serde(default)] exclude_newer: Option, + #[serde(rename = "distribution", default)] + distributions: Vec, } impl From for LockWire { fn from(lock: Lock) -> LockWire { LockWire { version: lock.version, + requires_python: lock.requires_python, + fork_markers: lock.fork_markers, + resolution_mode: lock.resolution_mode, + prerelease_mode: lock.prerelease_mode, + exclude_newer: lock.exclude_newer, distributions: lock .distributions .into_iter() .map(DistributionWire::from) .collect(), - requires_python: lock.requires_python, - resolution_mode: lock.resolution_mode, - prerelease_mode: lock.prerelease_mode, - exclude_newer: lock.exclude_newer, } } } @@ -633,6 +651,7 @@ impl TryFrom for Lock { wire.resolution_mode, wire.prerelease_mode, wire.exclude_newer, + wire.fork_markers, ) } } @@ -2546,12 +2565,13 @@ impl std::fmt::Display for HashParseError { /// { name = "sniffio" }, /// ] /// ``` -fn each_element_on_its_line_array(elements: impl Iterator) -> Array { +fn each_element_on_its_line_array(elements: impl Iterator>) -> Array { let mut array = elements - .map(|mut inline_table| { + .map(|item| { + let mut value = item.into(); // Each dependency is on its own line and indented. - inline_table.decor_mut().set_prefix("\n "); - inline_table + value.decor_mut().set_prefix("\n "); + value }) .collect::(); // With a trailing comma, inserting another entry doesn't change the preceding line, diff --git a/crates/uv-resolver/src/resolution/graph.rs b/crates/uv-resolver/src/resolution/graph.rs index 3b5f8eee2142..12c5edc9feb4 100644 --- a/crates/uv-resolver/src/resolution/graph.rs +++ b/crates/uv-resolver/src/resolution/graph.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use indexmap::IndexSet; use petgraph::{ graph::{Graph, NodeIndex}, @@ -25,7 +27,7 @@ use crate::resolution::AnnotatedDist; use crate::resolver::{Resolution, ResolutionDependencyEdge, ResolutionPackage}; use crate::{ InMemoryIndex, MetadataResponse, Options, PythonRequirement, RequiresPython, ResolveError, - VersionsResponse, + ResolverMarkers, VersionsResponse, }; /// A complete resolution graph in which every node represents a pinned package and every edge @@ -36,6 +38,9 @@ pub struct ResolutionGraph { pub(crate) petgraph: Graph, Directed>, /// The range of supported Python versions. pub(crate) requires_python: Option, + /// If the resolution had non-identical forks, store the forks in the lockfile so we can + /// recreate them in subsequent resolutions. + pub(crate) fork_markers: Option>, /// Any diagnostics that were encountered while building the graph. pub(crate) diagnostics: Vec, /// The requirements that were used to build the graph. @@ -138,6 +143,28 @@ impl ResolutionGraph { } } + let fork_markers = if let [resolution] = resolutions { + match resolution.markers { + ResolverMarkers::Universal | ResolverMarkers::SpecificEnvironment(_) => None, + ResolverMarkers::Fork(_) => { + panic!("A single fork must be universal"); + } + } + } else { + Some( + resolutions + .iter() + .map(|resolution| { + resolution + .markers + .fork_markers() + .expect("A non-forking resolution exists in forking mode") + .clone() + }) + .collect(), + ) + }; + Ok(Self { petgraph, requires_python, @@ -146,6 +173,7 @@ impl ResolutionGraph { constraints: constraints.clone(), overrides: overrides.clone(), options, + fork_markers, }) } diff --git a/crates/uv-resolver/src/resolver/resolver_markers.rs b/crates/uv-resolver/src/resolver/resolver_markers.rs index 5b3d213fdd7a..9247418a1eaf 100644 --- a/crates/uv-resolver/src/resolver/resolver_markers.rs +++ b/crates/uv-resolver/src/resolver/resolver_markers.rs @@ -35,6 +35,7 @@ impl ResolverMarkers { ResolverMarkers::SpecificEnvironment(env) => Some(env), } } + /// If solving a fork, return that fork's markers. pub fn fork_markers(&self) -> Option<&MarkerTree> { match self { diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap index c2283fa666cf..0e322b85bb34 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -61,10 +66,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_present.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_present.snap index fe357d9f7c57..c7355b467f12 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_present.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_present.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -68,10 +73,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_required_present.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_required_present.snap index bff7c8d68232..53c8566dc1d4 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_required_present.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_required_present.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -54,10 +59,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap index 75ab8ea9fd0c..80aa4cfdd709 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -133,10 +138,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap index 75ab8ea9fd0c..80aa4cfdd709 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -133,10 +138,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap index 75ab8ea9fd0c..80aa4cfdd709 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -133,10 +138,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap index 37124663f3e1..216498e5d352 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -42,10 +47,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap index 21755df53c4d..002f5e4904e9 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -40,10 +45,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_directory.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_directory.snap index c4e5e5c7cf49..2a59c212ae9b 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_directory.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_directory.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -23,10 +28,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_editable.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_editable.snap index 44c8e42d44d5..f4ff2eec2ccf 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_editable.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_editable.snap @@ -5,6 +5,11 @@ expression: result Ok( Lock { version: 1, + fork_markers: None, + requires_python: None, + resolution_mode: Highest, + prerelease_mode: IfNecessaryOrExplicit, + exclude_newer: None, distributions: [ Distribution { id: DistributionId { @@ -23,10 +28,6 @@ Ok( dev_dependencies: {}, }, ], - requires_python: None, - resolution_mode: Highest, - prerelease_mode: IfNecessaryOrExplicit, - exclude_newer: None, by_id: { DistributionId { name: PackageName( diff --git a/crates/uv/tests/branching_urls.rs b/crates/uv/tests/branching_urls.rs index e56219ea2a20..18cb62dbd2ad 100644 --- a/crates/uv/tests/branching_urls.rs +++ b/crates/uv/tests/branching_urls.rs @@ -208,6 +208,10 @@ fn root_package_splits_transitive_too() -> Result<()> { assert_snapshot!(fs_err::read_to_string(context.temp_dir.join("uv.lock"))?, @r###" version = 1 requires-python = ">=3.11, <3.13" + environment-markers = [ + "python_version < '3.12'", + "python_version >= '3.12'", + ] exclude-newer = "2024-03-25 00:00:00 UTC" [[distribution]] @@ -366,6 +370,10 @@ fn root_package_splits_other_dependencies_too() -> Result<()> { assert_snapshot!(fs_err::read_to_string(context.temp_dir.join("uv.lock"))?, @r###" version = 1 requires-python = ">=3.11, <3.13" + environment-markers = [ + "python_version < '3.12'", + "python_version >= '3.12'", + ] exclude-newer = "2024-03-25 00:00:00 UTC" [[distribution]] @@ -495,6 +503,10 @@ fn branching_between_registry_and_direct_url() -> Result<()> { assert_snapshot!(fs_err::read_to_string(context.temp_dir.join("uv.lock"))?, @r###" version = 1 requires-python = ">=3.11, <3.13" + environment-markers = [ + "python_version < '3.12'", + "python_version >= '3.12'", + ] exclude-newer = "2024-03-25 00:00:00 UTC" [[distribution]] @@ -562,6 +574,10 @@ fn branching_urls_of_different_sources_disjoint() -> Result<()> { assert_snapshot!(fs_err::read_to_string(context.temp_dir.join("uv.lock"))?, @r###" version = 1 requires-python = ">=3.11, <3.13" + environment-markers = [ + "python_version < '3.12'", + "python_version >= '3.12'", + ] exclude-newer = "2024-03-25 00:00:00 UTC" [[distribution]] diff --git a/crates/uv/tests/lock.rs b/crates/uv/tests/lock.rs index b9f0ced4b932..ae298574aaf3 100644 --- a/crates/uv/tests/lock.rs +++ b/crates/uv/tests/lock.rs @@ -1500,6 +1500,10 @@ fn lock_upgrade_log_multi_version() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.12" + environment-markers = [ + "sys_platform == 'win32'", + "sys_platform != 'win32'", + ] exclude-newer = "2024-03-25 00:00:00 UTC" [[distribution]] @@ -2876,6 +2880,16 @@ fn lock_python_version_marker_complement() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "python_full_version <= '3.10' and python_version == '3.10'", + "python_full_version <= '3.10' and python_version < '3.10'", + "python_full_version <= '3.10' and python_version < '3.10' and python_version > '3.10'", + "python_full_version <= '3.10' and python_version > '3.10'", + "python_full_version > '3.10' and python_version == '3.10'", + "python_full_version > '3.10' and python_version < '3.10'", + "python_full_version > '3.10' and python_version < '3.10' and python_version > '3.10'", + "python_full_version > '3.10' and python_version > '3.10'", + ] exclude-newer = "2024-03-25 00:00:00 UTC" [[distribution]] @@ -4029,6 +4043,10 @@ fn lock_same_version_multiple_urls() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.12" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform != 'darwin'", + ] exclude-newer = "2024-03-25 00:00:00 UTC" [[distribution]] diff --git a/crates/uv/tests/lock_scenarios.rs b/crates/uv/tests/lock_scenarios.rs index 01d4d4d017ed..4987f05989bc 100644 --- a/crates/uv/tests/lock_scenarios.rs +++ b/crates/uv/tests/lock_scenarios.rs @@ -82,6 +82,11 @@ fn fork_allows_non_conflicting_non_overlapping_dependencies() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -261,6 +266,11 @@ fn fork_basic() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -519,6 +529,11 @@ fn fork_filter_sibling_dependencies() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -669,6 +684,11 @@ fn fork_incomplete_markers() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "python_version < '3.10'", + "python_version >= '3.11'", + "python_version < '3.11' and python_version >= '3.10'", + ] [[distribution]] name = "package-a" @@ -982,6 +1002,13 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'linux'", + "implementation_name == 'cpython' and sys_platform == 'darwin'", + "implementation_name == 'pypy' and sys_platform == 'darwin'", + "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -1125,6 +1152,13 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'linux'", + "implementation_name == 'cpython' and sys_platform == 'darwin'", + "implementation_name == 'pypy' and sys_platform == 'darwin'", + "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -1257,6 +1291,13 @@ fn fork_marker_inherit_combined() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'linux'", + "implementation_name == 'cpython' and sys_platform == 'darwin'", + "implementation_name == 'pypy' and sys_platform == 'darwin'", + "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -1383,6 +1424,11 @@ fn fork_marker_inherit_isolated() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -1504,6 +1550,11 @@ fn fork_marker_inherit_transitive() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -1632,6 +1683,11 @@ fn fork_marker_inherit() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -1741,6 +1797,11 @@ fn fork_marker_limited_inherit() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -1869,6 +1930,11 @@ fn fork_marker_selection() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" @@ -1996,6 +2062,11 @@ fn fork_marker_track() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'darwin'", + "sys_platform == 'linux'", + "sys_platform != 'darwin' and sys_platform != 'linux'", + ] [[distribution]] name = "package-a" From fd78f7218222d58c42ca3bc3999d50c2b82918b3 Mon Sep 17 00:00:00 2001 From: konstin Date: Wed, 24 Jul 2024 11:13:54 +0200 Subject: [PATCH 2/3] Store the fork markers of diverging versions in uv.lock To set the preferences correctly, we need to know which version belongs to which fork. Instead of tracing that through the graph, we save this information in the lockfile to indicate to the user how and why there are multiple version for a package. --- crates/distribution-types/src/lib.rs | 10 ++ crates/uv-resolver/src/lock.rs | 37 +++++- crates/uv-resolver/src/resolution/graph.rs | 43 ++++++- crates/uv-resolver/src/resolver/mod.rs | 6 + ...r__lock__tests__hash_optional_missing.snap | 1 + ...r__lock__tests__hash_optional_present.snap | 1 + ...r__lock__tests__hash_required_present.snap | 1 + ...missing_dependency_source_unambiguous.snap | 2 + ...dependency_source_version_unambiguous.snap | 2 + ...issing_dependency_version_unambiguous.snap | 2 + ...lock__tests__source_direct_has_subdir.snap | 1 + ..._lock__tests__source_direct_no_subdir.snap | 1 + ...solver__lock__tests__source_directory.snap | 1 + ...esolver__lock__tests__source_editable.snap | 1 + crates/uv/tests/branching_urls.rs | 36 ++++++ crates/uv/tests/lock.rs | 18 +++ crates/uv/tests/lock_scenarios.rs | 112 ++++++++++++++++++ 17 files changed, 268 insertions(+), 7 deletions(-) diff --git a/crates/distribution-types/src/lib.rs b/crates/distribution-types/src/lib.rs index edd5a331383f..33b1a9a98596 100644 --- a/crates/distribution-types/src/lib.rs +++ b/crates/distribution-types/src/lib.rs @@ -87,6 +87,16 @@ pub enum VersionOrUrlRef<'a, T: Pep508Url = VerbatimUrl> { Url(&'a T), } +impl<'a, T: Pep508Url> VersionOrUrlRef<'a, T> { + /// If it is a URL, return its value. + pub fn url(&self) -> Option<&T> { + match self { + VersionOrUrlRef::Version(_) => None, + VersionOrUrlRef::Url(url) => Some(url), + } + } +} + impl Verbatim for VersionOrUrlRef<'_> { fn verbatim(&self) -> Cow<'_, str> { match self { diff --git a/crates/uv-resolver/src/lock.rs b/crates/uv-resolver/src/lock.rs index acc21ce82705..5fe7f265cb9b 100644 --- a/crates/uv-resolver/src/lock.rs +++ b/crates/uv-resolver/src/lock.rs @@ -20,10 +20,10 @@ use cache_key::RepositoryUrl; use distribution_filename::WheelFilename; use distribution_types::{ BuiltDist, DirectUrlBuiltDist, DirectUrlSourceDist, DirectorySourceDist, Dist, - DistributionMetadata, FileLocation, GitSourceDist, HashComparison, IndexUrl, PathBuiltDist, - PathSourceDist, PrioritizedDist, RegistryBuiltDist, RegistryBuiltWheel, RegistrySourceDist, - RemoteSource, Resolution, ResolvedDist, SourceDistCompatibility, ToUrlError, UrlString, - VersionId, WheelCompatibility, + DistributionMetadata, FileLocation, GitSourceDist, HashComparison, IndexUrl, Name, + PathBuiltDist, PathSourceDist, PrioritizedDist, RegistryBuiltDist, RegistryBuiltWheel, + RegistrySourceDist, RemoteSource, Resolution, ResolvedDist, SourceDistCompatibility, + ToUrlError, UrlString, VersionId, WheelCompatibility, }; use pep440_rs::{Version, VersionSpecifier}; use pep508_rs::{ @@ -92,7 +92,12 @@ impl Lock { continue; }; if dist.is_base() { - let mut locked_dist = Distribution::from_annotated_dist(dist)?; + let fork_markers = graph + .fork_markers(dist.name(), &dist.version, dist.dist.version_or_url().url()) + .cloned(); + let mut locked_dist = Distribution::from_annotated_dist(dist, fork_markers)?; + + // Add all dependencies for edge in graph.petgraph.edges(node_index) { let ResolutionGraphNode::Dist(dependency_dist) = &graph.petgraph[edge.target()] else { @@ -661,13 +666,22 @@ pub struct Distribution { pub(crate) id: DistributionId, sdist: Option, wheels: Vec, + /// If there are multiple distributions for the same package name, we add the markers of the + /// fork(s) that contained this distribution, so we can set the correct preferences in the next + /// resolution. + /// + /// Named `environment-markers` in `uv.lock`. + fork_markers: Option>, dependencies: Vec, optional_dependencies: BTreeMap>, dev_dependencies: BTreeMap>, } impl Distribution { - fn from_annotated_dist(annotated_dist: &AnnotatedDist) -> Result { + fn from_annotated_dist( + annotated_dist: &AnnotatedDist, + fork_markers: Option>, + ) -> Result { let id = DistributionId::from_annotated_dist(annotated_dist); let sdist = SourceDist::from_annotated_dist(&id, annotated_dist)?; let wheels = Wheel::from_annotated_dist(annotated_dist)?; @@ -675,6 +689,7 @@ impl Distribution { id, sdist, wheels, + fork_markers, dependencies: vec![], optional_dependencies: BTreeMap::default(), dev_dependencies: BTreeMap::default(), @@ -1024,6 +1039,12 @@ impl Distribution { self.id.to_toml(None, &mut table); + if let Some(ref fork_markers) = self.fork_markers { + let wheels = + each_element_on_its_line_array(fork_markers.iter().map(ToString::to_string)); + table.insert("environment-markers", value(wheels)); + } + if !self.dependencies.is_empty() { let deps = each_element_on_its_line_array( self.dependencies @@ -1164,6 +1185,8 @@ struct DistributionWire { sdist: Option, #[serde(default)] wheels: Vec, + #[serde(default, rename = "environment-markers")] + fork_markers: BTreeSet, #[serde(default)] dependencies: Vec, #[serde(default)] @@ -1186,6 +1209,7 @@ impl DistributionWire { id: self.id, sdist: self.sdist, wheels: self.wheels, + fork_markers: (!self.fork_markers.is_empty()).then_some(self.fork_markers), dependencies: unwire_deps(self.dependencies)?, optional_dependencies: self .optional_dependencies @@ -1210,6 +1234,7 @@ impl From for DistributionWire { id: dist.id, sdist: dist.sdist, wheels: dist.wheels, + fork_markers: dist.fork_markers.unwrap_or_default(), dependencies: wire_deps(dist.dependencies), optional_dependencies: dist .optional_dependencies diff --git a/crates/uv-resolver/src/resolution/graph.rs b/crates/uv-resolver/src/resolution/graph.rs index 12c5edc9feb4..550f99823cb6 100644 --- a/crates/uv-resolver/src/resolution/graph.rs +++ b/crates/uv-resolver/src/resolution/graph.rs @@ -12,7 +12,7 @@ use distribution_types::{ VersionOrUrlRef, }; use pep440_rs::{Version, VersionSpecifier}; -use pep508_rs::{MarkerEnvironment, MarkerTree}; +use pep508_rs::{MarkerEnvironment, MarkerTree, VerbatimUrl}; use pypi_types::{HashDigest, ParsedUrlError, Requirement, VerbatimParsedUrl, Yanked}; use uv_configuration::{Constraints, Overrides}; use uv_distribution::Metadata; @@ -30,6 +30,9 @@ use crate::{ ResolverMarkers, VersionsResponse, }; +pub(crate) type MarkersForDistribution = + FxHashMap<(Version, Option), BTreeSet>; + /// A complete resolution graph in which every node represents a pinned package and every edge /// represents a dependency between two pinned packages. #[derive(Debug)] @@ -51,6 +54,9 @@ pub struct ResolutionGraph { pub(crate) overrides: Overrides, /// The options that were used to build the graph. pub(crate) options: Options, + /// If there are multiple options for a package, track which fork they belong to so we + /// can write that to the lockfile and later get the correct preference per fork back. + pub(crate) package_markers: FxHashMap, } #[derive(Debug)] @@ -91,10 +97,28 @@ impl ResolutionGraph { // Add the root node. let root_index = petgraph.add_node(ResolutionGraphNode::Root); + let mut package_markers: FxHashMap = + FxHashMap::default(); + let mut seen = FxHashSet::default(); for resolution in resolutions { // Add every package to the graph. for (package, version) in &resolution.nodes { + if package.is_base() { + // For packages with diverging versions, store which version comes from which + // fork. + if let Some(markers) = resolution.markers.fork_markers() { + let entry = package_markers + .entry(package.name.clone()) + .or_default() + .entry((version.clone(), package.url.clone().map(|url| url.verbatim))) + .or_default(); + if !entry.contains(markers) { + entry.insert(markers.clone()); + } + } + } + if !seen.insert((package, version)) { // Insert each node only once. continue; @@ -168,6 +192,7 @@ impl ResolutionGraph { Ok(Self { petgraph, requires_python, + package_markers, diagnostics, requirements: requirements.to_vec(), constraints: constraints.clone(), @@ -577,6 +602,22 @@ impl ResolutionGraph { } Ok(MarkerTree::And(conjuncts)) } + + /// If there are multiple distributions for the same package name, return the markers of the + /// fork(s) that contained this distribution, otherwise return `None`. + pub fn fork_markers( + &self, + package_name: &PackageName, + version: &Version, + url: Option<&VerbatimUrl>, + ) -> Option<&BTreeSet> { + let package_markers = &self.package_markers.get(package_name)?; + if package_markers.len() == 1 { + None + } else { + Some(&package_markers[&(version.clone(), url.cloned())]) + } + } } impl From for distribution_types::Resolution { diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 333f6f91c5aa..266196a5816d 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -2479,6 +2479,12 @@ impl Resolution { } } +impl ResolutionPackage { + pub(crate) fn is_base(&self) -> bool { + self.extra.is_none() && self.dev.is_none() + } +} + /// Fetch the metadata for an item #[derive(Debug)] #[allow(clippy::large_enum_variant)] diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap index 0e322b85bb34..636ef555f178 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_missing.snap @@ -61,6 +61,7 @@ Ok( }, }, ], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_present.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_present.snap index c7355b467f12..4cd107c7c1cb 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_present.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_optional_present.snap @@ -68,6 +68,7 @@ Ok( }, }, ], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_required_present.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_required_present.snap index 53c8566dc1d4..95f2a9993cbc 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_required_present.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__hash_required_present.snap @@ -54,6 +54,7 @@ Ok( }, }, ], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap index 80aa4cfdd709..6c9e068fc452 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_unambiguous.snap @@ -56,6 +56,7 @@ Ok( }, ), wheels: [], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, @@ -105,6 +106,7 @@ Ok( }, ), wheels: [], + fork_markers: None, dependencies: [ Dependency { distribution_id: DistributionId { diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap index 80aa4cfdd709..6c9e068fc452 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_source_version_unambiguous.snap @@ -56,6 +56,7 @@ Ok( }, ), wheels: [], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, @@ -105,6 +106,7 @@ Ok( }, ), wheels: [], + fork_markers: None, dependencies: [ Dependency { distribution_id: DistributionId { diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap index 80aa4cfdd709..6c9e068fc452 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__missing_dependency_version_unambiguous.snap @@ -56,6 +56,7 @@ Ok( }, ), wheels: [], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, @@ -105,6 +106,7 @@ Ok( }, ), wheels: [], + fork_markers: None, dependencies: [ Dependency { distribution_id: DistributionId { diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap index 216498e5d352..51430bee4d81 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_has_subdir.snap @@ -42,6 +42,7 @@ Ok( }, sdist: None, wheels: [], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap index 002f5e4904e9..7844eef5ce3e 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_direct_no_subdir.snap @@ -40,6 +40,7 @@ Ok( }, sdist: None, wheels: [], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_directory.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_directory.snap index 2a59c212ae9b..2d196221e16b 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_directory.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_directory.snap @@ -23,6 +23,7 @@ Ok( }, sdist: None, wheels: [], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, diff --git a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_editable.snap b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_editable.snap index f4ff2eec2ccf..26fe7527ad76 100644 --- a/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_editable.snap +++ b/crates/uv-resolver/src/snapshots/uv_resolver__lock__tests__source_editable.snap @@ -23,6 +23,7 @@ Ok( }, sdist: None, wheels: [], + fork_markers: None, dependencies: [], optional_dependencies: {}, dev_dependencies: {}, diff --git a/crates/uv/tests/branching_urls.rs b/crates/uv/tests/branching_urls.rs index 18cb62dbd2ad..d321f6ea0c23 100644 --- a/crates/uv/tests/branching_urls.rs +++ b/crates/uv/tests/branching_urls.rs @@ -228,6 +228,9 @@ fn root_package_splits_transitive_too() -> Result<()> { name = "anyio" version = "4.2.0" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "python_version < '3.12'", + ] dependencies = [ { name = "idna" }, { name = "sniffio" }, @@ -241,6 +244,9 @@ fn root_package_splits_transitive_too() -> Result<()> { name = "anyio" version = "4.3.0" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "python_version >= '3.12'", + ] dependencies = [ { name = "idna" }, { name = "sniffio" }, @@ -288,6 +294,9 @@ fn root_package_splits_transitive_too() -> Result<()> { name = "iniconfig" version = "1.1.1" source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" } + environment-markers = [ + "python_version < '3.12'", + ] wheels = [ { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3" }, ] @@ -296,6 +305,9 @@ fn root_package_splits_transitive_too() -> Result<()> { name = "iniconfig" version = "2.0.0" source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" } + environment-markers = [ + "python_version >= '3.12'", + ] wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" }, ] @@ -391,6 +403,9 @@ fn root_package_splits_other_dependencies_too() -> Result<()> { name = "anyio" version = "4.2.0" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "python_version < '3.12'", + ] dependencies = [ { name = "idna" }, { name = "sniffio" }, @@ -404,6 +419,9 @@ fn root_package_splits_other_dependencies_too() -> Result<()> { name = "anyio" version = "4.3.0" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "python_version >= '3.12'", + ] dependencies = [ { name = "idna" }, { name = "sniffio" }, @@ -442,6 +460,9 @@ fn root_package_splits_other_dependencies_too() -> Result<()> { name = "iniconfig" version = "1.1.1" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "python_version < '3.12'", + ] sdist = { url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32", size = 8104 } wheels = [ { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", size = 4990 }, @@ -451,6 +472,9 @@ fn root_package_splits_other_dependencies_too() -> Result<()> { name = "iniconfig" version = "2.0.0" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "python_version >= '3.12'", + ] sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, @@ -522,6 +546,9 @@ fn branching_between_registry_and_direct_url() -> Result<()> { name = "iniconfig" version = "1.1.1" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "python_version < '3.12'", + ] sdist = { url = "https://files.pythonhosted.org/packages/23/a2/97899f6bd0e873fed3a7e67ae8d3a08b21799430fb4da15cfedf10d6e2c2/iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32", size = 8104 } wheels = [ { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", size = 4990 }, @@ -531,6 +558,9 @@ fn branching_between_registry_and_direct_url() -> Result<()> { name = "iniconfig" version = "2.0.0" source = { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl" } + environment-markers = [ + "python_version >= '3.12'", + ] wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" }, ] @@ -593,6 +623,9 @@ fn branching_urls_of_different_sources_disjoint() -> Result<()> { name = "iniconfig" version = "1.1.1" source = { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl" } + environment-markers = [ + "python_version < '3.12'", + ] wheels = [ { url = "https://files.pythonhosted.org/packages/9b/dd/b3c12c6d707058fa947864b67f0c4e0c39ef8610988d7baea9578f3c48f3/iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3" }, ] @@ -601,6 +634,9 @@ fn branching_urls_of_different_sources_disjoint() -> Result<()> { name = "iniconfig" version = "2.0.0" source = { git = "https://github.com/pytest-dev/iniconfig?rev=93f5930e668c0d1ddf4597e38dd0dea4e2665e7a#93f5930e668c0d1ddf4597e38dd0dea4e2665e7a" } + environment-markers = [ + "python_version >= '3.12'", + ] "###); Ok(()) diff --git a/crates/uv/tests/lock.rs b/crates/uv/tests/lock.rs index ae298574aaf3..0bd5d2b0cf33 100644 --- a/crates/uv/tests/lock.rs +++ b/crates/uv/tests/lock.rs @@ -1510,12 +1510,18 @@ fn lock_upgrade_log_multi_version() -> Result<()> { name = "markupsafe" version = "1.1.1" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "sys_platform != 'win32'", + ] sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", size = 19151 } [[distribution]] name = "markupsafe" version = "2.0.0" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "sys_platform == 'win32'", + ] sdist = { url = "https://files.pythonhosted.org/packages/67/6a/5b3ed5c122e20c33d2562df06faf895a6b91b0a6b96a4626440ffe1d5c8e/MarkupSafe-2.0.0.tar.gz", hash = "sha256:4fae0677f712ee090721d8b17f412f1cbceefbf0dc180fe91bab3232f38b4527", size = 18466 } [[distribution]] @@ -4053,6 +4059,9 @@ fn lock_same_version_multiple_urls() -> Result<()> { name = "anyio" version = "3.0.0" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "sys_platform != 'darwin'", + ] dependencies = [ { name = "idna" }, { name = "sniffio" }, @@ -4066,6 +4075,9 @@ fn lock_same_version_multiple_urls() -> Result<()> { name = "anyio" version = "3.7.0" source = { registry = "https://pypi.org/simple" } + environment-markers = [ + "sys_platform == 'darwin'", + ] dependencies = [ { name = "idna" }, { name = "sniffio" }, @@ -4079,6 +4091,9 @@ fn lock_same_version_multiple_urls() -> Result<()> { name = "dependency" version = "0.0.1" source = { directory = "[TEMP_DIR]/v1" } + environment-markers = [ + "sys_platform == 'darwin'", + ] dependencies = [ { name = "anyio", version = "3.7.0", source = { registry = "https://pypi.org/simple" } }, ] @@ -4087,6 +4102,9 @@ fn lock_same_version_multiple_urls() -> Result<()> { name = "dependency" version = "0.0.1" source = { directory = "[TEMP_DIR]/v2" } + environment-markers = [ + "sys_platform != 'darwin'", + ] dependencies = [ { name = "anyio", version = "3.0.0", source = { registry = "https://pypi.org/simple" } }, ] diff --git a/crates/uv/tests/lock_scenarios.rs b/crates/uv/tests/lock_scenarios.rs index 4987f05989bc..35f458228f71 100644 --- a/crates/uv/tests/lock_scenarios.rs +++ b/crates/uv/tests/lock_scenarios.rs @@ -276,6 +276,9 @@ fn fork_basic() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_basic_a-1.0.0.tar.gz#sha256=9bd6d9d74d8928854f79ea3ed4cd0d8a4906eeaa40f5f3d63460a1c2d5f6d773", hash = "sha256:9bd6d9d74d8928854f79ea3ed4cd0d8a4906eeaa40f5f3d63460a1c2d5f6d773" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_basic_a-1.0.0-py3-none-any.whl#sha256=9d3af617bb44ae1c8daf19f6d4d118ee8aac7eaf0cc5368d0f405137411291a1", hash = "sha256:9d3af617bb44ae1c8daf19f6d4d118ee8aac7eaf0cc5368d0f405137411291a1" }, @@ -285,6 +288,9 @@ fn fork_basic() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_basic_a-2.0.0.tar.gz#sha256=c0ce6dfb6d712eb42a4bbe9402a1f823627b9d3773f31d259c49478fc7d8d082", hash = "sha256:c0ce6dfb6d712eb42a4bbe9402a1f823627b9d3773f31d259c49478fc7d8d082" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_basic_a-2.0.0-py3-none-any.whl#sha256=3876778dc6e5178b0e456b0d988cb8c2542cb943a45497aff3e198cbec3dfcc9", hash = "sha256:3876778dc6e5178b0e456b0d988cb8c2542cb943a45497aff3e198cbec3dfcc9" }, @@ -539,6 +545,9 @@ fn fork_filter_sibling_dependencies() -> Result<()> { name = "package-a" version = "4.3.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_a-4.3.0.tar.gz#sha256=5389f0927f61393ba8bd940622329299d769e79b725233604a6bdac0fd088c49", hash = "sha256:5389f0927f61393ba8bd940622329299d769e79b725233604a6bdac0fd088c49" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_a-4.3.0-py3-none-any.whl#sha256=932c128393cd499617d1a5b457b11887d51039284b18e06add4c384ab661148c", hash = "sha256:932c128393cd499617d1a5b457b11887d51039284b18e06add4c384ab661148c" }, @@ -548,6 +557,9 @@ fn fork_filter_sibling_dependencies() -> Result<()> { name = "package-a" version = "4.4.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_a-4.4.0.tar.gz#sha256=7dbb8575aec8f87063954917b6ee628191cd53ca233ec810f6d926b4954e578b", hash = "sha256:7dbb8575aec8f87063954917b6ee628191cd53ca233ec810f6d926b4954e578b" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_a-4.4.0-py3-none-any.whl#sha256=26989734e8fa720896dbbf900adc64551bf3f0026fb62c3c22b47dc23edd4a4c", hash = "sha256:26989734e8fa720896dbbf900adc64551bf3f0026fb62c3c22b47dc23edd4a4c" }, @@ -581,6 +593,9 @@ fn fork_filter_sibling_dependencies() -> Result<()> { name = "package-d" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_d-1.0.0.tar.gz#sha256=cc1af60e53faf957fd0542349441ea79a909cd5feb30fb8933c39dc33404e4b2", hash = "sha256:cc1af60e53faf957fd0542349441ea79a909cd5feb30fb8933c39dc33404e4b2" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_d-1.0.0-py3-none-any.whl#sha256=de669ada03e9f8625e3ac4af637c88de04066a72675c16c3d1757e0e9d5db7a8", hash = "sha256:de669ada03e9f8625e3ac4af637c88de04066a72675c16c3d1757e0e9d5db7a8" }, @@ -590,6 +605,9 @@ fn fork_filter_sibling_dependencies() -> Result<()> { name = "package-d" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_d-2.0.0.tar.gz#sha256=68e380efdea5206363f5397e4cd560a64f5f4927396dc0b6f6f36dd3f026281f", hash = "sha256:68e380efdea5206363f5397e4cd560a64f5f4927396dc0b6f6f36dd3f026281f" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_filter_sibling_dependencies_d-2.0.0-py3-none-any.whl#sha256=87c133dcc987137d62c011a41af31e8372ca971393d93f808dffc32e136363c7", hash = "sha256:87c133dcc987137d62c011a41af31e8372ca971393d93f808dffc32e136363c7" }, @@ -694,6 +712,9 @@ fn fork_incomplete_markers() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "python_version < '3.10'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-1.0.0.tar.gz#sha256=dd56de2e560b3f95c529c44cbdae55d9b1ada826ddd3e19d3ea45438224ad603", hash = "sha256:dd56de2e560b3f95c529c44cbdae55d9b1ada826ddd3e19d3ea45438224ad603" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-1.0.0-py3-none-any.whl#sha256=779bb805058fc59858e8b9260cd1a40f13f1640631fdea89d9d243691a4f39ca", hash = "sha256:779bb805058fc59858e8b9260cd1a40f13f1640631fdea89d9d243691a4f39ca" }, @@ -703,6 +724,9 @@ fn fork_incomplete_markers() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "python_version >= '3.11'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-2.0.0.tar.gz#sha256=580f1454a172036c89f5cfbefe52f175b011806a61f243eb476526bcc186e0be", hash = "sha256:580f1454a172036c89f5cfbefe52f175b011806a61f243eb476526bcc186e0be" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_incomplete_markers_a-2.0.0-py3-none-any.whl#sha256=58a4b7dcf929aabf1ed434d9ff8d715929dc3dec02b92cf2b364d5a2206f1f6a", hash = "sha256:58a4b7dcf929aabf1ed434d9ff8d715929dc3dec02b92cf2b364d5a2206f1f6a" }, @@ -1014,6 +1038,11 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'cpython' and sys_platform == 'darwin'", + "implementation_name == 'pypy' and sys_platform == 'darwin'", + "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", + ] dependencies = [ { name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'pypy'" }, { name = "package-b", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'cpython'" }, @@ -1027,6 +1056,9 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_a-2.0.0.tar.gz#sha256=0dcb58c8276afbe439e1c94708fb71954fb8869cc65c230ce8f462c911992ceb", hash = "sha256:0dcb58c8276afbe439e1c94708fb71954fb8869cc65c230ce8f462c911992ceb" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_a-2.0.0-py3-none-any.whl#sha256=61b7d273468584342de4c0185beed5b128797ce95ec9ec4a670fe30f73351cf7", hash = "sha256:61b7d273468584342de4c0185beed5b128797ce95ec9ec4a670fe30f73351cf7" }, @@ -1036,6 +1068,9 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { name = "package-b" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'pypy' and sys_platform == 'darwin'", + ] dependencies = [ { name = "package-c", marker = "implementation_name == 'pypy' or sys_platform == 'linux'" }, ] @@ -1048,6 +1083,9 @@ fn fork_marker_inherit_combined_allowed() -> Result<()> { name = "package-b" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'cpython' and sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_b-2.0.0.tar.gz#sha256=4533845ba671575a25ceb32f10f0bc6836949bef37b7da6e7dd37d9be389871c", hash = "sha256:4533845ba671575a25ceb32f10f0bc6836949bef37b7da6e7dd37d9be389871c" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_allowed_b-2.0.0-py3-none-any.whl#sha256=736d1b59cb46a0b889614bc7557c293de245fe8954e3200e786500ae8e42504b", hash = "sha256:736d1b59cb46a0b889614bc7557c293de245fe8954e3200e786500ae8e42504b" }, @@ -1164,6 +1202,11 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'cpython' and sys_platform == 'darwin'", + "implementation_name == 'pypy' and sys_platform == 'darwin'", + "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", + ] dependencies = [ { name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'pypy'" }, { name = "package-b", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'cpython'" }, @@ -1177,6 +1220,9 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_a-2.0.0.tar.gz#sha256=9d48383b0699f575af15871f6f7a928b835cd5ad4e13f91a675ee5aba722dabc", hash = "sha256:9d48383b0699f575af15871f6f7a928b835cd5ad4e13f91a675ee5aba722dabc" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_a-2.0.0-py3-none-any.whl#sha256=099db8d3af6c9dfc10589ab0f1e2e6a74276a167afb39322ddaf657791247456", hash = "sha256:099db8d3af6c9dfc10589ab0f1e2e6a74276a167afb39322ddaf657791247456" }, @@ -1186,6 +1232,9 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { name = "package-b" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'pypy' and sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_b-1.0.0.tar.gz#sha256=d44b87bd8d39240bca55eaae84a245e74197ed0b7897c27af9f168c713cc63bd", hash = "sha256:d44b87bd8d39240bca55eaae84a245e74197ed0b7897c27af9f168c713cc63bd" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_b-1.0.0-py3-none-any.whl#sha256=999b3d0029ea0131272257e2b04c0e673defa6c25be6efc411e04936bce72ef6", hash = "sha256:999b3d0029ea0131272257e2b04c0e673defa6c25be6efc411e04936bce72ef6" }, @@ -1195,6 +1244,9 @@ fn fork_marker_inherit_combined_disallowed() -> Result<()> { name = "package-b" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'cpython' and sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_b-2.0.0.tar.gz#sha256=75a48bf2d44a0a0be6ca33820f5076665765be7b43dabf5f94f7fd5247071097", hash = "sha256:75a48bf2d44a0a0be6ca33820f5076665765be7b43dabf5f94f7fd5247071097" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_disallowed_b-2.0.0-py3-none-any.whl#sha256=2c6aedd257d0ed21bb96f6e0baba8314c001d4078d09413cda147fb6badb39a2", hash = "sha256:2c6aedd257d0ed21bb96f6e0baba8314c001d4078d09413cda147fb6badb39a2" }, @@ -1303,6 +1355,11 @@ fn fork_marker_inherit_combined() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'cpython' and sys_platform == 'darwin'", + "implementation_name == 'pypy' and sys_platform == 'darwin'", + "implementation_name != 'cpython' and implementation_name != 'pypy' and sys_platform == 'darwin'", + ] dependencies = [ { name = "package-b", version = "1.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'pypy'" }, { name = "package-b", version = "2.0.0", source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" }, marker = "implementation_name == 'cpython'" }, @@ -1316,6 +1373,9 @@ fn fork_marker_inherit_combined() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_a-2.0.0.tar.gz#sha256=47958d1659220ee7722b0f26e8c1fe41217a2816881ffa929f0ba794a87ceebf", hash = "sha256:47958d1659220ee7722b0f26e8c1fe41217a2816881ffa929f0ba794a87ceebf" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_a-2.0.0-py3-none-any.whl#sha256=67e142d749674a27c872db714d50fda083010789da51291e3c30b4daf0e96b3b", hash = "sha256:67e142d749674a27c872db714d50fda083010789da51291e3c30b4daf0e96b3b" }, @@ -1325,6 +1385,9 @@ fn fork_marker_inherit_combined() -> Result<()> { name = "package-b" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'pypy' and sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_b-1.0.0.tar.gz#sha256=6992d194cb5a0f0eed9ed6617d3212af4e3ff09274bf7622c8a1008b072128da", hash = "sha256:6992d194cb5a0f0eed9ed6617d3212af4e3ff09274bf7622c8a1008b072128da" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_b-1.0.0-py3-none-any.whl#sha256=d9b50d8a0968d65af338e27d6b2a58eea59c514e47b820752a2c068b5a8333a7", hash = "sha256:d9b50d8a0968d65af338e27d6b2a58eea59c514e47b820752a2c068b5a8333a7" }, @@ -1334,6 +1397,9 @@ fn fork_marker_inherit_combined() -> Result<()> { name = "package-b" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "implementation_name == 'cpython' and sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_b-2.0.0.tar.gz#sha256=e340061505d621a340d10ec1dbaf02dfce0c66358ee8190f61f78018f9999989", hash = "sha256:e340061505d621a340d10ec1dbaf02dfce0c66358ee8190f61f78018f9999989" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_combined_b-2.0.0-py3-none-any.whl#sha256=ff364fd590d05651579d8bea621b069934470106b9a82ab960fb93dfd88ea038", hash = "sha256:ff364fd590d05651579d8bea621b069934470106b9a82ab960fb93dfd88ea038" }, @@ -1434,6 +1500,9 @@ fn fork_marker_inherit_isolated() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_isolated_a-1.0.0.tar.gz#sha256=724ffc24debfa2bc6b5c2457df777c523638ec3586cc953f8509dad581aa6887", hash = "sha256:724ffc24debfa2bc6b5c2457df777c523638ec3586cc953f8509dad581aa6887" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_isolated_a-1.0.0-py3-none-any.whl#sha256=6823b88bf6debf2ec6195d82943c2812235a642438f007c0a3c95d745a5b95ba", hash = "sha256:6823b88bf6debf2ec6195d82943c2812235a642438f007c0a3c95d745a5b95ba" }, @@ -1443,6 +1512,9 @@ fn fork_marker_inherit_isolated() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] dependencies = [ { name = "package-b", marker = "sys_platform == 'linux'" }, ] @@ -1560,6 +1632,9 @@ fn fork_marker_inherit_transitive() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] dependencies = [ { name = "package-b" }, ] @@ -1572,6 +1647,9 @@ fn fork_marker_inherit_transitive() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_a-2.0.0.tar.gz#sha256=4437ac14c340fec0b451cbc9486f5b8e106568634264ecad339a8de565a93be6", hash = "sha256:4437ac14c340fec0b451cbc9486f5b8e106568634264ecad339a8de565a93be6" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_transitive_a-2.0.0-py3-none-any.whl#sha256=420c4c6b02d22c33f7f8ae9f290acc5b4c372fc2e49c881d259237a31c76dc0b", hash = "sha256:420c4c6b02d22c33f7f8ae9f290acc5b4c372fc2e49c881d259237a31c76dc0b" }, @@ -1693,6 +1771,9 @@ fn fork_marker_inherit() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_a-1.0.0.tar.gz#sha256=177511ec69a2f04de39867d43f167a33194ae983e8f86a1cc9b51f59fc379d4b", hash = "sha256:177511ec69a2f04de39867d43f167a33194ae983e8f86a1cc9b51f59fc379d4b" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_a-1.0.0-py3-none-any.whl#sha256=16447932477c5feaa874b4e7510023c6f732578cec07158bc0e872af887a77d6", hash = "sha256:16447932477c5feaa874b4e7510023c6f732578cec07158bc0e872af887a77d6" }, @@ -1702,6 +1783,9 @@ fn fork_marker_inherit() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_a-2.0.0.tar.gz#sha256=43e24ce6fcbfbbff1db5eb20b583c20c2aa0888138bfafeab205c4ccc6e7e0a4", hash = "sha256:43e24ce6fcbfbbff1db5eb20b583c20c2aa0888138bfafeab205c4ccc6e7e0a4" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_inherit_a-2.0.0-py3-none-any.whl#sha256=d650b6acf8f68d85e210ceb3e7802fbe84aad2b918b06a72dee534fe5474852b", hash = "sha256:d650b6acf8f68d85e210ceb3e7802fbe84aad2b918b06a72dee534fe5474852b" }, @@ -1807,6 +1891,9 @@ fn fork_marker_limited_inherit() -> Result<()> { name = "package-a" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_a-1.0.0.tar.gz#sha256=ab1fde8d0acb9a2fe99b7a005939962b1c26c6d876e4a55e81fb9d1a1e5e9f76", hash = "sha256:ab1fde8d0acb9a2fe99b7a005939962b1c26c6d876e4a55e81fb9d1a1e5e9f76" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_a-1.0.0-py3-none-any.whl#sha256=0dcb9659eeb891701535005a2afd7c579f566d3908e96137db74129924ae6a7e", hash = "sha256:0dcb9659eeb891701535005a2afd7c579f566d3908e96137db74129924ae6a7e" }, @@ -1816,6 +1903,9 @@ fn fork_marker_limited_inherit() -> Result<()> { name = "package-a" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_a-2.0.0.tar.gz#sha256=009fdb8872cf52324c1bcdebef31feaba3c262fd76d150a753152aeee3d55b10", hash = "sha256:009fdb8872cf52324c1bcdebef31feaba3c262fd76d150a753152aeee3d55b10" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_limited_inherit_a-2.0.0-py3-none-any.whl#sha256=10957fddbd5611e0db154744a01d588c7105e26fd5f6a8150956ca9542d844c5", hash = "sha256:10957fddbd5611e0db154744a01d588c7105e26fd5f6a8150956ca9542d844c5" }, @@ -1949,6 +2039,9 @@ fn fork_marker_selection() -> Result<()> { name = "package-b" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_b-1.0.0.tar.gz#sha256=6f5ea28cadb8b5dfa15d32c9e38818f8f7150fc4f9a58e49aec4e10b23342be4", hash = "sha256:6f5ea28cadb8b5dfa15d32c9e38818f8f7150fc4f9a58e49aec4e10b23342be4" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_b-1.0.0-py3-none-any.whl#sha256=5eb8c7fc25dfe94c8a3b71bc09eadb8cd4c7e55b974cee851b848c3856d6a4f9", hash = "sha256:5eb8c7fc25dfe94c8a3b71bc09eadb8cd4c7e55b974cee851b848c3856d6a4f9" }, @@ -1958,6 +2051,9 @@ fn fork_marker_selection() -> Result<()> { name = "package-b" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_b-2.0.0.tar.gz#sha256=d32033ecdf37d605e4b3b3e88df6562bb7ca01c6ed3fb9a55ec078eccc1df9d1", hash = "sha256:d32033ecdf37d605e4b3b3e88df6562bb7ca01c6ed3fb9a55ec078eccc1df9d1" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_selection_b-2.0.0-py3-none-any.whl#sha256=163fbcd238a66243064d41bd383657a63e45155f63bf92668c23af5245307380", hash = "sha256:163fbcd238a66243064d41bd383657a63e45155f63bf92668c23af5245307380" }, @@ -2084,6 +2180,9 @@ fn fork_marker_track() -> Result<()> { name = "package-b" version = "2.7" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'darwin'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_b-2.7.tar.gz#sha256=855bf45837a4ba669a5850b14b0253cb138925fdd2b06a2f15c6582b8fabb8a0", hash = "sha256:855bf45837a4ba669a5850b14b0253cb138925fdd2b06a2f15c6582b8fabb8a0" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_b-2.7-py3-none-any.whl#sha256=544eb2b567d2293c47da724af91fec59c2d3e06675617d29068864ec3a4e390f", hash = "sha256:544eb2b567d2293c47da724af91fec59c2d3e06675617d29068864ec3a4e390f" }, @@ -2093,6 +2192,9 @@ fn fork_marker_track() -> Result<()> { name = "package-b" version = "2.8" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_b-2.8.tar.gz#sha256=2e14b0ff1fb7f5cf491bd31d876218adee1d6a208ff197dc30363cdf25262e80", hash = "sha256:2e14b0ff1fb7f5cf491bd31d876218adee1d6a208ff197dc30363cdf25262e80" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/fork_marker_track_b-2.8-py3-none-any.whl#sha256=5aba691ce804ee39b2464c7757f8680786a1468e152ee845ff841c37f8112e21", hash = "sha256:5aba691ce804ee39b2464c7757f8680786a1468e152ee845ff841c37f8112e21" }, @@ -2588,6 +2690,10 @@ fn preferences_dependent_forking() -> Result<()> { lock, @r###" version = 1 requires-python = ">=3.8" + environment-markers = [ + "sys_platform == 'linux'", + "sys_platform != 'linux'", + ] [[distribution]] name = "package-bar" @@ -2615,6 +2721,9 @@ fn preferences_dependent_forking() -> Result<()> { name = "package-foo" version = "1.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform == 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_foo-1.0.0.tar.gz#sha256=abf1c0ac825ee5961e683067634916f98c6651a6d4473ff87d8b57c17af8fed2", hash = "sha256:abf1c0ac825ee5961e683067634916f98c6651a6d4473ff87d8b57c17af8fed2" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_foo-1.0.0-py3-none-any.whl#sha256=85348e8df4892b9f297560c16abcf231828f538dc07339ed121197a00a0626a5", hash = "sha256:85348e8df4892b9f297560c16abcf231828f538dc07339ed121197a00a0626a5" }, @@ -2624,6 +2733,9 @@ fn preferences_dependent_forking() -> Result<()> { name = "package-foo" version = "2.0.0" source = { registry = "https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/" } + environment-markers = [ + "sys_platform != 'linux'", + ] sdist = { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_foo-2.0.0.tar.gz#sha256=ad54d14a4fd931b8ccb6412edef71fe223c36362d0ccfe3fa251c17d4f07e4a9", hash = "sha256:ad54d14a4fd931b8ccb6412edef71fe223c36362d0ccfe3fa251c17d4f07e4a9" } wheels = [ { url = "https://astral-sh.github.io/packse/PACKSE_VERSION/files/preferences_dependent_forking_foo-2.0.0-py3-none-any.whl#sha256=bae278cf259c0e031e52b6cbb537d945e0e606d045e980b90d406d0f1e06aae9", hash = "sha256:bae278cf259c0e031e52b6cbb537d945e0e606d045e980b90d406d0f1e06aae9" }, From 4b1ca1f11efff8064be61ce05be6cc15ee2c949e Mon Sep 17 00:00:00 2001 From: konstin Date: Wed, 24 Jul 2024 12:52:14 +0200 Subject: [PATCH 3/3] Add fork preferences to `ResolverMarkers::Universal` This gives the resolver access to the fork preferences from the lockfile. --- crates/uv-resolver/src/error.rs | 2 +- crates/uv-resolver/src/fork_urls.rs | 3 ++- crates/uv-resolver/src/lock.rs | 6 +++++ crates/uv-resolver/src/resolution/display.rs | 4 ++- crates/uv-resolver/src/resolution/graph.rs | 2 +- crates/uv-resolver/src/resolver/fork_map.rs | 2 +- crates/uv-resolver/src/resolver/mod.rs | 6 ++--- .../src/resolver/resolver_markers.rs | 27 ++++++++++++++----- crates/uv/src/commands/pip/compile.rs | 2 +- crates/uv/src/commands/project/lock.rs | 12 +++++++-- 10 files changed, 49 insertions(+), 17 deletions(-) diff --git a/crates/uv-resolver/src/error.rs b/crates/uv-resolver/src/error.rs index b96b32633334..4826ab535e8c 100644 --- a/crates/uv-resolver/src/error.rs +++ b/crates/uv-resolver/src/error.rs @@ -131,7 +131,7 @@ pub struct NoSolutionError { impl NoSolutionError { pub fn header(&self) -> String { match &self.markers { - ResolverMarkers::Universal | ResolverMarkers::SpecificEnvironment(_) => { + ResolverMarkers::Universal { .. } | ResolverMarkers::SpecificEnvironment(_) => { "No solution found when resolving dependencies:".to_string() } ResolverMarkers::Fork(markers) => { diff --git a/crates/uv-resolver/src/fork_urls.rs b/crates/uv-resolver/src/fork_urls.rs index f34c26de2cf8..cad2b5925928 100644 --- a/crates/uv-resolver/src/fork_urls.rs +++ b/crates/uv-resolver/src/fork_urls.rs @@ -40,7 +40,8 @@ impl ForkUrls { ]; conflicting_url.sort(); return match fork_markers { - ResolverMarkers::Universal | ResolverMarkers::SpecificEnvironment(_) => { + ResolverMarkers::Universal { .. } + | ResolverMarkers::SpecificEnvironment(_) => { Err(ResolveError::ConflictingUrlsUniversal( package_name.clone(), conflicting_url, diff --git a/crates/uv-resolver/src/lock.rs b/crates/uv-resolver/src/lock.rs index 5fe7f265cb9b..724cb8589de1 100644 --- a/crates/uv-resolver/src/lock.rs +++ b/crates/uv-resolver/src/lock.rs @@ -376,6 +376,12 @@ impl Lock { self.exclude_newer } + /// If this lockfile was built from a forking resolution with non-identical forks, return the + /// markers of those forks, otherwise `None`. + pub fn fork_markers(&self) -> &Option> { + &self.fork_markers + } + /// Convert the [`Lock`] to a [`Resolution`] using the given marker environment, tags, and root. pub fn to_resolution( &self, diff --git a/crates/uv-resolver/src/resolution/display.rs b/crates/uv-resolver/src/resolution/display.rs index cf579d5dc6f2..91e8ad9392de 100644 --- a/crates/uv-resolver/src/resolution/display.rs +++ b/crates/uv-resolver/src/resolution/display.rs @@ -13,7 +13,9 @@ use uv_normalize::PackageName; use crate::resolution::{RequirementsTxtDist, ResolutionGraphNode}; use crate::{marker, ResolutionGraph, ResolverMarkers}; -static UNIVERSAL_MARKERS: ResolverMarkers = ResolverMarkers::Universal; +static UNIVERSAL_MARKERS: ResolverMarkers = ResolverMarkers::Universal { + fork_preferences: None, +}; /// A [`std::fmt::Display`] implementation for the resolution graph. #[derive(Debug)] diff --git a/crates/uv-resolver/src/resolution/graph.rs b/crates/uv-resolver/src/resolution/graph.rs index 550f99823cb6..254c6331dfba 100644 --- a/crates/uv-resolver/src/resolution/graph.rs +++ b/crates/uv-resolver/src/resolution/graph.rs @@ -169,7 +169,7 @@ impl ResolutionGraph { let fork_markers = if let [resolution] = resolutions { match resolution.markers { - ResolverMarkers::Universal | ResolverMarkers::SpecificEnvironment(_) => None, + ResolverMarkers::Universal { .. } | ResolverMarkers::SpecificEnvironment(_) => None, ResolverMarkers::Fork(_) => { panic!("A single fork must be universal"); } diff --git a/crates/uv-resolver/src/resolver/fork_map.rs b/crates/uv-resolver/src/resolver/fork_map.rs index b61017d1a026..16c49f7bb581 100644 --- a/crates/uv-resolver/src/resolver/fork_map.rs +++ b/crates/uv-resolver/src/resolver/fork_map.rs @@ -78,7 +78,7 @@ impl ForkMap { .collect(), // If we haven't forked yet, all values are potentially compatible. - ResolverMarkers::Universal => values.iter().map(|entry| &entry.value).collect(), + ResolverMarkers::Universal { .. } => values.iter().map(|entry| &entry.value).collect(), } } } diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 266196a5816d..f7096128a066 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -403,7 +403,7 @@ impl ResolverState ResolverState ForkedDependencies::Unforked(deps), Dependencies::Unavailable(err) => ForkedDependencies::Unavailable(err), }), - ResolverMarkers::Universal | ResolverMarkers::Fork(_) => Ok(result?.fork()), + ResolverMarkers::Universal { .. } | ResolverMarkers::Fork(_) => Ok(result?.fork()), } } @@ -1770,7 +1770,7 @@ impl ResolverState>, + }, /// We're in a fork of the universal resolution solving only for specific markers. Fork(MarkerTree), } impl ResolverMarkers { + /// Set the resolver to perform a resolution for a specific environment. + pub fn specific_environment(markers: MarkerEnvironment) -> Self { + Self::SpecificEnvironment(markers) + } + + /// Set the resolver to perform a universal resolution. + pub fn universal(fork_preferences: Option>) -> Self { + Self::Universal { fork_preferences } + } + /// Add the markers of an initial or subsequent fork to the current markers. pub(crate) fn and(self, other: MarkerTree) -> MarkerTree { match self { - ResolverMarkers::Universal => other, + ResolverMarkers::Universal { .. } => other, ResolverMarkers::Fork(mut current) => { current.and(other); current @@ -31,7 +46,7 @@ impl ResolverMarkers { /// If solving for a specific environment, return this environment. pub fn marker_environment(&self) -> Option<&MarkerEnvironment> { match self { - ResolverMarkers::Universal | ResolverMarkers::Fork(_) => None, + ResolverMarkers::Universal { .. } | ResolverMarkers::Fork(_) => None, ResolverMarkers::SpecificEnvironment(env) => Some(env), } } @@ -39,7 +54,7 @@ impl ResolverMarkers { /// If solving a fork, return that fork's markers. pub fn fork_markers(&self) -> Option<&MarkerTree> { match self { - ResolverMarkers::SpecificEnvironment(_) | ResolverMarkers::Universal => None, + ResolverMarkers::SpecificEnvironment(_) | ResolverMarkers::Universal { .. } => None, ResolverMarkers::Fork(markers) => Some(markers), } } @@ -48,7 +63,7 @@ impl ResolverMarkers { impl Display for ResolverMarkers { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - ResolverMarkers::Universal => f.write_str("universal"), + ResolverMarkers::Universal { .. } => f.write_str("universal"), ResolverMarkers::SpecificEnvironment(_) => f.write_str("specific environment"), ResolverMarkers::Fork(markers) => { write!(f, "({markers})") diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index 3502255aed80..08f8688af964 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -237,7 +237,7 @@ pub(crate) async fn pip_compile( // Determine the environment for the resolution. let (tags, markers) = if universal { - (None, ResolverMarkers::Universal) + (None, ResolverMarkers::universal(None)) } else { let (tags, markers) = resolution_environment(python_version, python_platform, &interpreter)?; diff --git a/crates/uv/src/commands/project/lock.rs b/crates/uv/src/commands/project/lock.rs index 1edbbbe94731..baa904e1be7e 100644 --- a/crates/uv/src/commands/project/lock.rs +++ b/crates/uv/src/commands/project/lock.rs @@ -379,6 +379,14 @@ async fn do_lock( } }); + // When we run the same resolution from the lockfile again, we could get a different result the + // second time due to the preferences causing us to skip a fork point (see + // "preferences-dependent-forking" packse scenario). To avoid this, we store the forks in the + // lockfile. We read those after all the lockfile filters, to allow the forks to change when + // the environment changed, e.g. the python bound check above can lead to different forking. + let resolver_markers = + ResolverMarkers::universal(existing_lock.and_then(|lock| lock.fork_markers().clone())); + let resolution = match existing_lock { None => None, @@ -432,7 +440,7 @@ async fn do_lock( &Reinstall::default(), upgrade, None, - ResolverMarkers::Universal, + resolver_markers, python_requirement.clone(), &client, &flat_index, @@ -508,7 +516,7 @@ async fn do_lock( &Reinstall::default(), upgrade, None, - ResolverMarkers::Universal, + ResolverMarkers::universal(None), python_requirement, &client, &flat_index,