mirror of https://github.com/astral-sh/uv
Avoid erroring on omitted wheel-only packages in `pylock.toml` (#13132)
## Summary Closes https://github.com/astral-sh/uv/issues/13127.
This commit is contained in:
parent
1cafcd0ad9
commit
17b4ebed8e
|
|
@ -62,6 +62,8 @@ pub enum PylockTomlError {
|
||||||
"Package `{0}` must include one of: `wheels`, `directory`, `archive`, `sdist`, or `vcs`"
|
"Package `{0}` must include one of: `wheels`, `directory`, `archive`, `sdist`, or `vcs`"
|
||||||
)]
|
)]
|
||||||
MissingSource(PackageName),
|
MissingSource(PackageName),
|
||||||
|
#[error("Package `{0}` does not include a compatible wheel for the current platform")]
|
||||||
|
MissingWheel(PackageName),
|
||||||
#[error("`packages.wheel` entry for `{0}` must have a `path` or `url`")]
|
#[error("`packages.wheel` entry for `{0}` must have a `path` or `url`")]
|
||||||
WheelMissingPathUrl(PackageName),
|
WheelMissingPathUrl(PackageName),
|
||||||
#[error("`packages.sdist` entry for `{0}` must have a `path` or `url`")]
|
#[error("`packages.sdist` entry for `{0}` must have a `path` or `url`")]
|
||||||
|
|
@ -863,6 +865,11 @@ impl<'lock> PylockToml {
|
||||||
let root = graph.add_node(Node::Root);
|
let root = graph.add_node(Node::Root);
|
||||||
|
|
||||||
for package in self.packages {
|
for package in self.packages {
|
||||||
|
// Omit packages that aren't relevant to the current environment.
|
||||||
|
if !package.marker.evaluate(markers, &[]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
match (
|
match (
|
||||||
package.wheels.is_some(),
|
package.wheels.is_some(),
|
||||||
package.sdist.is_some(),
|
package.sdist.is_some(),
|
||||||
|
|
@ -901,12 +908,12 @@ impl<'lock> PylockToml {
|
||||||
(_, _, _, true, true) => {
|
(_, _, _, true, true) => {
|
||||||
return Err(PylockTomlError::VcsWithArchive(package.name.clone()));
|
return Err(PylockTomlError::VcsWithArchive(package.name.clone()));
|
||||||
}
|
}
|
||||||
|
(false, false, false, false, false) => {
|
||||||
|
return Err(PylockTomlError::MissingSource(package.name.clone()));
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Omit packages that aren't relevant to the current environment.
|
|
||||||
let install = package.marker.evaluate(markers, &[]);
|
|
||||||
|
|
||||||
// Search for a matching wheel.
|
// Search for a matching wheel.
|
||||||
let dist = if let Some(best_wheel) = package.find_best_wheel(tags) {
|
let dist = if let Some(best_wheel) = package.find_best_wheel(tags) {
|
||||||
let hashes = HashDigests::from(best_wheel.hashes.clone());
|
let hashes = HashDigests::from(best_wheel.hashes.clone());
|
||||||
|
|
@ -926,7 +933,7 @@ impl<'lock> PylockToml {
|
||||||
Node::Dist {
|
Node::Dist {
|
||||||
dist,
|
dist,
|
||||||
hashes,
|
hashes,
|
||||||
install,
|
install: true,
|
||||||
}
|
}
|
||||||
} else if let Some(sdist) = package.sdist.as_ref() {
|
} else if let Some(sdist) = package.sdist.as_ref() {
|
||||||
let hashes = HashDigests::from(sdist.hashes.clone());
|
let hashes = HashDigests::from(sdist.hashes.clone());
|
||||||
|
|
@ -943,7 +950,7 @@ impl<'lock> PylockToml {
|
||||||
Node::Dist {
|
Node::Dist {
|
||||||
dist,
|
dist,
|
||||||
hashes,
|
hashes,
|
||||||
install,
|
install: true,
|
||||||
}
|
}
|
||||||
} else if let Some(sdist) = package.directory.as_ref() {
|
} else if let Some(sdist) = package.directory.as_ref() {
|
||||||
let hashes = HashDigests::empty();
|
let hashes = HashDigests::empty();
|
||||||
|
|
@ -957,7 +964,7 @@ impl<'lock> PylockToml {
|
||||||
Node::Dist {
|
Node::Dist {
|
||||||
dist,
|
dist,
|
||||||
hashes,
|
hashes,
|
||||||
install,
|
install: true,
|
||||||
}
|
}
|
||||||
} else if let Some(sdist) = package.vcs.as_ref() {
|
} else if let Some(sdist) = package.vcs.as_ref() {
|
||||||
let hashes = HashDigests::empty();
|
let hashes = HashDigests::empty();
|
||||||
|
|
@ -971,7 +978,7 @@ impl<'lock> PylockToml {
|
||||||
Node::Dist {
|
Node::Dist {
|
||||||
dist,
|
dist,
|
||||||
hashes,
|
hashes,
|
||||||
install,
|
install: true,
|
||||||
}
|
}
|
||||||
} else if let Some(dist) = package.archive.as_ref() {
|
} else if let Some(dist) = package.archive.as_ref() {
|
||||||
let hashes = HashDigests::from(dist.hashes.clone());
|
let hashes = HashDigests::from(dist.hashes.clone());
|
||||||
|
|
@ -983,10 +990,16 @@ impl<'lock> PylockToml {
|
||||||
Node::Dist {
|
Node::Dist {
|
||||||
dist,
|
dist,
|
||||||
hashes,
|
hashes,
|
||||||
install,
|
install: true,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(PylockTomlError::MissingSource(package.name.clone()));
|
// This is only reachable if the package contains a `wheels` entry (and nothing
|
||||||
|
// else), but there are no wheels available for the current environment. (If the
|
||||||
|
// package doesn't contain _any_ of `wheels`, `sdist`, etc., then we error in the
|
||||||
|
// match above.)
|
||||||
|
//
|
||||||
|
// TODO(charlie): Include a hint, like in `uv.lock`.
|
||||||
|
return Err(PylockTomlError::MissingWheel(package.name.clone()));
|
||||||
};
|
};
|
||||||
|
|
||||||
let index = graph.add_node(dist);
|
let index = graph.add_node(dist);
|
||||||
|
|
|
||||||
|
|
@ -5822,3 +5822,78 @@ fn pep_751() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Avoid erroring for packages that only include wheels, and _don't_ include a wheel for the
|
||||||
|
/// current platform, but are omitted by markers anyway.
|
||||||
|
///
|
||||||
|
/// See: <https://github.com/astral-sh/uv/issues/13127>
|
||||||
|
#[test]
|
||||||
|
fn pep_751_wheel_only() -> 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.0"
|
||||||
|
dependencies = ["torch"]
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
context
|
||||||
|
.export()
|
||||||
|
.arg("-o")
|
||||||
|
.arg("pylock.toml")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// If there's no compatible wheel for a package we _don't_ need to install (e.g., anything
|
||||||
|
// CUDA-related), succeed.
|
||||||
|
uv_snapshot!(context.filters(), context.pip_sync()
|
||||||
|
.arg("--preview")
|
||||||
|
.arg("pylock.toml")
|
||||||
|
.arg("--dry-run")
|
||||||
|
.arg("--python-platform")
|
||||||
|
.arg("macos"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Would download 9 packages
|
||||||
|
Would install 9 packages
|
||||||
|
+ filelock==3.13.1
|
||||||
|
+ fsspec==2024.3.1
|
||||||
|
+ jinja2==3.1.3
|
||||||
|
+ markupsafe==2.1.5
|
||||||
|
+ mpmath==1.3.0
|
||||||
|
+ networkx==3.2.1
|
||||||
|
+ sympy==1.12
|
||||||
|
+ torch==2.2.1
|
||||||
|
+ typing-extensions==4.10.0
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
// However, if there's no compatible wheel for a package that we _do_ need to install, we should
|
||||||
|
// error
|
||||||
|
uv_snapshot!(context.filters(), context.pip_sync()
|
||||||
|
.arg("--preview")
|
||||||
|
.arg("pylock.toml")
|
||||||
|
.arg("--dry-run")
|
||||||
|
.arg("--python-platform")
|
||||||
|
.arg("macos")
|
||||||
|
.arg("--python-version")
|
||||||
|
.arg("3.8"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Package `torch` does not include a compatible wheel for the current platform
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue