Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show target Python version in error messages #10582

Merged
merged 5 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions crates/uv-platform-tags/src/abi_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ pub enum AbiTag {
Pyston { implementation_version: (u8, u8) },
}

impl AbiTag {
/// Return a pretty string representation of the ABI tag.
pub fn pretty(self) -> Option<String> {
match self {
AbiTag::None => None,
AbiTag::Abi3 => None,
AbiTag::CPython { python_version, .. } => {
Some(format!("CPython {}.{}", python_version.0, python_version.1))
}
AbiTag::PyPy { python_version, .. } => {
Some(format!("PyPy {}.{}", python_version.0, python_version.1))
}
AbiTag::GraalPy { python_version, .. } => {
Some(format!("GraalPy {}.{}", python_version.0, python_version.1))
}
AbiTag::Pyston { .. } => Some("Pyston".to_string()),
}
}
}

impl std::fmt::Display for AbiTag {
/// Format an [`AbiTag`] as a string.
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Expand Down
28 changes: 28 additions & 0 deletions crates/uv-platform-tags/src/language_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ pub enum LanguageTag {
Pyston { python_version: (u8, u8) },
}

impl LanguageTag {
/// Return a pretty string representation of the language tag.
pub fn pretty(self) -> Option<String> {
match self {
Self::None => None,
Self::Python { major, minor } => {
if let Some(minor) = minor {
Some(format!("Python {major}.{minor}"))
} else {
Some(format!("Python {major}"))
}
}
Self::CPython {
python_version: (major, minor),
} => Some(format!("CPython {major}.{minor}")),
Self::PyPy {
python_version: (major, minor),
} => Some(format!("PyPy {major}.{minor}")),
Self::GraalPy {
python_version: (major, minor),
} => Some(format!("GraalPy {major}.{minor}")),
Self::Pyston {
python_version: (major, minor),
} => Some(format!("Pyston {major}.{minor}")),
}
}
}

impl std::fmt::Display for LanguageTag {
/// Format a [`LanguageTag`] as a string.
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Expand Down
27 changes: 27 additions & 0 deletions crates/uv-platform-tags/src/platform_tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,33 @@ pub enum PlatformTag {
Solaris { release_arch: SmallString },
}

impl PlatformTag {
/// Return a pretty string representation of the language tag.
pub fn pretty(&self) -> Option<&'static str> {
match self {
PlatformTag::Any => None,
PlatformTag::Manylinux { .. } => Some("Linux"),
PlatformTag::Manylinux1 { .. } => Some("Linux"),
PlatformTag::Manylinux2010 { .. } => Some("Linux"),
PlatformTag::Manylinux2014 { .. } => Some("Linux"),
PlatformTag::Linux { .. } => Some("Linux"),
PlatformTag::Musllinux { .. } => Some("Linux"),
PlatformTag::Macos { .. } => Some("macOS"),
PlatformTag::Win32 => Some("Windows"),
PlatformTag::WinAmd64 => Some("Windows"),
PlatformTag::WinArm64 => Some("Windows"),
PlatformTag::Android { .. } => Some("Android"),
PlatformTag::FreeBsd { .. } => Some("FreeBSD"),
PlatformTag::NetBsd { .. } => Some("NetBSD"),
PlatformTag::OpenBsd { .. } => Some("OpenBSD"),
PlatformTag::Dragonfly { .. } => Some("DragonFly"),
PlatformTag::Haiku { .. } => Some("Haiku"),
PlatformTag::Illumos { .. } => Some("Illumos"),
PlatformTag::Solaris { .. } => Some("Solaris"),
}
}
}

impl PlatformTag {
/// Returns `true` if the platform is manylinux-only.
pub fn is_manylinux(&self) -> bool {
Expand Down
1 change: 1 addition & 0 deletions crates/uv-resolver/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ impl std::fmt::Display for NoSolutionError {
&self.fork_urls,
&self.fork_indexes,
&self.env,
self.tags.as_ref(),
&self.workspace_members,
&self.options,
&mut additional_hints,
Expand Down
106 changes: 81 additions & 25 deletions crates/uv-resolver/src/pubgrub/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ impl PubGrubReportFormatter<'_> {
fork_urls: &ForkUrls,
fork_indexes: &ForkIndexes,
env: &ResolverEnvironment,
tags: Option<&Tags>,
workspace_members: &BTreeSet<PackageName>,
options: &Options,
output_hints: &mut IndexSet<PubGrubHint>,
Expand Down Expand Up @@ -591,6 +592,7 @@ impl PubGrubReportFormatter<'_> {
selector,
fork_indexes,
env,
tags,
) {
output_hints.insert(hint);
}
Expand Down Expand Up @@ -686,6 +688,7 @@ impl PubGrubReportFormatter<'_> {
fork_urls,
fork_indexes,
env,
tags,
workspace_members,
options,
output_hints,
Expand All @@ -702,6 +705,7 @@ impl PubGrubReportFormatter<'_> {
fork_urls,
fork_indexes,
env,
tags,
workspace_members,
options,
output_hints,
Expand All @@ -721,6 +725,7 @@ impl PubGrubReportFormatter<'_> {
selector: &CandidateSelector,
fork_indexes: &ForkIndexes,
env: &ResolverEnvironment,
tags: Option<&Tags>,
) -> Option<PubGrubHint> {
let response = if let Some(url) = fork_indexes.get(name) {
index.explicit().get(&(name.clone(), url.clone()))
Expand All @@ -739,7 +744,7 @@ impl PubGrubReportFormatter<'_> {
match tag {
IncompatibleTag::Invalid => None,
IncompatibleTag::Python => {
// Return all available language tags.
let best = tags.and_then(Tags::python_tag);
let tags = prioritized.python_tags();
if tags.is_empty() {
None
Expand All @@ -748,10 +753,12 @@ impl PubGrubReportFormatter<'_> {
package: name.clone(),
version: candidate.version().clone(),
tags,
best,
})
}
}
IncompatibleTag::Abi | IncompatibleTag::AbiPythonVersion => {
let best = tags.and_then(Tags::abi_tag);
let tags = prioritized
.abi_tags()
.into_iter()
Expand All @@ -772,6 +779,7 @@ impl PubGrubReportFormatter<'_> {
package: name.clone(),
version: candidate.version().clone(),
tags,
best,
})
}
}
Expand Down Expand Up @@ -1105,6 +1113,8 @@ pub(crate) enum PubGrubHint {
version: Version,
// excluded from `PartialEq` and `Hash`
tags: BTreeSet<LanguageTag>,
// excluded from `PartialEq` and `Hash`
best: Option<LanguageTag>,
},
/// No wheels are available for a package, and using source distributions was disabled.
AbiTags {
Expand All @@ -1113,6 +1123,8 @@ pub(crate) enum PubGrubHint {
version: Version,
// excluded from `PartialEq` and `Hash`
tags: BTreeSet<AbiTag>,
// excluded from `PartialEq` and `Hash`
best: Option<AbiTag>,
},
/// No wheels are available for a package, and using source distributions was disabled.
PlatformTags {
Expand Down Expand Up @@ -1562,37 +1574,81 @@ impl std::fmt::Display for PubGrubHint {
package,
version,
tags,
best,
} => {
let s = if tags.len() == 1 { "" } else { "s" };
write!(
f,
"{}{} Wheels are available for `{}` ({}) with the following Python tag{s}: {}",
"hint".bold().cyan(),
":".bold(),
package.cyan(),
format!("v{version}").cyan(),
tags.iter()
.map(|tag| format!("`{}`", tag.cyan()))
.join(", "),
)
if let Some(best) = best {
let s = if tags.len() == 1 { "" } else { "s" };
let best = if let Some(pretty) = best.pretty() {
format!("{} (`{}`)", pretty.cyan(), best.cyan())
} else {
format!("{}", best.cyan())
};
write!(
f,
"{}{} You require {}, but we only found wheels for `{}` ({}) with the following Python implementation tag{s}: {}",
"hint".bold().cyan(),
":".bold(),
best,
package.cyan(),
format!("v{version}").cyan(),
tags.iter()
.map(|tag| format!("`{}`", tag.cyan()))
.join(", "),
)
} else {
let s = if tags.len() == 1 { "" } else { "s" };
write!(
f,
"{}{} Wheels are available for `{}` ({}) with the following Python implementation tag{s}: {}",
"hint".bold().cyan(),
":".bold(),
package.cyan(),
format!("v{version}").cyan(),
tags.iter()
.map(|tag| format!("`{}`", tag.cyan()))
.join(", "),
)
}
}
Self::AbiTags {
package,
version,
tags,
best,
} => {
let s = if tags.len() == 1 { "" } else { "s" };
write!(
f,
"{}{} Wheels are available for `{}` ({}) with the following ABI tag{s}: {}",
"hint".bold().cyan(),
":".bold(),
package.cyan(),
format!("v{version}").cyan(),
tags.iter()
.map(|tag| format!("`{}`", tag.cyan()))
.join(", "),
)
if let Some(best) = best {
let s = if tags.len() == 1 { "" } else { "s" };
let best = if let Some(pretty) = best.pretty() {
format!("{} (`{}`)", pretty.cyan(), best.cyan())
} else {
format!("{}", best.cyan())
};
write!(
f,
"{}{} You require {}, but we only found wheels for `{}` ({}) with the following Python ABI tag{s}: {}",
"hint".bold().cyan(),
":".bold(),
best,
package.cyan(),
format!("v{version}").cyan(),
tags.iter()
.map(|tag| format!("`{}`", tag.cyan()))
.join(", "),
)
} else {
let s = if tags.len() == 1 { "" } else { "s" };
write!(
f,
"{}{} Wheels are available for `{}` ({}) with the following Python ABI tag{s}: {}",
"hint".bold().cyan(),
":".bold(),
package.cyan(),
format!("v{version}").cyan(),
tags.iter()
.map(|tag| format!("`{}`", tag.cyan()))
.join(", "),
)
}
}
Self::PlatformTags {
package,
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/tests/it/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6758,7 +6758,7 @@ fn lock_requires_python_no_wheels() -> Result<()> {
× No solution found when resolving dependencies:
╰─▶ Because dearpygui==1.9.1 has no wheels with a matching Python version tag (e.g., `cp312`) and your project depends on dearpygui==1.9.1, we can conclude that your project's requirements are unsatisfiable.

hint: Wheels are available for `dearpygui` (v1.9.1) with the following ABI tags: `cp37m`, `cp38`, `cp39`, `cp310`, `cp311`
hint: Wheels are available for `dearpygui` (v1.9.1) with the following Python ABI tags: `cp37m`, `cp38`, `cp39`, `cp310`, `cp311`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change for consistency with the rest of the error report.

Interesting we say "Python version tag" specifically above!

"###);

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion crates/uv/tests/it/pip_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13981,7 +13981,7 @@ fn invalid_platform() -> Result<()> {
╰─▶ Because only open3d<=0.18.0 is available and open3d<=0.15.2 has no wheels with a matching Python ABI tag (e.g., `cp310`), we can conclude that open3d<=0.15.2 cannot be used.
And because open3d>=0.16.0,<=0.18.0 has no wheels with a matching platform tag (e.g., `manylinux_2_17_x86_64`) and you require open3d, we can conclude that your requirements are unsatisfiable.

hint: Wheels are available for `open3d` (v0.15.2) with the following ABI tags: `cp36m`, `cp37m`, `cp38`, `cp39`
hint: You require CPython 3.10 (`cp310`), but we only found wheels for `open3d` (v0.15.2) with the following Python ABI tags: `cp36m`, `cp37m`, `cp38`, `cp39`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A concern here: is this valid in universal resolution?


hint: Wheels are available for `open3d` (v0.18.0) on the following platforms: `manylinux_2_27_aarch64`, `manylinux_2_27_x86_64`, `macosx_11_0_x86_64`, `macosx_13_0_arm64`, `win_amd64`
"###);
Expand Down
4 changes: 2 additions & 2 deletions crates/uv/tests/it/pip_install_scenarios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4091,7 +4091,7 @@ fn no_sdist_no_wheels_with_matching_abi() {
╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels with a matching Python ABI tag (e.g., `cp38`), we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that your requirements are unsatisfiable.

hint: Wheels are available for `package-a` (v1.0.0) with the following ABI tag: `graalpy310_graalpy240_310_native`
hint: You require CPython 3.8 (`cp38`), but we only found wheels for `package-a` (v1.0.0) with the following Python ABI tag: `graalpy310_graalpy240_310_native`
"###);

assert_not_installed(
Expand Down Expand Up @@ -4177,7 +4177,7 @@ fn no_sdist_no_wheels_with_matching_python() {
╰─▶ Because only package-a==1.0.0 is available and package-a==1.0.0 has no wheels with a matching Python implementation tag (e.g., `cp38`), we can conclude that all versions of package-a cannot be used.
And because you require package-a, we can conclude that your requirements are unsatisfiable.

hint: Wheels are available for `package-a` (v1.0.0) with the following Python tag: `graalpy310`
hint: You require CPython 3.8 (`cp38`), but we only found wheels for `package-a` (v1.0.0) with the following Python implementation tag: `graalpy310`
"###);

assert_not_installed(
Expand Down
Loading