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.
This commit is contained in:
Charlie Marsh 2024-06-10 05:40:51 -07:00 committed by GitHub
parent 5269a0dba8
commit 763e2d2e84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 280 additions and 112 deletions

View file

@ -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<MarkerTree>,
#[serde(default)]
sdist: Option<SourceDist>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
wheels: Vec<Wheel>,
@ -507,17 +505,10 @@ pub struct Distribution {
impl Distribution {
fn from_annotated_dist(annotated_dist: &AnnotatedDist) -> Result<Self, LockError> {
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<ExtraName>,
#[serde(skip_serializing_if = "Option::is_none")]
marker: Option<MarkerTree>,
}
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
}

View file

@ -31,7 +31,7 @@ use crate::{
#[derive(Debug)]
pub struct ResolutionGraph {
/// The underlying graph.
pub(crate) petgraph: Graph<AnnotatedDist, (), Directed>,
pub(crate) petgraph: Graph<AnnotatedDist, Option<MarkerTree>, Directed>,
/// The range of supported Python versions.
pub(crate) requires_python: Option<RequiresPython>,
/// Any diagnostics that were encountered while building the graph.
@ -55,28 +55,8 @@ impl ResolutionGraph {
python: &PythonRequirement,
resolution: Resolution,
) -> anyhow::Result<Self, ResolveError> {
// Collect all marker expressions from relevant PubGrub packages.
let mut markers: FxHashMap<(&PackageName, &Version, &Option<ExtraName>), 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<AnnotatedDist, (), Directed> =
let mut petgraph: Graph<AnnotatedDist, Option<MarkerTree>, Directed> =
Graph::with_capacity(resolution.packages.len(), resolution.packages.len());
let mut inverse: FxHashMap<NodeKey, NodeIndex<u32>> = 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());
}
}
}

View file

@ -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<ExtraName>,
pub(crate) dev: Option<GroupName>,
pub(crate) marker: Option<MarkerTree>,
pub(crate) hashes: Vec<HashDigest>,
pub(crate) metadata: Metadata,
}

View file

@ -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<ExtraName>,
pub(crate) to_dev: Option<GroupName>,
pub(crate) marker: Option<MarkerTree>,
}
impl Resolution {

View file

@ -27,7 +27,6 @@ Ok(
},
},
},
marker: None,
sdist: None,
wheels: [
Wheel {

View file

@ -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(())
}

View file

@ -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" }]