mirror of https://github.com/astral-sh/uv
Add required environment marker example to hint (#16244)
## Summary fixes issue #15938 - show platform wheel hint with a concrete `tool.uv.required-environments` example so users know how to configure compatibility - add `WheelTagHint::suggest_environment_marker` to pick a sensible environment marker based on the available wheel tags - update the `sync_required_environment_hint` integration snapshot to expect the new multi-line hint ## Test Plan cargo test --package uv --test it -- sync::sync_required_environment_hint
This commit is contained in:
parent
2b0407e277
commit
ed3f99a119
|
|
@ -1144,13 +1144,13 @@ impl<'lock> PylockToml {
|
|||
kind: Box::new(PylockTomlErrorKind::IncompatibleWheelOnly(
|
||||
package.name.clone(),
|
||||
)),
|
||||
hint: package.tag_hint(tags),
|
||||
hint: package.tag_hint(tags, markers),
|
||||
}),
|
||||
(false, false) => Err(PylockTomlError {
|
||||
kind: Box::new(PylockTomlErrorKind::NeitherSourceDistNorWheel(
|
||||
package.name.clone(),
|
||||
)),
|
||||
hint: package.tag_hint(tags),
|
||||
hint: package.tag_hint(tags, markers),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
|
@ -1279,7 +1279,7 @@ impl PylockTomlPackage {
|
|||
}
|
||||
|
||||
/// Generate a [`WheelTagHint`] based on wheel-tag incompatibilities.
|
||||
fn tag_hint(&self, tags: &Tags) -> Option<WheelTagHint> {
|
||||
fn tag_hint(&self, tags: &Tags, markers: &MarkerEnvironment) -> Option<WheelTagHint> {
|
||||
let filenames = self
|
||||
.wheels
|
||||
.iter()
|
||||
|
|
@ -1287,7 +1287,7 @@ impl PylockTomlPackage {
|
|||
.filter_map(|wheel| wheel.filename(&self.name).ok())
|
||||
.collect::<Vec<_>>();
|
||||
let filenames = filenames.iter().map(Cow::as_ref).collect::<Vec<_>>();
|
||||
WheelTagHint::from_wheels(&self.name, self.version.as_ref(), &filenames, tags)
|
||||
WheelTagHint::from_wheels(&self.name, self.version.as_ref(), &filenames, tags, markers)
|
||||
}
|
||||
|
||||
/// Returns the [`ResolvedRepositoryReference`] for the package, if it is a Git source.
|
||||
|
|
|
|||
|
|
@ -107,9 +107,9 @@ pub trait Installable<'lock> {
|
|||
|
||||
// Add the workspace package to the graph.
|
||||
let index = petgraph.add_node(if groups.prod() {
|
||||
self.package_to_node(dist, tags, build_options, install_options)?
|
||||
self.package_to_node(dist, tags, build_options, install_options, marker_env)?
|
||||
} else {
|
||||
self.non_installable_node(dist, tags)?
|
||||
self.non_installable_node(dist, tags, marker_env)?
|
||||
});
|
||||
inverse.insert(&dist.id, index);
|
||||
|
||||
|
|
@ -162,6 +162,7 @@ pub trait Installable<'lock> {
|
|||
tags,
|
||||
build_options,
|
||||
install_options,
|
||||
marker_env,
|
||||
)?);
|
||||
entry.insert(index);
|
||||
index
|
||||
|
|
@ -178,6 +179,7 @@ pub trait Installable<'lock> {
|
|||
tags,
|
||||
build_options,
|
||||
install_options,
|
||||
marker_env,
|
||||
)?;
|
||||
}
|
||||
index
|
||||
|
|
@ -226,9 +228,9 @@ pub trait Installable<'lock> {
|
|||
|
||||
// Add the package to the graph.
|
||||
let index = petgraph.add_node(if groups.prod() {
|
||||
self.package_to_node(dist, tags, build_options, install_options)?
|
||||
self.package_to_node(dist, tags, build_options, install_options, marker_env)?
|
||||
} else {
|
||||
self.non_installable_node(dist, tags)?
|
||||
self.non_installable_node(dist, tags, marker_env)?
|
||||
});
|
||||
inverse.insert(&dist.id, index);
|
||||
|
||||
|
|
@ -284,6 +286,7 @@ pub trait Installable<'lock> {
|
|||
tags,
|
||||
build_options,
|
||||
install_options,
|
||||
marker_env,
|
||||
)?);
|
||||
entry.insert(index);
|
||||
index
|
||||
|
|
@ -295,7 +298,13 @@ pub trait Installable<'lock> {
|
|||
let index = *entry.get();
|
||||
let node = &mut petgraph[index];
|
||||
if !groups.prod() {
|
||||
*node = self.package_to_node(dist, tags, build_options, install_options)?;
|
||||
*node = self.package_to_node(
|
||||
dist,
|
||||
tags,
|
||||
build_options,
|
||||
install_options,
|
||||
marker_env,
|
||||
)?;
|
||||
}
|
||||
index
|
||||
}
|
||||
|
|
@ -475,6 +484,7 @@ pub trait Installable<'lock> {
|
|||
tags,
|
||||
build_options,
|
||||
install_options,
|
||||
marker_env,
|
||||
)?);
|
||||
entry.insert(index);
|
||||
index
|
||||
|
|
@ -514,12 +524,14 @@ pub trait Installable<'lock> {
|
|||
&self,
|
||||
package: &Package,
|
||||
tags: &Tags,
|
||||
marker_env: &ResolverMarkerEnvironment,
|
||||
build_options: &BuildOptions,
|
||||
) -> Result<Node, LockError> {
|
||||
let dist = package.to_dist(
|
||||
self.install_path(),
|
||||
TagPolicy::Required(tags),
|
||||
build_options,
|
||||
marker_env,
|
||||
)?;
|
||||
let version = package.version().cloned();
|
||||
let dist = ResolvedDist::Installable {
|
||||
|
|
@ -535,11 +547,17 @@ pub trait Installable<'lock> {
|
|||
}
|
||||
|
||||
/// Create a non-installable [`Node`] from a [`Package`].
|
||||
fn non_installable_node(&self, package: &Package, tags: &Tags) -> Result<Node, LockError> {
|
||||
fn non_installable_node(
|
||||
&self,
|
||||
package: &Package,
|
||||
tags: &Tags,
|
||||
marker_env: &ResolverMarkerEnvironment,
|
||||
) -> Result<Node, LockError> {
|
||||
let dist = package.to_dist(
|
||||
self.install_path(),
|
||||
TagPolicy::Preferred(tags),
|
||||
&BuildOptions::default(),
|
||||
marker_env,
|
||||
)?;
|
||||
let version = package.version().cloned();
|
||||
let dist = ResolvedDist::Installable {
|
||||
|
|
@ -561,15 +579,16 @@ pub trait Installable<'lock> {
|
|||
tags: &Tags,
|
||||
build_options: &BuildOptions,
|
||||
install_options: &InstallOptions,
|
||||
marker_env: &ResolverMarkerEnvironment,
|
||||
) -> Result<Node, LockError> {
|
||||
if install_options.include_package(
|
||||
package.as_install_target(),
|
||||
self.project_name(),
|
||||
self.lock().members(),
|
||||
) {
|
||||
self.installable_node(package, tags, build_options)
|
||||
self.installable_node(package, tags, marker_env, build_options)
|
||||
} else {
|
||||
self.non_installable_node(package, tags)
|
||||
self.non_installable_node(package, tags, marker_env)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1452,6 +1452,7 @@ impl Lock {
|
|||
dependency_metadata: &DependencyMetadata,
|
||||
indexes: Option<&IndexLocations>,
|
||||
tags: &Tags,
|
||||
markers: &MarkerEnvironment,
|
||||
hasher: &HashStrategy,
|
||||
index: &InMemoryIndex,
|
||||
database: &DistributionDatabase<'_, Context>,
|
||||
|
|
@ -1724,8 +1725,12 @@ impl Lock {
|
|||
|
||||
if let Some(version) = package.id.version.as_ref() {
|
||||
// For a non-dynamic package, fetch the metadata from the distribution database.
|
||||
let dist =
|
||||
package.to_dist(root, TagPolicy::Preferred(tags), &BuildOptions::default())?;
|
||||
let dist = package.to_dist(
|
||||
root,
|
||||
TagPolicy::Preferred(tags),
|
||||
&BuildOptions::default(),
|
||||
markers,
|
||||
)?;
|
||||
|
||||
let metadata = {
|
||||
let id = dist.version_id();
|
||||
|
|
@ -1875,6 +1880,7 @@ impl Lock {
|
|||
root,
|
||||
TagPolicy::Preferred(tags),
|
||||
&BuildOptions::default(),
|
||||
markers,
|
||||
)?;
|
||||
|
||||
let metadata = {
|
||||
|
|
@ -2511,6 +2517,7 @@ impl Package {
|
|||
workspace_root: &Path,
|
||||
tag_policy: TagPolicy<'_>,
|
||||
build_options: &BuildOptions,
|
||||
markers: &MarkerEnvironment,
|
||||
) -> Result<Dist, LockError> {
|
||||
let no_binary = build_options.no_binary_package(&self.id.name);
|
||||
let no_build = build_options.no_build_package(&self.id.name);
|
||||
|
|
@ -2613,19 +2620,23 @@ impl Package {
|
|||
kind: Box::new(LockErrorKind::IncompatibleWheelOnly {
|
||||
id: self.id.clone(),
|
||||
}),
|
||||
hint: self.tag_hint(tag_policy),
|
||||
hint: self.tag_hint(tag_policy, markers),
|
||||
}),
|
||||
(false, false) => Err(LockError {
|
||||
kind: Box::new(LockErrorKind::NeitherSourceDistNorWheel {
|
||||
id: self.id.clone(),
|
||||
}),
|
||||
hint: self.tag_hint(tag_policy),
|
||||
hint: self.tag_hint(tag_policy, markers),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a [`WheelTagHint`] based on wheel-tag incompatibilities.
|
||||
fn tag_hint(&self, tag_policy: TagPolicy<'_>) -> Option<WheelTagHint> {
|
||||
fn tag_hint(
|
||||
&self,
|
||||
tag_policy: TagPolicy<'_>,
|
||||
markers: &MarkerEnvironment,
|
||||
) -> Option<WheelTagHint> {
|
||||
let filenames = self
|
||||
.wheels
|
||||
.iter()
|
||||
|
|
@ -2636,6 +2647,7 @@ impl Package {
|
|||
self.id.version.as_ref(),
|
||||
&filenames,
|
||||
tag_policy.tags(),
|
||||
markers,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -5260,6 +5272,7 @@ enum WheelTagHint {
|
|||
version: Option<Version>,
|
||||
tags: BTreeSet<PlatformTag>,
|
||||
best: Option<PlatformTag>,
|
||||
markers: MarkerEnvironment,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -5270,6 +5283,7 @@ impl WheelTagHint {
|
|||
version: Option<&Version>,
|
||||
filenames: &[&WheelFilename],
|
||||
tags: &Tags,
|
||||
markers: &MarkerEnvironment,
|
||||
) -> Option<Self> {
|
||||
let incompatibility = filenames
|
||||
.iter()
|
||||
|
|
@ -5322,17 +5336,18 @@ impl WheelTagHint {
|
|||
}
|
||||
TagCompatibility::Incompatible(IncompatibleTag::Platform) => {
|
||||
let best = tags.platform_tag().cloned();
|
||||
let tags = Self::platform_tags(filenames.iter().copied(), tags)
|
||||
let incompatible_tags = Self::platform_tags(filenames.iter().copied(), tags)
|
||||
.cloned()
|
||||
.collect::<BTreeSet<_>>();
|
||||
if tags.is_empty() {
|
||||
if incompatible_tags.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Self::PlatformTags {
|
||||
package: name.clone(),
|
||||
version: version.cloned(),
|
||||
tags,
|
||||
tags: incompatible_tags,
|
||||
best,
|
||||
markers: markers.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -5373,6 +5388,18 @@ impl WheelTagHint {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn suggest_environment_marker(markers: &MarkerEnvironment) -> String {
|
||||
let sys_platform = markers.sys_platform();
|
||||
let platform_machine = markers.platform_machine();
|
||||
|
||||
// Generate the marker string based on actual environment values
|
||||
if platform_machine.is_empty() {
|
||||
format!("sys_platform == '{sys_platform}'")
|
||||
} else {
|
||||
format!("sys_platform == '{sys_platform}' and platform_machine == '{platform_machine}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for WheelTagHint {
|
||||
|
|
@ -5517,9 +5544,11 @@ impl std::fmt::Display for WheelTagHint {
|
|||
version,
|
||||
tags,
|
||||
best,
|
||||
markers,
|
||||
} => {
|
||||
let s = if tags.len() == 1 { "" } else { "s" };
|
||||
if let Some(best) = best {
|
||||
let example_marker = Self::suggest_environment_marker(markers);
|
||||
let best = if let Some(pretty) = best.pretty() {
|
||||
format!("{} (`{}`)", pretty.cyan(), best.cyan())
|
||||
} else {
|
||||
|
|
@ -5530,9 +5559,9 @@ impl std::fmt::Display for WheelTagHint {
|
|||
} else {
|
||||
format!("`{}`", package.cyan())
|
||||
};
|
||||
writeln!(
|
||||
write!(
|
||||
f,
|
||||
"{}{} You're on {}, but {} only has wheels for the following platform{s}: {}; consider adding your platform to `{}` to ensure uv resolves to a version with compatible wheels",
|
||||
"{}{} You're on {}, but {} only has wheels for the following platform{s}: {}; consider adding {} to `{}` to ensure uv resolves to a version with compatible wheels",
|
||||
"hint".bold().cyan(),
|
||||
":".bold(),
|
||||
best,
|
||||
|
|
@ -5540,6 +5569,7 @@ impl std::fmt::Display for WheelTagHint {
|
|||
tags.iter()
|
||||
.map(|tag| format!("`{}`", tag.cyan()))
|
||||
.join(", "),
|
||||
format!("\"{example_marker}\"").cyan(),
|
||||
"tool.uv.required-environments".green()
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1206,6 +1206,7 @@ impl ValidatedLock {
|
|||
dependency_metadata,
|
||||
indexes,
|
||||
interpreter.tags()?,
|
||||
interpreter.markers(),
|
||||
hasher,
|
||||
index,
|
||||
database,
|
||||
|
|
|
|||
|
|
@ -12077,8 +12077,12 @@ fn sync_required_environment_hint() -> Result<()> {
|
|||
r"You're on [^ ]+ \(`.*`\)",
|
||||
"You're on [PLATFORM] (`[TAG]`)",
|
||||
));
|
||||
filters.push((
|
||||
r"sys_platform == '[^']+' and platform_machine == '[^']+'",
|
||||
"sys_platform == '[PLATFORM]' and platform_machine == '[MACHINE]'",
|
||||
));
|
||||
|
||||
uv_snapshot!(filters, context.sync().env_remove(EnvVars::UV_EXCLUDE_NEWER), @r"
|
||||
uv_snapshot!(filters, context.sync().env_remove(EnvVars::UV_EXCLUDE_NEWER), @r#"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
|
@ -12087,8 +12091,8 @@ fn sync_required_environment_hint() -> Result<()> {
|
|||
Resolved 2 packages in [TIME]
|
||||
error: Distribution `no-sdist-no-wheels-with-matching-platform-a==1.0.0 @ registry+https://astral-sh.github.io/packse/PACKSE_VERSION/simple-html/` can't be installed because it doesn't have a source distribution or wheel for the current platform
|
||||
|
||||
hint: You're on [PLATFORM] (`[TAG]`), but `no-sdist-no-wheels-with-matching-platform-a` (v1.0.0) only has wheels for the following platform: `macosx_10_0_ppc64`; consider adding your platform to `tool.uv.required-environments` to ensure uv resolves to a version with compatible wheels
|
||||
");
|
||||
hint: You're on [PLATFORM] (`[TAG]`), but `no-sdist-no-wheels-with-matching-platform-a` (v1.0.0) only has wheels for the following platform: `macosx_10_0_ppc64`; consider adding "sys_platform == '[PLATFORM]' and platform_machine == '[MACHINE]'" to `tool.uv.required-environments` to ensure uv resolves to a version with compatible wheels
|
||||
"#);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue