From 763e2d2e842bfb310539febaa0daf849eb8f3c73 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 10 Jun 2024 05:40:51 -0700 Subject: [PATCH] Add markers to edges rather than distributions (#4166) ## Summary We've debated this a bit but the thing that tipped me over the edge is https://github.com/astral-sh/uv/issues/4157. As-is, there's no way to represent "a package should be installed, but the extra should only be installed conditionally based on the markers", because the markers sit on the _distribution_. By placing the markers on the edge, we can now represent scenarios that weren't previously representable. Closes https://github.com/astral-sh/uv/issues/4137. Closes https://github.com/astral-sh/uv/issues/4125. Closes https://github.com/astral-sh/uv/issues/4157. --- crates/uv-resolver/src/lock.rs | 83 ++++--- crates/uv-resolver/src/resolution/graph.rs | 50 ++-- crates/uv-resolver/src/resolution/mod.rs | 3 +- crates/uv-resolver/src/resolver/mod.rs | 8 + ...r__lock__tests__hash_optional_missing.snap | 1 - crates/uv/tests/lock.rs | 223 +++++++++++++++--- crates/uv/tests/lock_scenarios.rs | 24 +- 7 files changed, 280 insertions(+), 112 deletions(-) diff --git a/crates/uv-resolver/src/lock.rs b/crates/uv-resolver/src/lock.rs index e11d3303c..05b3c6d38 100644 --- a/crates/uv-resolver/src/lock.rs +++ b/crates/uv-resolver/src/lock.rs @@ -9,6 +9,7 @@ use std::str::FromStr; use anyhow::Result; use either::Either; use indexmap::IndexMap; +use petgraph::visit::EdgeRef; use rustc_hash::FxHashMap; use toml_edit::{value, Array, ArrayOfTables, InlineTable, Item, Table, Value}; use url::Url; @@ -62,9 +63,10 @@ impl Lock { let dist = &graph.petgraph[node_index]; if dist.is_base() { let mut locked_dist = Distribution::from_annotated_dist(dist)?; - for neighbor in graph.petgraph.neighbors(node_index) { - let dependency_dist = &graph.petgraph[neighbor]; - locked_dist.add_dependency(dependency_dist); + for edge in graph.petgraph.edges(node_index) { + let dependency_dist = &graph.petgraph[edge.target()]; + let marker = edge.weight().as_ref(); + locked_dist.add_dependency(dependency_dist, marker); } let id = locked_dist.id.clone(); if let Some(locked_dist) = locked_dists.insert(id, locked_dist) { @@ -81,9 +83,10 @@ impl Lock { let Some(locked_dist) = locked_dists.get_mut(&id) else { return Err(LockError::missing_extra_base(id, extra.clone())); }; - for neighbor in graph.petgraph.neighbors(node_index) { - let dependency_dist = &graph.petgraph[neighbor]; - locked_dist.add_optional_dependency(extra.clone(), dependency_dist); + for edge in graph.petgraph.edges(node_index) { + let dependency_dist = &graph.petgraph[edge.target()]; + let marker = edge.weight().as_ref(); + locked_dist.add_optional_dependency(extra.clone(), dependency_dist, marker); } } if let Some(group) = dist.dev.as_ref() { @@ -91,9 +94,10 @@ impl Lock { let Some(locked_dist) = locked_dists.get_mut(&id) else { return Err(LockError::missing_dev_base(id, group.clone())); }; - for neighbor in graph.petgraph.neighbors(node_index) { - let dependency_dist = &graph.petgraph[neighbor]; - locked_dist.add_dev_dependency(group.clone(), dependency_dist); + for edge in graph.petgraph.edges(node_index) { + let dependency_dist = &graph.petgraph[edge.target()]; + let marker = edge.weight().as_ref(); + locked_dist.add_dev_dependency(group.clone(), dependency_dist, marker); } } } @@ -176,12 +180,12 @@ impl Lock { }; for dep in deps { - let dep_dist = self.find_by_id(&dep.distribution_id); - if dep_dist + if dep .marker .as_ref() .map_or(true, |marker| marker.evaluate(marker_env, &[])) { + let dep_dist = self.find_by_id(&dep.distribution_id); let dep_extra = dep.extra.as_ref(); queue.push_back((dep_dist, dep_extra)); } @@ -259,10 +263,6 @@ impl Lock { table.insert("version", value(dist.id.version.to_string())); table.insert("source", value(dist.id.source.to_string())); - if let Some(ref marker) = dist.marker { - table.insert("marker", value(marker.to_string())); - } - if let Some(ref sdist) = dist.sdist { table.insert("sdist", value(sdist.to_toml()?)); } @@ -491,8 +491,6 @@ pub struct Distribution { #[serde(flatten)] pub(crate) id: DistributionId, #[serde(default)] - marker: Option, - #[serde(default)] sdist: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] wheels: Vec, @@ -507,17 +505,10 @@ pub struct Distribution { impl Distribution { fn from_annotated_dist(annotated_dist: &AnnotatedDist) -> Result { let id = DistributionId::from_annotated_dist(annotated_dist); - let mut marker = annotated_dist.marker.clone(); - // Markers can be combined in an unpredictable order, so normalize them - // such that the lock file output is consistent and deterministic. - if let Some(ref mut marker) = marker { - crate::marker::normalize(marker); - } let sdist = SourceDist::from_annotated_dist(annotated_dist)?; let wheels = Wheel::from_annotated_dist(annotated_dist)?; Ok(Distribution { id, - marker, sdist, wheels, dependencies: vec![], @@ -527,24 +518,35 @@ impl Distribution { } /// Add the [`AnnotatedDist`] as a dependency of the [`Distribution`]. - fn add_dependency(&mut self, annotated_dist: &AnnotatedDist) { + fn add_dependency(&mut self, annotated_dist: &AnnotatedDist, marker: Option<&MarkerTree>) { self.dependencies - .push(Dependency::from_annotated_dist(annotated_dist)); + .push(Dependency::from_annotated_dist(annotated_dist, marker)); } /// Add the [`AnnotatedDist`] as an optional dependency of the [`Distribution`]. - fn add_optional_dependency(&mut self, extra: ExtraName, annotated_dist: &AnnotatedDist) { - let dep = Dependency::from_annotated_dist(annotated_dist); + fn add_optional_dependency( + &mut self, + extra: ExtraName, + annotated_dist: &AnnotatedDist, + marker: Option<&MarkerTree>, + ) { self.optional_dependencies .entry(extra) .or_default() - .push(dep); + .push(Dependency::from_annotated_dist(annotated_dist, marker)); } /// Add the [`AnnotatedDist`] as a development dependency of the [`Distribution`]. - fn add_dev_dependency(&mut self, dev: GroupName, annotated_dist: &AnnotatedDist) { - let dep = Dependency::from_annotated_dist(annotated_dist); - self.dev_dependencies.entry(dev).or_default().push(dep); + fn add_dev_dependency( + &mut self, + dev: GroupName, + annotated_dist: &AnnotatedDist, + marker: Option<&MarkerTree>, + ) { + self.dev_dependencies + .entry(dev) + .or_default() + .push(Dependency::from_annotated_dist(annotated_dist, marker)); } /// Convert the [`Distribution`] to a [`Dist`] that can be used in installation. @@ -1469,15 +1471,27 @@ struct Dependency { distribution_id: DistributionId, #[serde(skip_serializing_if = "Option::is_none")] extra: Option, + #[serde(skip_serializing_if = "Option::is_none")] + marker: Option, } impl Dependency { - fn from_annotated_dist(annotated_dist: &AnnotatedDist) -> Dependency { + fn from_annotated_dist( + annotated_dist: &AnnotatedDist, + marker: Option<&MarkerTree>, + ) -> Dependency { let distribution_id = DistributionId::from_annotated_dist(annotated_dist); let extra = annotated_dist.extra.clone(); + let mut marker = marker.cloned(); + // Markers can be combined in an unpredictable order, so normalize them + // such that the lock file output is consistent and deterministic. + if let Some(ref mut marker) = marker { + crate::marker::normalize(marker); + } Dependency { distribution_id, extra, + marker, } } @@ -1490,6 +1504,9 @@ impl Dependency { if let Some(ref extra) = self.extra { table.insert("extra", value(extra.to_string())); } + if let Some(ref marker) = self.marker { + table.insert("marker", value(marker.to_string())); + } table } diff --git a/crates/uv-resolver/src/resolution/graph.rs b/crates/uv-resolver/src/resolution/graph.rs index 4701e2d06..19e776c83 100644 --- a/crates/uv-resolver/src/resolution/graph.rs +++ b/crates/uv-resolver/src/resolution/graph.rs @@ -31,7 +31,7 @@ use crate::{ #[derive(Debug)] pub struct ResolutionGraph { /// The underlying graph. - pub(crate) petgraph: Graph, + pub(crate) petgraph: Graph, Directed>, /// The range of supported Python versions. pub(crate) requires_python: Option, /// Any diagnostics that were encountered while building the graph. @@ -55,28 +55,8 @@ impl ResolutionGraph { python: &PythonRequirement, resolution: Resolution, ) -> anyhow::Result { - // Collect all marker expressions from relevant PubGrub packages. - let mut markers: FxHashMap<(&PackageName, &Version, &Option), MarkerTree> = - FxHashMap::default(); - for (package, versions) in &resolution.packages { - if let PubGrubPackageInner::Package { - name, - marker: Some(marker), - extra, - .. - } = &**package - { - for version in versions { - markers - .entry((name, version, extra)) - .or_insert_with(|| MarkerTree::Or(vec![])) - .or(marker.clone()); - } - } - } - // Add every package to the graph. - let mut petgraph: Graph = + let mut petgraph: Graph, Directed> = Graph::with_capacity(resolution.packages.len(), resolution.packages.len()); let mut inverse: FxHashMap> = FxHashMap::with_capacity_and_hasher( resolution.packages.len(), @@ -186,15 +166,11 @@ impl ResolutionGraph { } } - // Extract the markers. - let marker = markers.get(&(name, version, extra)).cloned(); - // Add the distribution to the graph. let index = petgraph.add_node(AnnotatedDist { dist, extra: extra.clone(), dev: dev.clone(), - marker, hashes, metadata, }); @@ -278,15 +254,11 @@ impl ResolutionGraph { } } - // Extract the markers. - let marker = markers.get(&(name, version, extra)).cloned(); - // Add the distribution to the graph. let index = petgraph.add_node(AnnotatedDist { dist: dist.into(), extra: extra.clone(), dev: dev.clone(), - marker, hashes, metadata, }); @@ -313,7 +285,23 @@ impl ResolutionGraph { versions.to_extra.as_ref(), versions.to_dev.as_ref(), )]; - petgraph.update_edge(from_index, to_index, ()); + + if let Some(edge) = petgraph + .find_edge(from_index, to_index) + .and_then(|edge| petgraph.edge_weight_mut(edge)) + { + // If either the existing marker or new marker is `None`, then the dependency is + // included unconditionally, and so the combined marker should be `None`. + if let (Some(marker), Some(ref version_marker)) = + (edge.as_mut(), versions.marker) + { + marker.or(version_marker.clone()); + } else { + *edge = None; + } + } else { + petgraph.update_edge(from_index, to_index, versions.marker.clone()); + } } } diff --git a/crates/uv-resolver/src/resolution/mod.rs b/crates/uv-resolver/src/resolution/mod.rs index 9aadbbeeb..1efb3252e 100644 --- a/crates/uv-resolver/src/resolution/mod.rs +++ b/crates/uv-resolver/src/resolution/mod.rs @@ -5,7 +5,7 @@ use std::path::Path; use itertools::Itertools; use distribution_types::{DistributionMetadata, Name, ResolvedDist, Verbatim, VersionOrUrlRef}; -use pep508_rs::{split_scheme, MarkerTree, Scheme}; +use pep508_rs::{split_scheme, Scheme}; use pypi_types::HashDigest; use uv_distribution::Metadata; use uv_normalize::{ExtraName, GroupName, PackageName}; @@ -24,7 +24,6 @@ pub(crate) struct AnnotatedDist { pub(crate) dist: ResolvedDist, pub(crate) extra: Option, pub(crate) dev: Option, - pub(crate) marker: Option, pub(crate) hashes: Vec, pub(crate) metadata: Metadata, } diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index 29532de14..ec4636ef6 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -1566,12 +1566,14 @@ impl SolveState { to_version: dependency_version.clone(), to_extra: dependency_extra.clone(), to_dev: dependency_dev.clone(), + marker: None, }; dependencies.entry(names).or_default().insert(versions); } PubGrubPackageInner::Marker { name: ref dependency_name, + marker: ref dependency_marker, .. } => { if self_name == dependency_name { @@ -1588,6 +1590,7 @@ impl SolveState { to_version: dependency_version.clone(), to_extra: None, to_dev: None, + marker: Some(dependency_marker.clone()), }; dependencies.entry(names).or_default().insert(versions); } @@ -1595,6 +1598,7 @@ impl SolveState { PubGrubPackageInner::Extra { name: ref dependency_name, extra: ref dependency_extra, + marker: ref dependency_marker, .. } => { if self_name == dependency_name { @@ -1611,6 +1615,7 @@ impl SolveState { to_version: dependency_version.clone(), to_extra: Some(dependency_extra.clone()), to_dev: None, + marker: dependency_marker.clone(), }; dependencies.entry(names).or_default().insert(versions); } @@ -1618,6 +1623,7 @@ impl SolveState { PubGrubPackageInner::Dev { name: ref dependency_name, dev: ref dependency_dev, + marker: ref dependency_marker, .. } => { if self_name == dependency_name { @@ -1634,6 +1640,7 @@ impl SolveState { to_version: dependency_version.clone(), to_extra: None, to_dev: Some(dependency_dev.clone()), + marker: dependency_marker.clone(), }; dependencies.entry(names).or_default().insert(versions); } @@ -1676,6 +1683,7 @@ pub(crate) struct ResolutionDependencyVersions { pub(crate) to_version: Version, pub(crate) to_extra: Option, pub(crate) to_dev: Option, + pub(crate) marker: Option, } impl Resolution { 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 8ebaf6674..be0ea2b0b 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 @@ -27,7 +27,6 @@ Ok( }, }, }, - marker: None, sdist: None, wheels: [ Wheel { diff --git a/crates/uv/tests/lock.rs b/crates/uv/tests/lock.rs index 51f9ef508..bb5930079 100644 --- a/crates/uv/tests/lock.rs +++ b/crates/uv/tests/lock.rs @@ -55,6 +55,7 @@ fn lock_wheel_registry() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "idna" @@ -70,12 +71,12 @@ fn lock_wheel_registry() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -108,7 +109,6 @@ fn lock_wheel_registry() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 } wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }] "### @@ -312,6 +312,7 @@ fn lock_wheel_url() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "idna" @@ -327,12 +328,12 @@ fn lock_wheel_url() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -365,7 +366,6 @@ fn lock_wheel_url() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 } wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }] "### @@ -437,6 +437,7 @@ fn lock_sdist_url() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "idna" @@ -452,12 +453,12 @@ fn lock_sdist_url() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -490,7 +491,6 @@ fn lock_sdist_url() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 } wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }] "### @@ -566,6 +566,7 @@ fn lock_project_extra() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "idna" @@ -581,12 +582,12 @@ fn lock_project_extra() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -633,7 +634,6 @@ fn lock_project_extra() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 } wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }] "### @@ -726,17 +726,18 @@ fn lock_dependency_extra() -> Result<()> { name = "colorama" version = "0.4.6" source = "registry+https://pypi.org/simple" + marker = "platform_system == 'Windows'" [[distribution.dependencies]] name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution]] name = "colorama" version = "0.4.6" source = "registry+https://pypi.org/simple" - marker = "platform_system == 'Windows'" sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } wheels = [{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }] @@ -761,6 +762,7 @@ fn lock_dependency_extra() -> Result<()> { name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.10'" [[distribution.dependencies]] name = "itsdangerous" @@ -788,7 +790,6 @@ fn lock_dependency_extra() -> Result<()> { name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.10'" sdist = { url = "https://files.pythonhosted.org/packages/a0/fc/c4e6078d21fc4fa56300a241b87eae76766aa380a23fc450fc85bb7bf547/importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2", size = 52120 } wheels = [{ url = "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", size = 24409 }] @@ -796,6 +797,7 @@ fn lock_dependency_extra() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution.dependencies]] name = "zipp" @@ -916,7 +918,6 @@ fn lock_dependency_extra() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 } wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }] @@ -967,9 +968,6 @@ fn lock_dependency_extra() -> Result<()> { } /// Lock a project with a dependency that has a conditional extra. -/// -/// TODO(charlie): This test incorrectly omits `requests` due to the condition applied to its -/// extra. #[test] fn lock_conditional_dependency_extra() -> Result<()> { let context = TestContext::new("3.12"); @@ -981,7 +979,7 @@ fn lock_conditional_dependency_extra() -> Result<()> { name = "project" version = "0.1.0" requires-python = ">=3.7" - dependencies = ["requests", "requests[socks] ; python_version < '3.8'"] + dependencies = ["requests", "requests[socks] ; python_version < '3.10'"] "#, )?; @@ -995,7 +993,8 @@ fn lock_conditional_dependency_extra() -> Result<()> { Resolved 7 packages in [TIME] "###); - let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock"))?; + let lockfile = context.temp_dir.join("uv.lock"); + let lock = fs_err::read_to_string(&lockfile)?; insta::with_settings!({ filters => context.filters(), @@ -1130,6 +1129,7 @@ fn lock_conditional_dependency_extra() -> Result<()> { version = "2.31.0" source = "registry+https://pypi.org/simple" extra = "socks" + marker = "python_version < '3.10'" [[distribution]] name = "pysocks" @@ -1145,7 +1145,6 @@ fn lock_conditional_dependency_extra() -> Result<()> { name = "requests" version = "2.31.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/9d/be/10918a2eac4ae9f02f6cfe6414b7a155ccd8f7f9d4380d62fd5b955065c3/requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1", size = 110794 } wheels = [{ url = "https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", size = 62574 }] @@ -1194,9 +1193,39 @@ fn lock_conditional_dependency_extra() -> Result<()> { ----- stderr ----- warning: `uv sync` is experimental and may change without warning. - Downloaded 1 package in [TIME] - Installed 1 package in [TIME] + Downloaded 6 packages in [TIME] + Installed 6 packages in [TIME] + + certifi==2024.2.2 + + charset-normalizer==3.0.1 + + idna==3.6 + project==0.1.0 (from file://[TEMP_DIR]/) + + requests==2.31.0 + + urllib3==2.0.7 + "###); + + // Validate that the extra is included on relevant Python versions. + let context_38 = TestContext::new("3.8"); + + fs_err::copy(pyproject_toml, context_38.temp_dir.join("pyproject.toml"))?; + fs_err::copy(lockfile, context_38.temp_dir.join("uv.lock"))?; + + // Install from the lockfile. + uv_snapshot!(context.filters(), context_38.sync(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv sync` is experimental and may change without warning. + Downloaded 7 packages in [TIME] + Installed 7 packages in [TIME] + + certifi==2024.2.2 + + charset-normalizer==3.0.1 + + idna==3.6 + + project==0.1.0 (from file://[TEMP_DIR]/) + + pysocks==1.7.1 + + requests==2.31.0 + + urllib3==2.0.7 "###); Ok(()) @@ -1604,6 +1633,7 @@ fn lock_requires_python() -> Result<()> { name = "importlib-metadata" version = "6.7.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution]] name = "cattrs" @@ -1621,17 +1651,18 @@ fn lock_requires_python() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "typing-extensions" version = "4.7.1" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -1639,7 +1670,6 @@ fn lock_requires_python() -> Result<()> { name = "importlib-metadata" version = "6.7.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/a3/82/f6e29c8d5c098b6be61460371c2c5591f4a335923639edec43b3830650a4/importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4", size = 53569 } wheels = [{ url = "https://files.pythonhosted.org/packages/ff/94/64287b38c7de4c90683630338cf28f129decbba0a44f0c6db35a873c73c4/importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5", size = 22934 }] @@ -1647,6 +1677,7 @@ fn lock_requires_python() -> Result<()> { name = "typing-extensions" version = "4.7.1" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution.dependencies]] name = "zipp" @@ -1709,7 +1740,6 @@ fn lock_requires_python() -> Result<()> { name = "typing-extensions" version = "4.7.1" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/3c/8b/0111dd7d6c1478bf83baa1cab85c686426c7a6274119aceb2bd9d35395ad/typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2", size = 72876 } wheels = [{ url = "https://files.pythonhosted.org/packages/ec/6b/63cc3df74987c36fe26157ee12e09e8f9db4de771e0f3404263117e75b95/typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", size = 33232 }] @@ -1768,6 +1798,7 @@ fn lock_requires_python() -> Result<()> { name = "importlib-metadata" version = "6.7.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution]] name = "cattrs" @@ -1785,17 +1816,18 @@ fn lock_requires_python() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "typing-extensions" version = "4.7.1" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -1803,7 +1835,6 @@ fn lock_requires_python() -> Result<()> { name = "importlib-metadata" version = "6.7.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/a3/82/f6e29c8d5c098b6be61460371c2c5591f4a335923639edec43b3830650a4/importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4", size = 53569 } wheels = [{ url = "https://files.pythonhosted.org/packages/ff/94/64287b38c7de4c90683630338cf28f129decbba0a44f0c6db35a873c73c4/importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5", size = 22934 }] @@ -1811,6 +1842,7 @@ fn lock_requires_python() -> Result<()> { name = "typing-extensions" version = "4.7.1" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution.dependencies]] name = "zipp" @@ -1861,7 +1893,6 @@ fn lock_requires_python() -> Result<()> { name = "typing-extensions" version = "4.7.1" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/3c/8b/0111dd7d6c1478bf83baa1cab85c686426c7a6274119aceb2bd9d35395ad/typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2", size = 72876 } wheels = [{ url = "https://files.pythonhosted.org/packages/ec/6b/63cc3df74987c36fe26157ee12e09e8f9db4de771e0f3404263117e75b95/typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", size = 33232 }] @@ -1920,6 +1951,7 @@ fn lock_requires_python() -> Result<()> { name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution]] name = "cattrs" @@ -1937,17 +1969,18 @@ fn lock_requires_python() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -1955,7 +1988,6 @@ fn lock_requires_python() -> Result<()> { name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/a0/fc/c4e6078d21fc4fa56300a241b87eae76766aa380a23fc450fc85bb7bf547/importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2", size = 52120 } wheels = [{ url = "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", size = 24409 }] @@ -1963,6 +1995,7 @@ fn lock_requires_python() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution.dependencies]] name = "zipp" @@ -2018,7 +2051,6 @@ fn lock_requires_python() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 } wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }] @@ -2102,6 +2134,7 @@ fn lock_requires_python_star() -> Result<()> { name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution]] name = "cattrs" @@ -2119,17 +2152,18 @@ fn lock_requires_python_star() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -2137,7 +2171,6 @@ fn lock_requires_python_star() -> Result<()> { name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/a0/fc/c4e6078d21fc4fa56300a241b87eae76766aa380a23fc450fc85bb7bf547/importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2", size = 52120 } wheels = [{ url = "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", size = 24409 }] @@ -2145,6 +2178,7 @@ fn lock_requires_python_star() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution.dependencies]] name = "zipp" @@ -2202,7 +2236,6 @@ fn lock_requires_python_star() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 } wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }] @@ -2270,6 +2303,7 @@ fn lock_requires_python_pre() -> Result<()> { name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution]] name = "cattrs" @@ -2287,17 +2321,18 @@ fn lock_requires_python_pre() -> Result<()> { name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution.dependencies]] name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.11'" [[distribution]] name = "exceptiongroup" version = "1.2.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/8e/1c/beef724eaf5b01bb44b6338c8c3494eff7cab376fab4904cfbbc3585dc79/exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68", size = 26264 } wheels = [{ url = "https://files.pythonhosted.org/packages/b8/9a/5028fd52db10e600f1c4674441b968cf2ea4959085bfb5b99fb1250e5f68/exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14", size = 16210 }] @@ -2305,7 +2340,6 @@ fn lock_requires_python_pre() -> Result<()> { name = "importlib-metadata" version = "7.1.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.8'" sdist = { url = "https://files.pythonhosted.org/packages/a0/fc/c4e6078d21fc4fa56300a241b87eae76766aa380a23fc450fc85bb7bf547/importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2", size = 52120 } wheels = [{ url = "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570", size = 24409 }] @@ -2313,6 +2347,7 @@ fn lock_requires_python_pre() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" + marker = "python_version < '3.8'" [[distribution.dependencies]] name = "zipp" @@ -2370,7 +2405,6 @@ fn lock_requires_python_pre() -> Result<()> { name = "typing-extensions" version = "4.10.0" source = "registry+https://pypi.org/simple" - marker = "python_version < '3.11'" sdist = { url = "https://files.pythonhosted.org/packages/16/3a/0d26ce356c7465a19c9ea8814b960f8a36c3b0d07c323176620b7b483e44/typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb", size = 77558 } wheels = [{ url = "https://files.pythonhosted.org/packages/f9/de/dc04a3ea60b22624b51c703a84bbe0184abcd1d0b9bc8074b5d6b7ab90bb/typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", size = 33926 }] @@ -2490,3 +2524,124 @@ fn lock_dev() -> Result<()> { Ok(()) } + +/// Lock a package that's included both conditionally and unconditionally in the lockfile. +#[test] +fn lock_conditional_unconditional() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["iniconfig", "iniconfig ; python_version < '3.12'"] + "#, + )?; + + uv_snapshot!(context.filters(), context.lock(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv lock` is experimental and may change without warning. + Resolved 2 packages in [TIME] + "###); + + let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock"))?; + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + lock, @r###" + version = 1 + requires-python = ">=3.12" + + [[distribution]] + name = "iniconfig" + version = "2.0.0" + source = "registry+https://pypi.org/simple" + 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 }] + + [[distribution]] + name = "project" + version = "0.1.0" + source = "editable+file://[TEMP_DIR]/" + sdist = { url = "file://[TEMP_DIR]/" } + + [[distribution.dependencies]] + name = "iniconfig" + version = "2.0.0" + source = "registry+https://pypi.org/simple" + "### + ); + }); + + Ok(()) +} + +/// Lock a package that's included twice with different markers. +#[test] +fn lock_multiple_markers() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["iniconfig ; implementation_name == 'cpython'", "iniconfig ; python_version < '3.12'"] + "#, + )?; + + uv_snapshot!(context.filters(), context.lock(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + warning: `uv lock` is experimental and may change without warning. + Resolved 2 packages in [TIME] + "###); + + let lock = fs_err::read_to_string(context.temp_dir.join("uv.lock"))?; + + insta::with_settings!({ + filters => context.filters(), + }, { + assert_snapshot!( + lock, @r###" + version = 1 + requires-python = ">=3.12" + + [[distribution]] + name = "iniconfig" + version = "2.0.0" + source = "registry+https://pypi.org/simple" + 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 }] + + [[distribution]] + name = "project" + version = "0.1.0" + source = "editable+file://[TEMP_DIR]/" + sdist = { url = "file://[TEMP_DIR]/" } + + [[distribution.dependencies]] + name = "iniconfig" + version = "2.0.0" + source = "registry+https://pypi.org/simple" + marker = "python_version < '3.12' or implementation_name == 'cpython'" + "### + ); + }); + + Ok(()) +} diff --git a/crates/uv/tests/lock_scenarios.rs b/crates/uv/tests/lock_scenarios.rs index d5701b3c0..d39607099 100644 --- a/crates/uv/tests/lock_scenarios.rs +++ b/crates/uv/tests/lock_scenarios.rs @@ -79,7 +79,6 @@ fn fork_basic() -> Result<()> { name = "package-a" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "sys_platform == 'darwin'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_basic_a-1.0.0.tar.gz#sha256=3e45d6136e4a52416f85b7f53f405493db8f9fea33210299e6a68895bf0acf2a", hash = "sha256:3e45d6136e4a52416f85b7f53f405493db8f9fea33210299e6a68895bf0acf2a" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_basic_a-1.0.0-py3-none-any.whl#sha256=b81a7553af25f15c9d49ed26af9c5b86eb2be107f3dd1bd97d7a4b0e8ca0329e", hash = "sha256:b81a7553af25f15c9d49ed26af9c5b86eb2be107f3dd1bd97d7a4b0e8ca0329e" }] @@ -87,7 +86,6 @@ fn fork_basic() -> Result<()> { name = "package-a" version = "2.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "sys_platform == 'linux'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_basic_a-2.0.0.tar.gz#sha256=ceb7349a6dd7640be952c70dce8ee6a44e3442dfd9b248b96242e37623e1028e", hash = "sha256:ceb7349a6dd7640be952c70dce8ee6a44e3442dfd9b248b96242e37623e1028e" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_basic_a-2.0.0-py3-none-any.whl#sha256=9cab1de38d28e75ac5fe5c4dda9157555c60dd03ee26e6ad51b01ca18d8a0f01", hash = "sha256:9cab1de38d28e75ac5fe5c4dda9157555c60dd03ee26e6ad51b01ca18d8a0f01" }] @@ -101,11 +99,13 @@ fn fork_basic() -> Result<()> { name = "package-a" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'darwin'" [[distribution.dependencies]] name = "package-a" version = "2.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'linux'" "### ); }); @@ -188,7 +188,6 @@ fn fork_marker_accrue() -> Result<()> { name = "package-a" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "implementation_name == 'cpython'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_accrue_a-1.0.0.tar.gz#sha256=9096dbf9c8e8c2da4a1527be515f740f697ee833ec1492953883f36c8931bc37", hash = "sha256:9096dbf9c8e8c2da4a1527be515f740f697ee833ec1492953883f36c8931bc37" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_accrue_a-1.0.0-py3-none-any.whl#sha256=5fed1607b73cc7a5e9703206c24cc3fa730600a776bf40ae264ad364ad610e0a", hash = "sha256:5fed1607b73cc7a5e9703206c24cc3fa730600a776bf40ae264ad364ad610e0a" }] @@ -196,12 +195,12 @@ fn fork_marker_accrue() -> Result<()> { name = "package-c" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'linux'" [[distribution]] name = "package-b" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "implementation_name == 'pypy'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_accrue_b-1.0.0.tar.gz#sha256=d92d0083d2d5da2f83180c08dfc79a03ec9606c00bc3153566f7b577c0e6b859", hash = "sha256:d92d0083d2d5da2f83180c08dfc79a03ec9606c00bc3153566f7b577c0e6b859" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_accrue_b-1.0.0-py3-none-any.whl#sha256=e5382e438f417f2de9427296a5960f9f9631ff1fa11c93d6b0b3b9d7fb60760f", hash = "sha256:e5382e438f417f2de9427296a5960f9f9631ff1fa11c93d6b0b3b9d7fb60760f" }] @@ -209,12 +208,12 @@ fn fork_marker_accrue() -> Result<()> { name = "package-c" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'darwin'" [[distribution]] name = "package-c" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "sys_platform == 'darwin' or sys_platform == 'linux'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_accrue_c-1.0.0.tar.gz#sha256=81068ae8b43deb3165cab17eb52aa5f99cda64f51c359b4659918d86995b9cad", hash = "sha256:81068ae8b43deb3165cab17eb52aa5f99cda64f51c359b4659918d86995b9cad" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_accrue_c-1.0.0-py3-none-any.whl#sha256=f5fe6d35f360ea802b3a7da030e9ed1dce776c30ed028ea7be04fafcb7ac55b6", hash = "sha256:f5fe6d35f360ea802b3a7da030e9ed1dce776c30ed028ea7be04fafcb7ac55b6" }] @@ -228,11 +227,13 @@ fn fork_marker_accrue() -> Result<()> { name = "package-a" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "implementation_name == 'cpython'" [[distribution.dependencies]] name = "package-b" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "implementation_name == 'pypy'" "### ); }); @@ -400,7 +401,6 @@ fn fork_marker_selection() -> Result<()> { name = "package-b" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "sys_platform == 'darwin'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_selection_b-1.0.0.tar.gz#sha256=97f1098f4c89457ab2b16982990d487ac6ae2c664f8e22e822a086df71999dc1", hash = "sha256:97f1098f4c89457ab2b16982990d487ac6ae2c664f8e22e822a086df71999dc1" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_selection_b-1.0.0-py3-none-any.whl#sha256=aba998c3dfa70f4118a4587f636c96f5a2785081b733120cf81b6d762f67b1ca", hash = "sha256:aba998c3dfa70f4118a4587f636c96f5a2785081b733120cf81b6d762f67b1ca" }] @@ -408,7 +408,6 @@ fn fork_marker_selection() -> Result<()> { name = "package-b" version = "2.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "sys_platform == 'linux'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_selection_b-2.0.0.tar.gz#sha256=1f66e4ba827d2913827fa52cc9fd08491b16ab409fa31c40a2fe4e3cde91cb4a", hash = "sha256:1f66e4ba827d2913827fa52cc9fd08491b16ab409fa31c40a2fe4e3cde91cb4a" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_selection_b-2.0.0-py3-none-any.whl#sha256=ad1b23547813b9ac69b33d3fcf1896cd49a90cd8f957e954dbdd77b628d631cf", hash = "sha256:ad1b23547813b9ac69b33d3fcf1896cd49a90cd8f957e954dbdd77b628d631cf" }] @@ -432,11 +431,13 @@ fn fork_marker_selection() -> Result<()> { name = "package-b" version = "1.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'darwin'" [[distribution.dependencies]] name = "package-b" version = "2.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'linux'" "### ); }); @@ -539,6 +540,7 @@ fn fork_marker_track() -> Result<()> { name = "package-c" version = "1.10" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "implementation_name == 'iron'" [[distribution]] name = "package-a" @@ -556,7 +558,6 @@ fn fork_marker_track() -> Result<()> { name = "package-b" version = "2.7" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "sys_platform == 'darwin'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_track_b-2.7.tar.gz#sha256=25258fd52c9611c9e101138f9986ada5930f5bea08988d0356645c772a8162dd", hash = "sha256:25258fd52c9611c9e101138f9986ada5930f5bea08988d0356645c772a8162dd" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_track_b-2.7-py3-none-any.whl#sha256=be56f5850a343cb02dfc22e75eaa1009db675ac2f1275b78ba4089c6ea2f2808", hash = "sha256:be56f5850a343cb02dfc22e75eaa1009db675ac2f1275b78ba4089c6ea2f2808" }] @@ -564,7 +565,6 @@ fn fork_marker_track() -> Result<()> { name = "package-b" version = "2.8" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "sys_platform == 'linux'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_track_b-2.8.tar.gz#sha256=7ec0f88f013fa0b75a4c88097799866617de4cae558b18ad0677f7cc65ad6628", hash = "sha256:7ec0f88f013fa0b75a4c88097799866617de4cae558b18ad0677f7cc65ad6628" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_track_b-2.8-py3-none-any.whl#sha256=d9969066117d846fe3a200df5bafc3b3279cc419f36f7275e6e55b2dbde2d5d1", hash = "sha256:d9969066117d846fe3a200df5bafc3b3279cc419f36f7275e6e55b2dbde2d5d1" }] @@ -572,7 +572,6 @@ fn fork_marker_track() -> Result<()> { name = "package-c" version = "1.10" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "implementation_name == 'iron'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_track_c-1.10.tar.gz#sha256=6f4a62bec34fbda0e605dc9acb40af318b1d789816d81cbd0bc7c60595de5930", hash = "sha256:6f4a62bec34fbda0e605dc9acb40af318b1d789816d81cbd0bc7c60595de5930" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_marker_track_c-1.10-py3-none-any.whl#sha256=19791f8bd3bad9a76be5477e1753dc2a4e797d163bef90fdfd99462c271ed6ff", hash = "sha256:19791f8bd3bad9a76be5477e1753dc2a4e797d163bef90fdfd99462c271ed6ff" }] @@ -596,11 +595,13 @@ fn fork_marker_track() -> Result<()> { name = "package-b" version = "2.7" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'darwin'" [[distribution.dependencies]] name = "package-b" version = "2.8" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'linux'" "### ); }); @@ -689,6 +690,7 @@ fn fork_non_fork_marker_transitive() -> Result<()> { name = "package-c" version = "2.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'linux'" [[distribution]] name = "package-b" @@ -701,12 +703,12 @@ fn fork_non_fork_marker_transitive() -> Result<()> { name = "package-c" version = "2.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" + marker = "sys_platform == 'darwin'" [[distribution]] name = "package-c" version = "2.0.0" source = "registry+https://astral-sh.github.io/packse/0.3.18/simple-html/" - marker = "sys_platform == 'darwin' or sys_platform == 'linux'" sdist = { url = "https://astral-sh.github.io/packse/0.3.18/files/fork_non_fork_marker_transitive_c-2.0.0.tar.gz#sha256=c989314fe5534401e9b2374e9b0461c9d44c237853d9122bc7d9aee006ee0c34", hash = "sha256:c989314fe5534401e9b2374e9b0461c9d44c237853d9122bc7d9aee006ee0c34" } wheels = [{ url = "https://astral-sh.github.io/packse/0.3.18/files/fork_non_fork_marker_transitive_c-2.0.0-py3-none-any.whl#sha256=661def8c77b372df8146049485a75678ecee810518fb7cba024b609920bdef74", hash = "sha256:661def8c77b372df8146049485a75678ecee810518fb7cba024b609920bdef74" }]