mirror of https://github.com/astral-sh/uv
Discard insufficient fork markers (#10682)
In #10669, a pyproject.toml with requires-python but no environment had a lockfile covering only a subset of the requires-python space: ```toml resolution-markers = [ "python_full_version >= '3.10' and platform_python_implementation == 'CPython'", "python_full_version == '3.9.*'", "python_full_version < '3.9'", ] ``` This marker set is invalid, we have to reject the lockfile. (We can still use the versions though, to avoid churn). Part 1/2 of #10669
This commit is contained in:
parent
797f1fbac0
commit
33b70b17ab
|
|
@ -723,8 +723,43 @@ impl Lock {
|
||||||
self.fork_markers.as_slice()
|
self.fork_markers.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether the fork markers cover the entire supported marker space.
|
||||||
|
///
|
||||||
|
/// Returns the actually covered and the expected marker space on validation error.
|
||||||
|
pub fn check_marker_coverage(&self) -> Result<(), (MarkerTree, MarkerTree)> {
|
||||||
|
let fork_markers_union = if self.fork_markers().is_empty() {
|
||||||
|
self.requires_python.to_marker_tree()
|
||||||
|
} else {
|
||||||
|
let mut fork_markers_union = MarkerTree::FALSE;
|
||||||
|
for fork_marker in self.fork_markers() {
|
||||||
|
fork_markers_union.or(fork_marker.pep508());
|
||||||
|
}
|
||||||
|
fork_markers_union
|
||||||
|
};
|
||||||
|
let mut environments_union = if !self.supported_environments.is_empty() {
|
||||||
|
let mut environments_union = MarkerTree::FALSE;
|
||||||
|
for fork_marker in &self.supported_environments {
|
||||||
|
environments_union.or(*fork_marker);
|
||||||
|
}
|
||||||
|
environments_union
|
||||||
|
} else {
|
||||||
|
MarkerTree::TRUE
|
||||||
|
};
|
||||||
|
// When a user defines environments, they are implicitly constrained by requires-python.
|
||||||
|
environments_union.and(self.requires_python.to_marker_tree());
|
||||||
|
if fork_markers_union.negate().is_disjoint(environments_union) {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err((fork_markers_union, environments_union))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the TOML representation of this lockfile.
|
/// Returns the TOML representation of this lockfile.
|
||||||
pub fn to_toml(&self) -> Result<String, toml_edit::ser::Error> {
|
pub fn to_toml(&self) -> Result<String, toml_edit::ser::Error> {
|
||||||
|
// Catch a lockfile where the union of fork markers doesn't cover the supported
|
||||||
|
// environments.
|
||||||
|
debug_assert!(self.check_marker_coverage().is_ok());
|
||||||
|
|
||||||
// We construct a TOML document manually instead of going through Serde to enable
|
// We construct a TOML document manually instead of going through Serde to enable
|
||||||
// the use of inline tables.
|
// the use of inline tables.
|
||||||
let mut doc = toml_edit::DocumentMut::new();
|
let mut doc = toml_edit::DocumentMut::new();
|
||||||
|
|
|
||||||
|
|
@ -965,6 +965,15 @@ impl ValidatedLock {
|
||||||
return Ok(Self::Versions(lock));
|
return Ok(Self::Versions(lock));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Err((fork_markers_union, environments_union)) = lock.check_marker_coverage() {
|
||||||
|
warn_user!(
|
||||||
|
"Ignoring existing lockfile due to fork markers not covering the supported environments: `{}` vs `{}`",
|
||||||
|
fork_markers_union.try_to_string().unwrap_or("true".to_string()),
|
||||||
|
environments_union.try_to_string().unwrap_or("true".to_string()),
|
||||||
|
);
|
||||||
|
return Ok(Self::Versions(lock));
|
||||||
|
}
|
||||||
|
|
||||||
// If the set of required platforms has changed, we have to perform a clean resolution.
|
// If the set of required platforms has changed, we have to perform a clean resolution.
|
||||||
let expected = lock.simplified_required_environments();
|
let expected = lock.simplified_required_environments();
|
||||||
let actual = required_environments
|
let actual = required_environments
|
||||||
|
|
|
||||||
|
|
@ -26372,3 +26372,84 @@ fn lock_empty_extra() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The fork markers in the lockfile don't cover the supported environments (here: universal). We
|
||||||
|
/// need to discard the lockfile.
|
||||||
|
#[test]
|
||||||
|
fn lock_invalid_fork_markers() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
context.temp_dir.child("pyproject.toml").write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "attrs"
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
version = "1.0.0"
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = ["idna"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
context.temp_dir.child("uv.lock").write_str(
|
||||||
|
r#"
|
||||||
|
version = 1
|
||||||
|
requires-python = ">=3.8"
|
||||||
|
resolution-markers = [
|
||||||
|
"python_full_version >= '3.10' and platform_python_implementation == 'CPython'",
|
||||||
|
"python_full_version == '3.9.*'",
|
||||||
|
"python_full_version < '3.9'",
|
||||||
|
]
|
||||||
|
|
||||||
|
[options]
|
||||||
|
exclude-newer = "2024-03-25T00:00:00Z"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "attrs"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = { editable = "." }
|
||||||
|
|
||||||
|
[package.dev-dependencies]
|
||||||
|
dev = [
|
||||||
|
{ name = "idna", marker = "python_full_version < '3.10' or platform_python_implementation == 'CPython'" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
|
||||||
|
[package.metadata.requires-dev]
|
||||||
|
dev = [{ name = "idna" }]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "idna"
|
||||||
|
version = "3.10"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
||||||
|
]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.lock(), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: Ignoring existing lockfile due to fork markers not covering the supported environments: `(python_full_version >= '3.8' and python_full_version < '3.10') or (python_full_version >= '3.8' and platform_python_implementation == 'CPython')` vs `python_full_version >= '3.8'`
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Updated idna v3.10 -> v3.6
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Check that the lockfile got updated and we don't show the warning anymore.
|
||||||
|
uv_snapshot!(context.filters(), context.lock(), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
"###);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue