From be9d1d5816cb7d63852f296c094d7d28adfc3514 Mon Sep 17 00:00:00 2001 From: konstin Date: Wed, 4 Dec 2024 20:35:03 +0100 Subject: [PATCH 1/6] Better build error messages Clearly delineate the part that's important (this is a build backend problem) from the internals (we called this hook). --- crates/uv-build-frontend/src/error.rs | 24 ++++++++--- crates/uv-build-frontend/src/lib.rs | 57 +++++++++++---------------- crates/uv/tests/it/build.rs | 6 ++- crates/uv/tests/it/edit.rs | 6 ++- crates/uv/tests/it/lock.rs | 12 ++++-- crates/uv/tests/it/pip_compile.rs | 3 +- crates/uv/tests/it/pip_install.rs | 12 ++++-- crates/uv/tests/it/sync.rs | 12 ++++-- crates/uv/tests/it/tool_install.rs | 1 + 9 files changed, 79 insertions(+), 54 deletions(-) diff --git a/crates/uv-build-frontend/src/error.rs b/crates/uv-build-frontend/src/error.rs index 10fdb12b56b14..eb8665da1799f 100644 --- a/crates/uv-build-frontend/src/error.rs +++ b/crates/uv-build-frontend/src/error.rs @@ -75,9 +75,9 @@ pub enum Error { Virtualenv(#[from] uv_virtualenv::Error), #[error("Failed to run `{0}`")] CommandFailed(PathBuf, #[source] io::Error), - #[error(transparent)] + #[error("The build backend returned an error. This likely means a problem with the package or your environment.")] BuildBackend(#[from] BuildBackendError), - #[error(transparent)] + #[error("The build backend returned an error. This likely means a problem with the package or your environment.")] MissingHeader(#[from] MissingHeaderError), #[error("Failed to build PATH for build script")] BuildScriptPath(#[source] env::JoinPathsError), @@ -416,7 +416,10 @@ mod test { assert!(matches!(err, Error::MissingHeader { .. })); // Unix uses exit status, Windows uses exit code. - let formatted = err.to_string().replace("exit status: ", "exit code: "); + let formatted = std::error::Error::source(&err) + .unwrap() + .to_string() + .replace("exit status: ", "exit code: "); let formatted = anstream::adapter::strip_str(&formatted); insta::assert_snapshot!(formatted, @r###" Failed building wheel through setup.py (exit code: 0) @@ -471,7 +474,10 @@ mod test { ); assert!(matches!(err, Error::MissingHeader { .. })); // Unix uses exit status, Windows uses exit code. - let formatted = err.to_string().replace("exit status: ", "exit code: "); + let formatted = std::error::Error::source(&err) + .unwrap() + .to_string() + .replace("exit status: ", "exit code: "); let formatted = anstream::adapter::strip_str(&formatted); insta::assert_snapshot!(formatted, @r###" Failed building wheel through setup.py (exit code: 0) @@ -516,7 +522,10 @@ mod test { ); assert!(matches!(err, Error::MissingHeader { .. })); // Unix uses exit status, Windows uses exit code. - let formatted = err.to_string().replace("exit status: ", "exit code: "); + let formatted = std::error::Error::source(&err) + .unwrap() + .to_string() + .replace("exit status: ", "exit code: "); let formatted = anstream::adapter::strip_str(&formatted); insta::assert_snapshot!(formatted, @r###" Failed building wheel through setup.py (exit code: 0) @@ -559,7 +568,10 @@ mod test { ); assert!(matches!(err, Error::MissingHeader { .. })); // Unix uses exit status, Windows uses exit code. - let formatted = err.to_string().replace("exit status: ", "exit code: "); + let formatted = std::error::Error::source(&err) + .unwrap() + .to_string() + .replace("exit status: ", "exit code: "); let formatted = anstream::adapter::strip_str(&formatted); insta::assert_snapshot!(formatted, @r###" Failed building wheel through setup.py (exit code: 0) diff --git a/crates/uv-build-frontend/src/lib.rs b/crates/uv-build-frontend/src/lib.rs index 05b1b2cfc965e..ae5755da0e8a7 100644 --- a/crates/uv-build-frontend/src/lib.rs +++ b/crates/uv-build-frontend/src/lib.rs @@ -858,8 +858,8 @@ async fn create_pep517_build_environment( if !output.status.success() { return Err(Error::from_command_output( format!( - "Build backend failed to determine requirements with `{}`", - format!("build_{build_kind}()").green() + "Call to `{}.build_{build_kind}()` failed", + pep517_backend.backend, ), &output, level, @@ -869,37 +869,28 @@ async fn create_pep517_build_environment( )); } - // Read the requirements from the output file. - let contents = fs_err::read(&outfile).map_err(|err| { - Error::from_command_output( - format!( - "Build backend failed to read requirements from `{}`: {err}", - format!("get_requires_for_build_{build_kind}").green(), - ), - &output, - level, - package_name, - package_version, - version_id, - ) - })?; - - // Deserialize the requirements from the output file. - let extra_requires: Vec> = - serde_json::from_slice::>>(&contents) - .map_err(|err| { - Error::from_command_output( - format!( - "Build backend failed to return requirements from `{}`: {err}", - format!("get_requires_for_build_{build_kind}").green(), - ), - &output, - level, - package_name, - package_version, - version_id, - ) - })?; + // Read and deserialize the requirements from the output file. + let read_requires_result = fs_err::read(&outfile) + .map_err(|err| err.to_string()) + .and_then(|contents| serde_json::from_slice(&contents).map_err(|err| err.to_string())); + let extra_requires: Vec> = match read_requires_result + { + Ok(extra_requires) => extra_requires, + Err(err) => { + return Err(Error::from_command_output( + format!( + "Build backend failed to return requirements from \ + `{}.get_requires_for_build_{build_kind}`: {err}", + pep517_backend.backend, + ), + &output, + level, + package_name, + package_version, + version_id, + )) + } + }; // If necessary, lower the requirements. let extra_requires = match source_strategy { diff --git a/crates/uv/tests/it/build.rs b/crates/uv/tests/it/build.rs index 3765b14d769d1..b6f961fc955e7 100644 --- a/crates/uv/tests/it/build.rs +++ b/crates/uv/tests/it/build.rs @@ -897,7 +897,8 @@ fn fail() -> Result<()> { from setuptools import setup IndentationError: unexpected indent × Failed to build `[TEMP_DIR]/project` - ╰─▶ Build backend failed to determine requirements with `build_sdist()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta.build_sdist()` failed (exit status: 1) "###); Ok(()) @@ -1345,7 +1346,8 @@ fn build_all_with_failure() -> Result<()> { Successfully built dist/member_a-0.1.0.tar.gz Successfully built dist/member_a-0.1.0-py3-none-any.whl × Failed to build `member-b @ [TEMP_DIR]/project/packages/member_b` - ╰─▶ Build backend failed to determine requirements with `build_sdist()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta.build_sdist()` failed (exit status: 1) Successfully built dist/project-0.1.0.tar.gz Successfully built dist/project-0.1.0-py3-none-any.whl "###); diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs index 6506b0cc9f264..48316de9f1ec1 100644 --- a/crates/uv/tests/it/edit.rs +++ b/crates/uv/tests/it/edit.rs @@ -5559,7 +5559,8 @@ fn fail_to_add_revert_project() -> Result<()> { ----- stderr ----- Resolved 3 packages in [TIME] × Failed to build `child @ file://[TEMP_DIR]/child` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -5668,7 +5669,8 @@ fn fail_to_edit_revert_project() -> Result<()> { ----- stderr ----- Resolved 3 packages in [TIME] × Failed to build `child @ file://[TEMP_DIR]/child` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 7ed90cb0673b8..24d572201cab5 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -18659,7 +18659,8 @@ fn lock_derivation_chain_prod() -> Result<()> { ----- stderr ----- × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -18717,7 +18718,8 @@ fn lock_derivation_chain_extra() -> Result<()> { ----- stderr ----- × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -18777,7 +18779,8 @@ fn lock_derivation_chain_group() -> Result<()> { ----- stderr ----- × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -18848,7 +18851,8 @@ fn lock_derivation_chain_extended() -> Result<()> { ----- stderr ----- × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index d0087ebe98f8c..23b216825f65d 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -13717,7 +13717,8 @@ fn compile_derivation_chain() -> Result<()> { ----- stderr ----- × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index 7f7104ea46e14..0a02936fde53c 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -267,7 +267,8 @@ dependencies = ["flask==1.0.x"] ----- stderr ----- × Failed to build `project @ file://[TEMP_DIR]/path_dep` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stdout] configuration error: `project.dependencies[0]` must be pep508 @@ -4020,6 +4021,7 @@ fn no_build_isolation() -> Result<()> { ----- stderr ----- × Failed to download and build `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) [stderr] @@ -4088,6 +4090,7 @@ fn respect_no_build_isolation_env_var() -> Result<()> { ----- stderr ----- × Failed to download and build `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) [stderr] @@ -7085,6 +7088,7 @@ fn install_build_isolation_package() -> Result<()> { ----- stderr ----- × Failed to download and build `iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz` + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) [stderr] @@ -7344,7 +7348,8 @@ fn sklearn() { ----- stderr ----- × Failed to download and build `sklearn==0.0.post12` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] The 'sklearn' PyPI package is deprecated, use 'scikit-learn' @@ -7400,7 +7405,8 @@ fn resolve_derivation_chain() -> Result<()> { ----- stderr ----- × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index 7f419b83e2f50..7a90fc6ea6448 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -762,6 +762,7 @@ fn sync_build_isolation_package() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] × Failed to download and build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) [stderr] @@ -853,6 +854,7 @@ fn sync_build_isolation_extra() -> Result<()> { ----- stderr ----- Resolved [N] packages in [TIME] × Failed to download and build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) [stderr] @@ -872,6 +874,7 @@ fn sync_build_isolation_extra() -> Result<()> { ----- stderr ----- Resolved [N] packages in [TIME] × Failed to download and build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) [stderr] @@ -4954,7 +4957,8 @@ fn sync_derivation_chain() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -5018,7 +5022,8 @@ fn sync_derivation_chain_extra() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -5084,7 +5089,8 @@ fn sync_derivation_chain_group() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] × Failed to download and build `wsgiref==0.1.2` - ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] Traceback (most recent call last): diff --git a/crates/uv/tests/it/tool_install.rs b/crates/uv/tests/it/tool_install.rs index 3323d8641c594..57cb5d018b760 100644 --- a/crates/uv/tests/it/tool_install.rs +++ b/crates/uv/tests/it/tool_install.rs @@ -1470,6 +1470,7 @@ fn tool_install_uninstallable() { ----- stderr ----- Resolved 1 package in [TIME] × Failed to download and build `pyenv==0.0.1` + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) [stdout] From e0bb63d6fea95000d9df9585e6882e583a33bd99 Mon Sep 17 00:00:00 2001 From: konstin Date: Thu, 5 Dec 2024 13:32:39 +0100 Subject: [PATCH 2/6] Special case build backend errors This does not modify the build error itself much, but it starts special casing build failures. Most of the implementation is working around the orphan rule, (this)error rules and trait rules. --- Cargo.lock | 3 +- crates/uv-build-frontend/Cargo.toml | 1 - crates/uv-build-frontend/src/error.rs | 33 +++++- crates/uv-build-frontend/src/lib.rs | 27 +++-- crates/uv-dispatch/Cargo.toml | 2 + crates/uv-dispatch/src/lib.rs | 66 ++++++++--- .../uv-distribution-types/src/dist_error.rs | 63 +++++++++- crates/uv-distribution/src/error.rs | 12 +- crates/uv-distribution/src/source/mod.rs | 6 +- crates/uv-installer/src/preparer.rs | 22 ++-- crates/uv-requirements/src/lib.rs | 30 +++-- crates/uv-resolver/src/resolver/mod.rs | 111 ++++++------------ crates/uv-types/src/traits.rs | 19 +-- crates/uv/src/commands/build_frontend.rs | 8 +- crates/uv/src/commands/venv.rs | 8 +- crates/uv/tests/it/lock.rs | 8 +- crates/uv/tests/it/pip_compile.rs | 2 +- crates/uv/tests/it/pip_install.rs | 10 +- crates/uv/tests/it/sync.rs | 12 +- crates/uv/tests/it/tool_install.rs | 2 +- docs/reference/build_failures.md | 37 +++--- 21 files changed, 303 insertions(+), 179 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d20151ffca5d..d8eeda569a956 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4595,7 +4595,6 @@ name = "uv-build-frontend" version = "0.0.1" dependencies = [ "anstream", - "anyhow", "fs-err 3.0.0", "indoc", "insta", @@ -4842,6 +4841,7 @@ dependencies = [ "futures", "itertools 0.13.0", "rustc-hash", + "thiserror 2.0.3", "tokio", "tracing", "uv-build-backend", @@ -4855,6 +4855,7 @@ dependencies = [ "uv-git", "uv-install-wheel", "uv-installer", + "uv-platform-tags", "uv-pypi-types", "uv-python", "uv-resolver", diff --git a/crates/uv-build-frontend/Cargo.toml b/crates/uv-build-frontend/Cargo.toml index a0e8fd89a5606..29e9dff0e47ea 100644 --- a/crates/uv-build-frontend/Cargo.toml +++ b/crates/uv-build-frontend/Cargo.toml @@ -30,7 +30,6 @@ uv-types = { workspace = true } uv-virtualenv = { workspace = true } anstream = { workspace = true } -anyhow = { workspace = true } fs-err = { workspace = true } indoc = { workspace = true } itertools = { workspace = true } diff --git a/crates/uv-build-frontend/src/error.rs b/crates/uv-build-frontend/src/error.rs index eb8665da1799f..612a937849b13 100644 --- a/crates/uv-build-frontend/src/error.rs +++ b/crates/uv-build-frontend/src/error.rs @@ -10,6 +10,7 @@ use regex::Regex; use thiserror::Error; use tracing::error; use uv_configuration::BuildOutput; +use uv_distribution_types::{AnyErrorBuild, IsBuildBackendError}; use uv_fs::Simplified; use uv_pep440::Version; use uv_pep508::PackageName; @@ -68,11 +69,12 @@ pub enum Error { #[error("Editable installs with setup.py legacy builds are unsupported, please specify a build backend in pyproject.toml")] EditableSetupPy, #[error("Failed to resolve requirements from {0}")] - RequirementsResolve(&'static str, #[source] anyhow::Error), + RequirementsResolve(&'static str, #[source] AnyErrorBuild), #[error("Failed to install requirements from {0}")] - RequirementsInstall(&'static str, #[source] anyhow::Error), + RequirementsInstall(&'static str, #[source] AnyErrorBuild), #[error("Failed to create temporary virtualenv")] Virtualenv(#[from] uv_virtualenv::Error), + // Build backend errors #[error("Failed to run `{0}`")] CommandFailed(PathBuf, #[source] io::Error), #[error("The build backend returned an error. This likely means a problem with the package or your environment.")] @@ -81,6 +83,33 @@ pub enum Error { MissingHeader(#[from] MissingHeaderError), #[error("Failed to build PATH for build script")] BuildScriptPath(#[source] env::JoinPathsError), + // For the convenience of typing `setup_build` properly. + #[error("Building source distributions for {0} is disabled")] + NoSourceDistBuild(PackageName), + #[error("Building source distributions is disabled")] + NoSourceDistBuilds, +} + +impl IsBuildBackendError for Error { + fn is_build_backend_error(&self) -> bool { + match self { + Self::Io(_) + | Self::Lowering(_) + | Self::InvalidSourceDist(_) + | Self::InvalidPyprojectTomlSyntax(_) + | Self::InvalidPyprojectTomlSchema(_) + | Self::EditableSetupPy + | Self::RequirementsResolve(_, _) + | Self::RequirementsInstall(_, _) + | Self::Virtualenv(_) + | Self::NoSourceDistBuild(_) + | Self::NoSourceDistBuilds => false, + Self::CommandFailed(_, _) + | Self::BuildBackend(_) + | Self::MissingHeader(_) + | Self::BuildScriptPath(_) => true, + } + } } #[derive(Debug)] diff --git a/crates/uv-build-frontend/src/lib.rs b/crates/uv-build-frontend/src/lib.rs index ae5755da0e8a7..59b1b1b65407b 100644 --- a/crates/uv-build-frontend/src/lib.rs +++ b/crates/uv-build-frontend/src/lib.rs @@ -29,7 +29,7 @@ use tracing::{debug, info_span, instrument, Instrument}; use uv_configuration::{BuildKind, BuildOutput, ConfigSettings, LowerBound, SourceStrategy}; use uv_distribution::BuildRequires; -use uv_distribution_types::{IndexLocations, Resolution}; +use uv_distribution_types::{AnyErrorBuild, IndexLocations, Resolution}; use uv_fs::{PythonExt, Simplified}; use uv_pep440::Version; use uv_pep508::PackageName; @@ -325,7 +325,7 @@ impl SourceBuild { build_context .install(&resolved_requirements, &venv) .await - .map_err(|err| Error::RequirementsInstall("`build-system.requires`", err))?; + .map_err(|err| Error::RequirementsInstall("`build-system.requires`", err.into()))?; } else { debug!("Proceeding without build isolation"); } @@ -423,7 +423,9 @@ impl SourceBuild { let resolved_requirements = build_context .resolve(&default_backend.requirements) .await - .map_err(|err| Error::RequirementsResolve("`setup.py` build", err))?; + .map_err(|err| { + Error::RequirementsResolve("`setup.py` build", err.into()) + })?; *resolution = Some(resolved_requirements.clone()); resolved_requirements } @@ -431,7 +433,9 @@ impl SourceBuild { build_context .resolve(&pep517_backend.requirements) .await - .map_err(|err| Error::RequirementsResolve("`build-system.requires`", err))? + .map_err(|err| { + Error::RequirementsResolve("`build-system.requires`", err.into()) + })? }, ) } @@ -776,11 +780,11 @@ impl SourceBuild { } impl SourceBuildTrait for SourceBuild { - async fn metadata(&mut self) -> anyhow::Result> { + async fn metadata(&mut self) -> Result, AnyErrorBuild> { Ok(self.get_metadata_without_build().await?) } - async fn wheel<'a>(&'a self, wheel_dir: &'a Path) -> anyhow::Result { + async fn wheel<'a>(&'a self, wheel_dir: &'a Path) -> Result { Ok(self.build(wheel_dir).await?) } } @@ -928,15 +932,16 @@ async fn create_pep517_build_environment( .cloned() .chain(extra_requires) .collect(); - let resolution = build_context - .resolve(&requirements) - .await - .map_err(|err| Error::RequirementsResolve("`build-system.requires`", err))?; + let resolution = build_context.resolve(&requirements).await.map_err(|err| { + Error::RequirementsResolve("`build-system.requires`", AnyErrorBuild::from(err)) + })?; build_context .install(&resolution, venv) .await - .map_err(|err| Error::RequirementsInstall("`build-system.requires`", err))?; + .map_err(|err| { + Error::RequirementsInstall("`build-system.requires`", AnyErrorBuild::from(err)) + })?; } Ok(()) diff --git a/crates/uv-dispatch/Cargo.toml b/crates/uv-dispatch/Cargo.toml index 3750ab24a3bee..07b0e1ab2c99a 100644 --- a/crates/uv-dispatch/Cargo.toml +++ b/crates/uv-dispatch/Cargo.toml @@ -28,6 +28,7 @@ uv-distribution-types = { workspace = true } uv-git = { workspace = true } uv-install-wheel = { workspace = true } uv-installer = { workspace = true } +uv-platform-tags = { workspace = true } uv-pypi-types = { workspace = true } uv-python = { workspace = true } uv-resolver = { workspace = true } @@ -38,5 +39,6 @@ anyhow = { workspace = true } futures = { workspace = true } itertools = { workspace = true } rustc-hash = { workspace = true } +thiserror = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 8620302505351..371e92cb24da6 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -5,10 +5,11 @@ use std::ffi::{OsStr, OsString}; use std::path::Path; -use anyhow::{anyhow, Context, Result}; +use anyhow::{Context, Result}; use futures::FutureExt; use itertools::Itertools; use rustc_hash::FxHashMap; +use thiserror::Error; use tracing::{debug, instrument, trace}; use uv_build_backend::check_direct_build; use uv_build_frontend::{SourceBuild, SourceBuildContext}; @@ -22,8 +23,8 @@ use uv_configuration::{BuildOutput, Concurrency}; use uv_distribution::DistributionDatabase; use uv_distribution_filename::DistFilename; use uv_distribution_types::{ - CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, Name, Resolution, - SourceDist, VersionOrUrlRef, + AnyErrorBuild, CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, + IsBuildBackendError, Name, Resolution, SourceDist, VersionOrUrlRef, }; use uv_git::GitResolver; use uv_installer::{Installer, Plan, Planner, Preparer, SitePackages}; @@ -35,6 +36,40 @@ use uv_resolver::{ }; use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight}; +#[derive(Debug, Error)] +pub enum BuildDispatchError { + #[error(transparent)] + BuildFrontend(#[from] AnyErrorBuild), + + #[error(transparent)] + Tags(#[from] uv_platform_tags::TagsError), + + #[error(transparent)] + Resolve(#[from] uv_resolver::ResolveError), + + #[error(transparent)] + Join(#[from] tokio::task::JoinError), + + #[error(transparent)] + Anyhow(#[from] anyhow::Error), + + #[error(transparent)] + Prepare(#[from] uv_installer::PrepareError), +} + +impl IsBuildBackendError for BuildDispatchError { + fn is_build_backend_error(&self) -> bool { + match self { + BuildDispatchError::Tags(_) + | BuildDispatchError::Resolve(_) + | BuildDispatchError::Join(_) + | BuildDispatchError::Anyhow(_) + | BuildDispatchError::Prepare(_) => false, + BuildDispatchError::BuildFrontend(err) => err.is_build_backend_error(), + } + } +} + /// The main implementation of [`BuildContext`], used by the CLI, see [`BuildContext`] /// documentation. pub struct BuildDispatch<'a> { @@ -124,6 +159,7 @@ impl<'a> BuildDispatch<'a> { } } +#[allow(refining_impl_trait)] impl<'a> BuildContext for BuildDispatch<'a> { type SourceDistBuilder = SourceBuild; @@ -167,7 +203,10 @@ impl<'a> BuildContext for BuildDispatch<'a> { self.index_locations } - async fn resolve<'data>(&'data self, requirements: &'data [Requirement]) -> Result { + async fn resolve<'data>( + &'data self, + requirements: &'data [Requirement], + ) -> Result { let python_requirement = PythonRequirement::from_interpreter(self.interpreter); let marker_env = self.interpreter.resolver_marker_environment(); let tags = self.interpreter.tags()?; @@ -215,7 +254,7 @@ impl<'a> BuildContext for BuildDispatch<'a> { &'data self, resolution: &'data Resolution, venv: &'data PythonEnvironment, - ) -> Result> { + ) -> Result, BuildDispatchError> { debug!( "Installing in {} in {}", resolution @@ -325,7 +364,7 @@ impl<'a> BuildContext for BuildDispatch<'a> { sources: SourceStrategy, build_kind: BuildKind, build_output: BuildOutput, - ) -> Result { + ) -> Result { let dist_name = dist.map(uv_distribution_types::Name::name); let dist_version = dist .map(uv_distribution_types::DistributionMetadata::version_or_url) @@ -342,13 +381,12 @@ impl<'a> BuildContext for BuildDispatch<'a> { // We always allow editable builds && !matches!(build_kind, BuildKind::Editable) { - if let Some(dist) = dist { - return Err(anyhow!( - "Building source distributions for {} is disabled", - dist.name() - )); - } - return Err(anyhow!("Building source distributions is disabled")); + let err = if let Some(dist) = dist { + uv_build_frontend::Error::NoSourceDistBuild(dist.name().clone()) + } else { + uv_build_frontend::Error::NoSourceDistBuilds + }; + return Err(err); } let builder = SourceBuild::setup( @@ -382,7 +420,7 @@ impl<'a> BuildContext for BuildDispatch<'a> { output_dir: &'data Path, build_kind: BuildKind, version_id: Option<&'data str>, - ) -> Result> { + ) -> Result, BuildDispatchError> { // Direct builds are a preview feature with the uv build backend. if self.preview.is_disabled() { trace!("Preview is disabled, not checking for direct build"); diff --git a/crates/uv-distribution-types/src/dist_error.rs b/crates/uv-distribution-types/src/dist_error.rs index e26462a7215a7..f68d7fe97c8f0 100644 --- a/crates/uv-distribution-types/src/dist_error.rs +++ b/crates/uv-distribution-types/src/dist_error.rs @@ -3,11 +3,72 @@ use petgraph::prelude::EdgeRef; use petgraph::Direction; use rustc_hash::FxHashSet; use std::collections::VecDeque; -use std::fmt::{Display, Formatter}; +use std::fmt::{Debug, Display, Formatter}; +use std::ops::Deref; use uv_normalize::{ExtraName, GroupName, PackageName}; use uv_pep440::Version; use version_ranges::Ranges; +/// Orphan-rule workaround for inspecting build frontend errors. +/// +/// Normally we would match on the variants, but we have to pass this error through an opaque type +/// to avoid cyclical crate dependencies between the build frontend and resolver/installer. +pub trait IsBuildBackendError: std::error::Error + Send + Sync + 'static { + /// Returns whether the build backend failed to build the package, so it's not a uv error. + fn is_build_backend_error(&self) -> bool; +} + +/// Wrapper type to make `thiserror`'s `AsDynError` work with `IsBuildFrontendError`. +/// +/// `thiserror` does not recognize `Box` as +/// error source by itself. +/// +/// TODO(konsti): The `From` blocks implementing `IsBuildBackendError` on the +/// type itself, so the wrapped +pub struct AnyErrorBuild(Box); + +impl Debug for AnyErrorBuild { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl Display for AnyErrorBuild { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl std::error::Error for AnyErrorBuild { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } + + #[allow(deprecated)] + fn description(&self) -> &str { + self.0.description() + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn std::error::Error> { + self.0.cause() + } +} + +impl From for AnyErrorBuild { + fn from(err: T) -> Self { + Self(Box::new(err)) + } +} + +impl Deref for AnyErrorBuild { + type Target = dyn IsBuildBackendError; + + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + /// The operation(s) that failed when reporting an error with a distribution. #[derive(Debug)] pub enum DistErrorKind { diff --git a/crates/uv-distribution/src/error.rs b/crates/uv-distribution/src/error.rs index 72ee339ad863e..5403d79feabca 100644 --- a/crates/uv-distribution/src/error.rs +++ b/crates/uv-distribution/src/error.rs @@ -8,6 +8,7 @@ use zip::result::ZipError; use crate::metadata::MetadataError; use uv_client::WrappedReqwestError; use uv_distribution_filename::WheelFilenameError; +use uv_distribution_types::{AnyErrorBuild, IsBuildBackendError}; use uv_fs::Simplified; use uv_normalize::PackageName; use uv_pep440::{Version, VersionSpecifiers}; @@ -52,7 +53,7 @@ pub enum Error { // Build error #[error(transparent)] - Build(anyhow::Error), + Build(AnyErrorBuild), #[error("Built wheel has an invalid filename")] WheelFilename(#[from] WheelFilenameError), #[error("Package metadata name `{metadata}` does not match given name `{given}`")] @@ -172,6 +173,15 @@ impl From for Error { } } +impl IsBuildBackendError for Error { + fn is_build_backend_error(&self) -> bool { + match self { + Self::Build(err) => err.is_build_backend_error(), + _ => false, + } + } +} + impl Error { /// Construct a hash mismatch error. pub fn hash_mismatch( diff --git a/crates/uv-distribution/src/source/mod.rs b/crates/uv-distribution/src/source/mod.rs index 8df51913f75bd..5382825bc8cc0 100644 --- a/crates/uv-distribution/src/source/mod.rs +++ b/crates/uv-distribution/src/source/mod.rs @@ -1818,7 +1818,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { Some(&source.to_string()), ) .await - .map_err(Error::Build)? + .map_err(|err| Error::Build(err.into()))? { // In the uv build backend, the normalized filename and the disk filename are the same. name.to_string() @@ -1839,7 +1839,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { BuildOutput::Debug, ) .await - .map_err(Error::Build)? + .map_err(|err| Error::Build(err.into()))? .wheel(temp_dir.path()) .await .map_err(Error::Build)? @@ -1915,7 +1915,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> { BuildOutput::Debug, ) .await - .map_err(Error::Build)?; + .map_err(|err| Error::Build(err.into()))?; // Build the metadata. let dist_info = builder.metadata().await.map_err(Error::Build)?; diff --git a/crates/uv-installer/src/preparer.rs b/crates/uv-installer/src/preparer.rs index 2093162acc32a..ea97d4d95ea57 100644 --- a/crates/uv-installer/src/preparer.rs +++ b/crates/uv-installer/src/preparer.rs @@ -9,8 +9,8 @@ use uv_cache::Cache; use uv_configuration::BuildOptions; use uv_distribution::{DistributionDatabase, LocalWheel}; use uv_distribution_types::{ - BuildableSource, CachedDist, DerivationChain, Dist, DistErrorKind, Hashed, Identifier, Name, - RemoteSource, Resolution, + BuildableSource, CachedDist, DerivationChain, Dist, DistErrorKind, Hashed, Identifier, + IsBuildBackendError, Name, RemoteSource, Resolution, }; use uv_pep508::PackageName; use uv_platform_tags::Tags; @@ -227,13 +227,17 @@ pub enum Error { impl Error { /// Create an [`Error`] from a distribution error. fn from_dist(dist: Dist, cause: uv_distribution::Error, resolution: &Resolution) -> Self { - let kind = match &dist { - Dist::Built(_) => DistErrorKind::Download, - Dist::Source(dist) => { - if dist.is_local() { - DistErrorKind::Build - } else { - DistErrorKind::DownloadAndBuild + let kind = if cause.is_build_backend_error() { + DistErrorKind::BuildBackend + } else { + match &dist { + Dist::Built(_) => DistErrorKind::Download, + Dist::Source(dist) => { + if dist.is_local() { + DistErrorKind::Build + } else { + DistErrorKind::DownloadAndBuild + } } } }; diff --git a/crates/uv-requirements/src/lib.rs b/crates/uv-requirements/src/lib.rs index 3c2c15f6a9a91..358ff313feda4 100644 --- a/crates/uv-requirements/src/lib.rs +++ b/crates/uv-requirements/src/lib.rs @@ -5,7 +5,9 @@ pub use crate::sources::*; pub use crate::specification::*; pub use crate::unnamed::*; -use uv_distribution_types::{Dist, DistErrorKind, GitSourceDist, SourceDist}; +use uv_distribution_types::{ + Dist, DistErrorKind, GitSourceDist, IsBuildBackendError, SourceDist, +}; use uv_git::GitUrl; use uv_pypi_types::{Requirement, RequirementSource}; @@ -35,19 +37,21 @@ pub enum Error { impl Error { /// Create an [`Error`] from a distribution error. pub(crate) fn from_dist(dist: Dist, cause: uv_distribution::Error) -> Self { - match dist { - Dist::Built(dist) => { - Self::Dist(DistErrorKind::Download, Box::new(Dist::Built(dist)), cause) + let kind = if cause.is_build_backend_error() { + DistErrorKind::BuildBackend + } else { + match &dist { + Dist::Built(_) => DistErrorKind::Download, + Dist::Source(dist) => { + if dist.is_local() { + DistErrorKind::Build + } else { + DistErrorKind::DownloadAndBuild + } + } } - Dist::Source(dist) => { - let kind = if dist.is_local() { - DistErrorKind::Build - } else { - DistErrorKind::DownloadAndBuild - }; - Self::Dist(kind, Box::new(Dist::Source(dist)), cause) - } - } + }; + Self::Dist(kind, Box::new(dist), DerivationChain::default(), cause) } } diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index f8ee9441f0d3e..c82f574a17acf 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -25,8 +25,8 @@ use uv_distribution::{ArchiveMetadata, DistributionDatabase}; use uv_distribution_types::{ BuiltDist, CompatibleDist, DerivationChain, Dist, DistErrorKind, DistributionMetadata, IncompatibleDist, IncompatibleSource, IncompatibleWheel, IndexCapabilities, IndexLocations, - IndexUrl, InstalledDist, PythonRequirementKind, RemoteSource, ResolvedDist, ResolvedDistRef, - SourceDist, VersionOrUrlRef, + IndexUrl, InstalledDist, IsBuildBackendError, PythonRequirementKind, RemoteSource, + ResolvedDist, ResolvedDistRef, SourceDist, VersionOrUrlRef, }; use uv_git::GitResolver; use uv_normalize::{ExtraName, GroupName, PackageName}; @@ -956,36 +956,29 @@ impl ResolverState { // TODO(charlie): Add derivation chain for URL dependencies. In practice, this isn't // critical since we fetch URL dependencies _prior_ to invoking the resolver. - let chain = DerivationChain::default(); - let (kind, dist) = match &**dist { - Dist::Built(built_dist @ BuiltDist::Path(_)) => { - (DistErrorKind::Read, Dist::Built(built_dist.clone())) - } - Dist::Source(source_dist @ SourceDist::Path(_)) => { - (DistErrorKind::Build, Dist::Source(source_dist.clone())) - } - Dist::Source(source_dist @ SourceDist::Directory(_)) => { - (DistErrorKind::Build, Dist::Source(source_dist.clone())) - } - Dist::Built(built_dist) => { - (DistErrorKind::Download, Dist::Built(built_dist.clone())) - } - Dist::Source(source_dist) => { - if source_dist.is_local() { - (DistErrorKind::Build, Dist::Source(source_dist.clone())) - } else { - ( - DistErrorKind::DownloadAndBuild, - Dist::Source(source_dist.clone()), - ) + let kind = if err.is_build_backend_error() { + DistErrorKind::BuildBackend + } else { + match &**dist { + Dist::Built(BuiltDist::Path(_)) => DistErrorKind::Read, + Dist::Source(SourceDist::Path(_) | SourceDist::Directory(_)) => { + DistErrorKind::Build + } + Dist::Built(_) => DistErrorKind::Download, + Dist::Source(source_dist) => { + if source_dist.is_local() { + DistErrorKind::Build + } else { + DistErrorKind::DownloadAndBuild + } } } }; return Err(ResolveError::Dist( kind, - Box::new(dist), - chain, - (*err).clone(), + dist.clone(), + DerivationChain::default(), + err.clone(), )); } }; @@ -1398,36 +1391,25 @@ impl ResolverState { let chain = DerivationChainBuilder::from_state(id, version, pubgrub) .unwrap_or_default(); - let (kind, dist) = match &**dist { - Dist::Built(built_dist @ BuiltDist::Path(_)) => { - (DistErrorKind::Read, Dist::Built(built_dist.clone())) - } - Dist::Source(source_dist @ SourceDist::Path(_)) => { - (DistErrorKind::Build, Dist::Source(source_dist.clone())) - } - Dist::Source(source_dist @ SourceDist::Directory(_)) => { - (DistErrorKind::Build, Dist::Source(source_dist.clone())) - } - Dist::Built(built_dist) => { - (DistErrorKind::Download, Dist::Built(built_dist.clone())) - } - Dist::Source(source_dist) => { - if source_dist.is_local() { - (DistErrorKind::Build, Dist::Source(source_dist.clone())) - } else { - ( - DistErrorKind::DownloadAndBuild, - Dist::Source(source_dist.clone()), - ) + let kind = if err.is_build_backend_error() { + DistErrorKind::BuildBackend + } else { + match &**dist { + Dist::Built(BuiltDist::Path(_)) => DistErrorKind::Read, + Dist::Source(SourceDist::Path(_) | SourceDist::Directory(_)) => { + DistErrorKind::Build + } + Dist::Built(_) => DistErrorKind::Download, + Dist::Source(source_dist) => { + if source_dist.is_local() { + DistErrorKind::Build + } else { + DistErrorKind::DownloadAndBuild + } } } }; - return Err(ResolveError::Dist( - kind, - Box::new(dist), - chain, - (*err).clone(), - )); + return Err(ResolveError::Dist(kind, dist.clone(), chain, err.clone())); } }; @@ -2261,11 +2243,11 @@ impl ForkState { for_version: &Version, urls: &Urls, indexes: &Indexes, - dependencies: Vec, + mut dependencies: Vec, git: &GitResolver, resolution_strategy: &ResolutionStrategy, ) -> Result<(), ResolveError> { - for dependency in &dependencies { + for dependency in &mut dependencies { let PubGrubDependency { package, version, @@ -2297,22 +2279,6 @@ impl ForkState { // A dependency from the root package or requirements.txt. debug!("Adding direct dependency: {package}{version}"); - let name = package.name_no_root().unwrap(); - - // Catch cases where we pass a package once by name with extras and then once as - // URL for the specific distribution. - has_url = has_url - || dependencies - .iter() - .filter(|other_dep| *other_dep != dependency) - .filter(|other_dep| { - other_dep - .package - .name() - .is_some_and(|other_name| other_name == name) - }) - .any(|other_dep| other_dep.url.is_some()); - // Warn the user if a direct dependency lacks a lower bound in `--lowest` resolution. let missing_lower_bound = version .bounding_range() @@ -2327,6 +2293,7 @@ impl ForkState { "The direct dependency `{name}` is unpinned. \ Consider setting a lower bound when using `--resolution lowest` \ to avoid using outdated versions.", + name = package.name_no_root().unwrap(), ); } } diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index 68c17258a0a4e..73bead2bf8cf7 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -9,8 +9,8 @@ use uv_configuration::{ BuildKind, BuildOptions, BuildOutput, ConfigSettings, LowerBound, SourceStrategy, }; use uv_distribution_types::{ - CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, InstalledDist, Resolution, - SourceDist, + AnyErrorBuild, CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, + InstalledDist, IsBuildBackendError, Resolution, SourceDist, }; use uv_git::GitResolver; use uv_pep508::PackageName; @@ -94,7 +94,7 @@ pub trait BuildContext { fn resolve<'a>( &'a self, requirements: &'a [Requirement], - ) -> impl Future> + 'a; + ) -> impl Future> + 'a; /// Install the given set of package versions into the virtual environment. The environment must /// use the same base Python as [`BuildContext::interpreter`] @@ -102,7 +102,7 @@ pub trait BuildContext { &'a self, resolution: &'a Resolution, venv: &'a PythonEnvironment, - ) -> impl Future>> + 'a; + ) -> impl Future, impl IsBuildBackendError>> + 'a; /// Set up a source distribution build by installing the required dependencies. A wrapper for /// `uv_build::SourceBuild::setup`. @@ -121,7 +121,7 @@ pub trait BuildContext { sources: SourceStrategy, build_kind: BuildKind, build_output: BuildOutput, - ) -> impl Future> + 'a; + ) -> impl Future> + 'a; /// Build by calling directly into the uv build backend without PEP 517, if possible. /// @@ -136,7 +136,7 @@ pub trait BuildContext { output_dir: &'a Path, build_kind: BuildKind, version_id: Option<&'a str>, - ) -> impl Future>> + 'a; + ) -> impl Future, impl IsBuildBackendError>> + 'a; } /// A wrapper for `uv_build::SourceBuild` to avoid cyclical crate dependencies. @@ -150,7 +150,7 @@ pub trait SourceBuildTrait { /// /// Returns the metadata directory if we're having a PEP 517 build and the /// `prepare_metadata_for_build_wheel` hook exists - fn metadata(&mut self) -> impl Future>>; + fn metadata(&mut self) -> impl Future, AnyErrorBuild>>; /// A wrapper for `uv_build::SourceBuild::build`. /// @@ -159,7 +159,10 @@ pub trait SourceBuildTrait { /// Returns the filename of the built wheel inside the given `wheel_dir`. The filename is a /// string and not a `WheelFilename` because the on disk filename might not be normalized in the /// same way as uv would. - fn wheel<'a>(&'a self, wheel_dir: &'a Path) -> impl Future> + 'a; + fn wheel<'a>( + &'a self, + wheel_dir: &'a Path, + ) -> impl Future> + 'a; } /// A wrapper for [`uv_installer::SitePackages`] diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 2fa5f1d5c7809..71cb2761fa204 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -29,7 +29,7 @@ use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution_filename::{ DistFilename, SourceDistExtension, SourceDistFilename, WheelFilename, }; -use uv_distribution_types::{DependencyMetadata, Index, IndexLocations, SourceDist}; +use uv_distribution_types::{AnyErrorBuild, DependencyMetadata, Index, IndexLocations, SourceDist}; use uv_fs::{relative_to, Simplified}; use uv_install_wheel::linker::LinkMode; use uv_normalize::PackageName; @@ -66,7 +66,7 @@ enum Error { #[error(transparent)] BuildBackend(#[from] uv_build_backend::Error), #[error(transparent)] - BuildDispatch(anyhow::Error), + BuildDispatch(AnyErrorBuild), #[error(transparent)] BuildFrontend(#[from] uv_build_frontend::Error), #[error("Failed to write message")] @@ -922,7 +922,7 @@ async fn build_sdist( build_output, ) .await - .map_err(Error::BuildDispatch)?; + .map_err(|err| Error::BuildDispatch(err.into()))?; let filename = builder.build(output_dir).await?; BuildMessage::Build { filename: DistFilename::SourceDistFilename( @@ -1019,7 +1019,7 @@ async fn build_wheel( build_output, ) .await - .map_err(Error::BuildDispatch)?; + .map_err(|err| Error::BuildDispatch(err.into()))?; let filename = builder.build(output_dir).await?; BuildMessage::Build { filename: DistFilename::WheelFilename( diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index f9f6b1698dfd0..3f0a773543068 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -16,7 +16,7 @@ use uv_configuration::{ LowerBound, NoBinary, NoBuild, PreviewMode, SourceStrategy, TrustedHost, }; use uv_dispatch::{BuildDispatch, SharedState}; -use uv_distribution_types::{DependencyMetadata, Index, IndexLocations}; +use uv_distribution_types::{AnyErrorBuild, DependencyMetadata, Index, IndexLocations}; use uv_fs::Simplified; use uv_install_wheel::linker::LinkMode; use uv_pypi_types::Requirement; @@ -113,7 +113,7 @@ enum VenvError { #[error("Failed to install seed packages")] #[diagnostic(code(uv::venv::seed))] - Seed(#[source] anyhow::Error), + Seed(#[source] AnyErrorBuild), #[error("Failed to extract interpreter tags")] #[diagnostic(code(uv::venv::tags))] @@ -360,11 +360,11 @@ async fn venv_impl( let resolution = build_dispatch .resolve(&requirements) .await - .map_err(VenvError::Seed)?; + .map_err(|err| VenvError::Seed(err.into()))?; let installed = build_dispatch .install(&resolution, &venv) .await - .map_err(VenvError::Seed)?; + .map_err(|err| VenvError::Seed(err.into()))?; let changelog = Changelog::from_installed(installed); DefaultInstallLogger diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 24d572201cab5..7d8cb53942442 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -18658,7 +18658,7 @@ fn lock_derivation_chain_prod() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) @@ -18717,7 +18717,7 @@ fn lock_derivation_chain_extra() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) @@ -18778,7 +18778,7 @@ fn lock_derivation_chain_group() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) @@ -18850,7 +18850,7 @@ fn lock_derivation_chain_extended() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 23b216825f65d..5d3b360175863 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -13716,7 +13716,7 @@ fn compile_derivation_chain() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index 0a02936fde53c..f181a990bdca7 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -4020,7 +4020,7 @@ fn no_build_isolation() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` + × Failed to build `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) @@ -4089,7 +4089,7 @@ fn respect_no_build_isolation_env_var() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` + × Failed to build `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) @@ -7087,7 +7087,7 @@ fn install_build_isolation_package() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz` + × Failed to build `iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) @@ -7347,7 +7347,7 @@ fn sklearn() { ----- stdout ----- ----- stderr ----- - × Failed to download and build `sklearn==0.0.post12` + × Failed to build `sklearn==0.0.post12` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) @@ -7404,7 +7404,7 @@ fn resolve_derivation_chain() -> Result<()> { ----- stdout ----- ----- stderr ----- - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index 7a90fc6ea6448..b43e66359fe3c 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -761,7 +761,7 @@ fn sync_build_isolation_package() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] - × Failed to download and build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` + × Failed to build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) @@ -853,7 +853,7 @@ fn sync_build_isolation_extra() -> Result<()> { ----- stderr ----- Resolved [N] packages in [TIME] - × Failed to download and build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` + × Failed to build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) @@ -873,7 +873,7 @@ fn sync_build_isolation_extra() -> Result<()> { ----- stderr ----- Resolved [N] packages in [TIME] - × Failed to download and build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` + × Failed to build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) @@ -4956,7 +4956,7 @@ fn sync_derivation_chain() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) @@ -5021,7 +5021,7 @@ fn sync_derivation_chain_extra() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) @@ -5088,7 +5088,7 @@ fn sync_derivation_chain_group() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] - × Failed to download and build `wsgiref==0.1.2` + × Failed to build `wsgiref==0.1.2` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) diff --git a/crates/uv/tests/it/tool_install.rs b/crates/uv/tests/it/tool_install.rs index 57cb5d018b760..57cfb68baa727 100644 --- a/crates/uv/tests/it/tool_install.rs +++ b/crates/uv/tests/it/tool_install.rs @@ -1469,7 +1469,7 @@ fn tool_install_uninstallable() { ----- stderr ----- Resolved 1 package in [TIME] - × Failed to download and build `pyenv==0.0.1` + × Failed to build `pyenv==0.0.1` ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) diff --git a/docs/reference/build_failures.md b/docs/reference/build_failures.md index d9a7922840877..a6d625947b9b0 100644 --- a/docs/reference/build_failures.md +++ b/docs/reference/build_failures.md @@ -12,22 +12,23 @@ unsupported version of Python: ```console $ uv pip install -p 3.13 'numpy<1.20' Resolved 1 package in 62ms - × Failed to download and build `numpy==1.19.5` - ├─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) - - │ [stderr] - │ Traceback (most recent call last): - │ File "", line 8, in - │ from setuptools.build_meta import __legacy__ as backend - │ File "/Users/example/.cache/uv/builds-v0/.tmp96A0WB/lib/python3.13/site-packages/setuptools/__init__.py", line 9, in - │ import distutils.core - │ ModuleNotFoundError: No module named 'distutils' - - ╰─▶ distutils was removed from the standard library in Python 3.12. Consider adding a constraint - (like `numpy >1.19.5`) to avoid building a version of numpy that depends on distutils. + × Failed to build `numpy==1.19.5` + ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + + [stderr] + Traceback (most recent call last): + File "", line 8, in + from setuptools.build_meta import __legacy__ as backend + File "/home/konsti/.cache/uv/builds-v0/.tmpi4bgKb/lib/python3.13/site-packages/setuptools/__init__.py", line 9, in + import distutils.core + ModuleNotFoundError: No module named 'distutils' + + hint: `distutils` was removed from the standard library in Python 3.12. Consider adding a constraint (like `numpy >1.19.5`) to avoid building a version of `numpy` that depends + on `distutils`. ``` -Notice that the error message is prefaced by "Build backend failed to determine requirements". +Notice that the error message is prefaced by "The build backend returned an error". The build failure includes the `[stderr]` (and `[stdout]`, if present) from the build backend that was used for the build. The error logs are not from uv itself. @@ -118,7 +119,7 @@ If the build error mentions a missing command, for example, `gcc`: ```hl_lines="17" -× Failed to download and build `pysha3==1.0.2` +× Failed to build `pysha3==1.0.2` ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) [stdout] @@ -165,7 +166,7 @@ For example, installing `pygraphviz` requires Graphviz to be installed: ```hl_lines="18-19" -× Failed to download and build `pygraphviz==1.14` +× Failed to build `pygraphviz==1.14` ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) [stdout] @@ -215,7 +216,7 @@ dependency: ```hl_lines="7" -× Failed to download and build `chumpy==0.70` +× Failed to build `chumpy==0.70` ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) [stderr] @@ -270,7 +271,7 @@ apache-beam<=2.49.0 ```hl_lines="1" -× Failed to download and build `apache-beam==2.0.0` +× Failed to build `apache-beam==2.0.0` ╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) [stderr] From b67c2ef054445af8ccf57e52df36c0ccb73f7653 Mon Sep 17 00:00:00 2001 From: konstin Date: Fri, 6 Dec 2024 11:56:53 +0100 Subject: [PATCH 3/6] More consistent errors --- crates/uv-build-frontend/src/error.rs | 11 ++++-- crates/uv-build-frontend/src/lib.rs | 23 ++++++------- .../uv-distribution-types/src/dist_error.rs | 21 +++++++----- crates/uv-types/src/traits.rs | 2 +- crates/uv/tests/it/build.rs | 10 +++--- crates/uv/tests/it/edit.rs | 10 +++--- crates/uv/tests/it/lock.rs | 20 ++++++----- crates/uv/tests/it/pip_compile.rs | 5 +-- crates/uv/tests/it/pip_install.rs | 34 ++++++++++++------- crates/uv/tests/it/sync.rs | 30 +++++++++------- crates/uv/tests/it/tool_install.rs | 9 +++-- docs/reference/build_failures.md | 20 ++++++----- 12 files changed, 118 insertions(+), 77 deletions(-) diff --git a/crates/uv-build-frontend/src/error.rs b/crates/uv-build-frontend/src/error.rs index 612a937849b13..11ccd37379983 100644 --- a/crates/uv-build-frontend/src/error.rs +++ b/crates/uv-build-frontend/src/error.rs @@ -77,9 +77,9 @@ pub enum Error { // Build backend errors #[error("Failed to run `{0}`")] CommandFailed(PathBuf, #[source] io::Error), - #[error("The build backend returned an error. This likely means a problem with the package or your environment.")] + #[error("The build backend returned an error")] BuildBackend(#[from] BuildBackendError), - #[error("The build backend returned an error. This likely means a problem with the package or your environment.")] + #[error("The build backend returned an error")] MissingHeader(#[from] MissingHeaderError), #[error("Failed to build PATH for build script")] BuildScriptPath(#[source] env::JoinPathsError), @@ -276,6 +276,13 @@ impl Display for BuildBackendError { writeln!(f)?; } + write!( + f, + "\n{}{} This usually indicates a problem with the package or the build environment.", + "hint".bold().cyan(), + ":".bold() + )?; + Ok(()) } } diff --git a/crates/uv-build-frontend/src/lib.rs b/crates/uv-build-frontend/src/lib.rs index 59b1b1b65407b..2877612b887e6 100644 --- a/crates/uv-build-frontend/src/lib.rs +++ b/crates/uv-build-frontend/src/lib.rs @@ -7,7 +7,6 @@ mod error; use fs_err as fs; use indoc::formatdoc; use itertools::Itertools; -use owo_colors::OwoColorize; use rustc_hash::FxHashMap; use serde::de::{value, IntoDeserializer, SeqAccess, Visitor}; use serde::{de, Deserialize, Deserializer}; @@ -626,8 +625,8 @@ impl SourceBuild { if !output.status.success() { return Err(Error::from_command_output( format!( - "Build backend failed to determine metadata through `{}`", - format!("prepare_metadata_for_build_{}", self.build_kind).green() + "Call to `{}.prepare_metadata_for_build_{}` failed", + self.pep517_backend.backend, self.build_kind ), &output, self.level, @@ -749,9 +748,8 @@ impl SourceBuild { if !output.status.success() { return Err(Error::from_command_output( format!( - "Build backend failed to build {} through `{}`", - self.build_kind, - format!("build_{}", self.build_kind).green(), + "Call to `{}.build_{}` failed", + pep517_backend.backend, self.build_kind ), &output, self.level, @@ -765,8 +763,8 @@ impl SourceBuild { if !output_dir.join(&distribution_filename).is_file() { return Err(Error::from_command_output( format!( - "Build backend failed to produce {} through `{}`: `{distribution_filename}` not found", - self.build_kind, format!("build_{}", self.build_kind).green(), + "Call to `{}.build_{}` failed", + pep517_backend.backend, self.build_kind ), &output, self.level, @@ -862,8 +860,8 @@ async fn create_pep517_build_environment( if !output.status.success() { return Err(Error::from_command_output( format!( - "Call to `{}.build_{build_kind}()` failed", - pep517_backend.backend, + "Call to `{}.build_{}` failed", + pep517_backend.backend, build_kind ), &output, level, @@ -883,9 +881,8 @@ async fn create_pep517_build_environment( Err(err) => { return Err(Error::from_command_output( format!( - "Build backend failed to return requirements from \ - `{}.get_requires_for_build_{build_kind}`: {err}", - pep517_backend.backend, + "Call to `{}.get_requires_for_build_{}` failed: {}", + pep517_backend.backend, build_kind, err ), &output, level, diff --git a/crates/uv-distribution-types/src/dist_error.rs b/crates/uv-distribution-types/src/dist_error.rs index f68d7fe97c8f0..a742b034bfc60 100644 --- a/crates/uv-distribution-types/src/dist_error.rs +++ b/crates/uv-distribution-types/src/dist_error.rs @@ -9,22 +9,27 @@ use uv_normalize::{ExtraName, GroupName, PackageName}; use uv_pep440::Version; use version_ranges::Ranges; -/// Orphan-rule workaround for inspecting build frontend errors. +/// Workaround for inspecting errors while avoiding cyclical crate dependencies. /// -/// Normally we would match on the variants, but we have to pass this error through an opaque type -/// to avoid cyclical crate dependencies between the build frontend and resolver/installer. +/// The `uv-resolver`, `uv-installer` and `uv-build-frontend` error types all reference each other: +/// Resolution and installation may need to build packages, while the build frontend needs to +/// resolve and install for the PEP 517 build environment (See also: `BuildContext`). We use an +/// opaque `dyn` error type, with this trait allowing to inspect it. pub trait IsBuildBackendError: std::error::Error + Send + Sync + 'static { /// Returns whether the build backend failed to build the package, so it's not a uv error. fn is_build_backend_error(&self) -> bool; } -/// Wrapper type to make `thiserror`'s `AsDynError` work with `IsBuildFrontendError`. +/// `anyhow::Error`-like wrapper type to make `IsBuildBackendError` work as `thiserror` `#[source]`. /// -/// `thiserror` does not recognize `Box` as -/// error source by itself. +/// The `uv-resolver`, `uv-installer` and `uv-build-frontend` error types all reference each other: +/// Resolution and installation may need to build packages, while the build frontend needs to +/// resolve and install for the PEP 517 build environment (See also: `BuildContext`). We use an +/// opaque `dyn` error type with [`IsBuildBackendError`] making the error inspectable. /// -/// TODO(konsti): The `From` blocks implementing `IsBuildBackendError` on the -/// type itself, so the wrapped +/// `thiserror` does not recognize `Box` as +/// error source by itself, it complains about the internal `AsDynError` not being implemented. +/// This struct is an otherwise transparent wrapper that thiserror recognizes. pub struct AnyErrorBuild(Box); impl Debug for AnyErrorBuild { diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index 73bead2bf8cf7..19dce092abfb2 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -47,7 +47,7 @@ use uv_python::{Interpreter, PythonEnvironment}; /// │ │ │ /// └─────────────┐ │ ┌──────────────┘ /// ┌──┴────┴────┴───┐ -/// │ uv-types │ +/// │ uv-types │ /// └────────────────┘ /// ``` /// diff --git a/crates/uv/tests/it/build.rs b/crates/uv/tests/it/build.rs index b6f961fc955e7..76e3345e4236f 100644 --- a/crates/uv/tests/it/build.rs +++ b/crates/uv/tests/it/build.rs @@ -897,8 +897,9 @@ fn fail() -> Result<()> { from setuptools import setup IndentationError: unexpected indent × Failed to build `[TEMP_DIR]/project` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta.build_sdist()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta.build_sdist` failed (exit status: 1) + hint: This usually indicates a problem with the package or the build environment. "###); Ok(()) @@ -1346,8 +1347,9 @@ fn build_all_with_failure() -> Result<()> { Successfully built dist/member_a-0.1.0.tar.gz Successfully built dist/member_a-0.1.0-py3-none-any.whl × Failed to build `member-b @ [TEMP_DIR]/project/packages/member_b` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta.build_sdist()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta.build_sdist` failed (exit status: 1) + hint: This usually indicates a problem with the package or the build environment. Successfully built dist/project-0.1.0.tar.gz Successfully built dist/project-0.1.0-py3-none-any.whl "###); diff --git a/crates/uv/tests/it/edit.rs b/crates/uv/tests/it/edit.rs index 48316de9f1ec1..dcc50bd634849 100644 --- a/crates/uv/tests/it/edit.rs +++ b/crates/uv/tests/it/edit.rs @@ -5559,8 +5559,8 @@ fn fail_to_add_revert_project() -> Result<()> { ----- stderr ----- Resolved 3 packages in [TIME] × Failed to build `child @ file://[TEMP_DIR]/child` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -5575,6 +5575,7 @@ fn fail_to_add_revert_project() -> Result<()> { File "", line 1, in ZeroDivisionError: division by zero + hint: This usually indicates a problem with the package or the build environment. help: `child` was included because `parent` (v0.1.0) depends on `child` "###); @@ -5669,8 +5670,8 @@ fn fail_to_edit_revert_project() -> Result<()> { ----- stderr ----- Resolved 3 packages in [TIME] × Failed to build `child @ file://[TEMP_DIR]/child` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -5685,6 +5686,7 @@ fn fail_to_edit_revert_project() -> Result<()> { File "", line 1, in ZeroDivisionError: division by zero + hint: This usually indicates a problem with the package or the build environment. help: `child` was included because `parent` (v0.1.0) depends on `child` "###); diff --git a/crates/uv/tests/it/lock.rs b/crates/uv/tests/it/lock.rs index 7d8cb53942442..c311bd0035f15 100644 --- a/crates/uv/tests/it/lock.rs +++ b/crates/uv/tests/it/lock.rs @@ -18659,8 +18659,8 @@ fn lock_derivation_chain_prod() -> Result<()> { ----- stderr ----- × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -18680,6 +18680,7 @@ fn lock_derivation_chain_prod() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `project` (v0.1.0) depends on `wsgiref==0.1.2` "###); @@ -18718,8 +18719,8 @@ fn lock_derivation_chain_extra() -> Result<()> { ----- stderr ----- × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -18739,6 +18740,7 @@ fn lock_derivation_chain_extra() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `project[wsgi]` (v0.1.0) depends on `wsgiref>=0.1` "###); @@ -18779,8 +18781,8 @@ fn lock_derivation_chain_group() -> Result<()> { ----- stderr ----- × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -18800,6 +18802,7 @@ fn lock_derivation_chain_group() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `project:wsgi` (v0.1.0) depends on `wsgiref` "###); @@ -18851,8 +18854,8 @@ fn lock_derivation_chain_extended() -> Result<()> { ----- stderr ----- × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -18872,6 +18875,7 @@ fn lock_derivation_chain_extended() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `project` (v0.1.0) depends on `child` (v0.1.0) which depends on `wsgiref>=0.1, <0.2` "###); diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 5d3b360175863..7a78f82e064cd 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -13717,8 +13717,8 @@ fn compile_derivation_chain() -> Result<()> { ----- stderr ----- × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -13738,6 +13738,7 @@ fn compile_derivation_chain() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `child` (v0.1.0) depends on `wsgiref` "### ); diff --git a/crates/uv/tests/it/pip_install.rs b/crates/uv/tests/it/pip_install.rs index f181a990bdca7..473430cdd224e 100644 --- a/crates/uv/tests/it/pip_install.rs +++ b/crates/uv/tests/it/pip_install.rs @@ -267,8 +267,8 @@ dependencies = ["flask==1.0.x"] ----- stderr ----- × Failed to build `project @ file://[TEMP_DIR]/path_dep` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stdout] configuration error: `project.dependencies[0]` must be pep508 @@ -320,6 +320,8 @@ dependencies = ["flask==1.0.x"] raise ValueError(f"{error}/n{summary}") from None ValueError: invalid pyproject.toml config: `project.dependencies[0]`. configuration error: `project.dependencies[0]` must be pep508 + + hint: This usually indicates a problem with the package or the build environment. "### ); @@ -4021,13 +4023,15 @@ fn no_build_isolation() -> Result<()> { ----- stderr ----- × Failed to build `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta.prepare_metadata_for_build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): File "", line 8, in ModuleNotFoundError: No module named 'setuptools' + + hint: This usually indicates a problem with the package or the build environment. "### ); @@ -4090,13 +4094,15 @@ fn respect_no_build_isolation_env_var() -> Result<()> { ----- stderr ----- × Failed to build `anyio @ https://files.pythonhosted.org/packages/db/4d/3970183622f0330d3c23d9b8a5f52e365e50381fd484d08e3285104333d3/anyio-4.3.0.tar.gz` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta.prepare_metadata_for_build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): File "", line 8, in ModuleNotFoundError: No module named 'setuptools' + + hint: This usually indicates a problem with the package or the build environment. "### ); @@ -7088,13 +7094,15 @@ fn install_build_isolation_package() -> Result<()> { ----- stderr ----- × Failed to build `iniconfig @ https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Build backend failed to determine metadata through `prepare_metadata_for_build_wheel` (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `hatchling.build.prepare_metadata_for_build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): File "", line 8, in ModuleNotFoundError: No module named 'hatchling' + + hint: This usually indicates a problem with the package or the build environment. "### ); @@ -7348,8 +7356,8 @@ fn sklearn() { ----- stderr ----- × Failed to build `sklearn==0.0.post12` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] The 'sklearn' PyPI package is deprecated, use 'scikit-learn' @@ -7368,6 +7376,7 @@ fn sklearn() { More information is available at https://github.com/scikit-learn/sklearn-pypi-package + hint: This usually indicates a problem with the package or the build environment. help: `sklearn` is often confused for `scikit-learn` Did you mean to install `scikit-learn` instead? "### ); @@ -7405,8 +7414,8 @@ fn resolve_derivation_chain() -> Result<()> { ----- stderr ----- × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -7426,6 +7435,7 @@ fn resolve_derivation_chain() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `project` (v0.1.0) depends on `wsgiref` "### ); diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index b43e66359fe3c..0761f1a903d93 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -762,14 +762,15 @@ fn sync_build_isolation_package() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] × Failed to build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `hatchling.build.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): File "", line 8, in ModuleNotFoundError: No module named 'hatchling' + hint: This usually indicates a problem with the package or the build environment. help: `source-distribution` was included because `project` (v0.1.0) depends on `source-distribution` "###); @@ -854,14 +855,15 @@ fn sync_build_isolation_extra() -> Result<()> { ----- stderr ----- Resolved [N] packages in [TIME] × Failed to build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `hatchling.build.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): File "", line 8, in ModuleNotFoundError: No module named 'hatchling' + hint: This usually indicates a problem with the package or the build environment. help: `source-distribution` was included because `project[compile]` (v0.1.0) depends on `source-distribution` "###); @@ -874,14 +876,15 @@ fn sync_build_isolation_extra() -> Result<()> { ----- stderr ----- Resolved [N] packages in [TIME] × Failed to build `source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `hatchling.build.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): File "", line 8, in ModuleNotFoundError: No module named 'hatchling' + hint: This usually indicates a problem with the package or the build environment. help: `source-distribution` was included because `project[compile]` (v0.1.0) depends on `source-distribution` "###); @@ -4957,8 +4960,8 @@ fn sync_derivation_chain() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -4978,6 +4981,7 @@ fn sync_derivation_chain() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `project` (v0.1.0) depends on `wsgiref` "###); @@ -5022,8 +5026,8 @@ fn sync_derivation_chain_extra() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -5043,6 +5047,7 @@ fn sync_derivation_chain_extra() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `project[wsgi]` (v0.1.0) depends on `wsgiref` "###); @@ -5089,8 +5094,8 @@ fn sync_derivation_chain_group() -> Result<()> { ----- stderr ----- Resolved 2 packages in [TIME] × Failed to build `wsgiref==0.1.2` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -5110,6 +5115,7 @@ fn sync_derivation_chain_group() -> Result<()> { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)? + hint: This usually indicates a problem with the package or the build environment. help: `wsgiref` (v0.1.2) was included because `project:wsgi` (v0.1.0) depends on `wsgiref` "###); diff --git a/crates/uv/tests/it/tool_install.rs b/crates/uv/tests/it/tool_install.rs index 57cfb68baa727..902f6853b72e4 100644 --- a/crates/uv/tests/it/tool_install.rs +++ b/crates/uv/tests/it/tool_install.rs @@ -1470,8 +1470,8 @@ fn tool_install_uninstallable() { ----- stderr ----- Resolved 1 package in [TIME] × Failed to build `pyenv==0.0.1` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. - ╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stdout] running bdist_wheel @@ -1484,9 +1484,12 @@ fn tool_install_uninstallable() { We are sorry, but this package is not installable with pip. Please read the installation instructions at: - + https://github.com/pyenv/pyenv#installation # + + + hint: This usually indicates a problem with the package or the build environment. "###); // Ensure the tool environment is not created. diff --git a/docs/reference/build_failures.md b/docs/reference/build_failures.md index a6d625947b9b0..aa0917146a6fa 100644 --- a/docs/reference/build_failures.md +++ b/docs/reference/build_failures.md @@ -13,7 +13,7 @@ unsupported version of Python: $ uv pip install -p 3.13 'numpy<1.20' Resolved 1 package in 62ms × Failed to build `numpy==1.19.5` - ├─▶ The build backend returned an error. This likely means a problem with the package or your environment. + ├─▶ The build backend returned an error ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel()` failed (exit status: 1) [stderr] @@ -120,7 +120,8 @@ If the build error mentions a missing command, for example, `gcc`: ```hl_lines="17" × Failed to build `pysha3==1.0.2` -╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) +├─▶ The build backend returned an error +╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stdout] running bdist_wheel @@ -131,8 +132,8 @@ If the build error mentions a missing command, for example, `gcc`: running build_ext building '_pysha3' extension creating build/temp.linux-x86_64-cpython-310/Modules/_sha3 - gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -DPY_WITH_KECCAK=1 -I/root/.cache/uv/builds-v0/.tmpxAJdUa/include -I/usr/local/include/python3.10 -c Modules/_sha3/sha3module.c -o - build/temp.linux-x86_64-cpython-310/Modules/_sha3/sha3module.o + gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -DPY_WITH_KECCAK=1 -I/root/.cache/uv/builds-v0/.tmp8V4iEk/include -I/usr/local/include/python3.10 -c + Modules/_sha3/sha3module.c -o build/temp.linux-x86_64-cpython-310/Modules/_sha3/sha3module.o [stderr] error: command 'gcc' failed: No such file or directory @@ -167,7 +168,8 @@ For example, installing `pygraphviz` requires Graphviz to be installed: ```hl_lines="18-19" × Failed to build `pygraphviz==1.14` -╰─▶ Build backend failed to build wheel through `build_wheel` (exit status: 1) +├─▶ The build backend returned an error +╰─▶ Call to `setuptools.build_meta.build_wheel` failed (exit status: 1) [stdout] running bdist_wheel @@ -216,8 +218,9 @@ dependency: ```hl_lines="7" -× Failed to build `chumpy==0.70` -╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) + × Failed to build `chumpy==0.70` + ├─▶ The build backend returned an error + ╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] Traceback (most recent call last): @@ -272,7 +275,8 @@ apache-beam<=2.49.0 ```hl_lines="1" × Failed to build `apache-beam==2.0.0` -╰─▶ Build backend failed to determine requirements with `build_wheel()` (exit status: 1) +├─▶ The build backend returned an error +╰─▶ Call to `setuptools.build_meta:__legacy__.build_wheel` failed (exit status: 1) [stderr] ... From 11922ebab44d9be0df3a289cf9f59ab6a354554b Mon Sep 17 00:00:00 2001 From: konstin Date: Fri, 6 Dec 2024 13:17:19 +0100 Subject: [PATCH 4/6] Unify dist error kind building --- .../uv-distribution-types/src/dist_error.rs | 25 +++++++++- crates/uv-installer/src/preparer.rs | 27 ++++------ crates/uv-requirements/src/lib.rs | 26 +++------- crates/uv-resolver/src/resolver/mod.rs | 49 ++++--------------- crates/uv/tests/it/pip_sync.rs | 2 +- 5 files changed, 50 insertions(+), 79 deletions(-) diff --git a/crates/uv-distribution-types/src/dist_error.rs b/crates/uv-distribution-types/src/dist_error.rs index a742b034bfc60..4954e1f2b363a 100644 --- a/crates/uv-distribution-types/src/dist_error.rs +++ b/crates/uv-distribution-types/src/dist_error.rs @@ -1,4 +1,4 @@ -use crate::{DistRef, Edge, Name, Node, Resolution, ResolvedDist}; +use crate::{BuiltDist, Dist, DistRef, Edge, Name, Node, Resolution, ResolvedDist, SourceDist}; use petgraph::prelude::EdgeRef; use petgraph::Direction; use rustc_hash::FxHashSet; @@ -84,6 +84,29 @@ pub enum DistErrorKind { Read, } +impl DistErrorKind { + pub fn from_dist_and_err(dist: &Dist, err: &impl IsBuildBackendError) -> Self { + if err.is_build_backend_error() { + DistErrorKind::BuildBackend + } else { + match dist { + Dist::Built(BuiltDist::Path(_)) => DistErrorKind::Read, + Dist::Source(SourceDist::Path(_) | SourceDist::Directory(_)) => { + DistErrorKind::Build + } + Dist::Built(_) => DistErrorKind::Download, + Dist::Source(source_dist) => { + if source_dist.is_local() { + DistErrorKind::Build + } else { + DistErrorKind::DownloadAndBuild + } + } + } + } + } +} + impl Display for DistErrorKind { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { diff --git a/crates/uv-installer/src/preparer.rs b/crates/uv-installer/src/preparer.rs index ea97d4d95ea57..fac49057025c1 100644 --- a/crates/uv-installer/src/preparer.rs +++ b/crates/uv-installer/src/preparer.rs @@ -9,8 +9,8 @@ use uv_cache::Cache; use uv_configuration::BuildOptions; use uv_distribution::{DistributionDatabase, LocalWheel}; use uv_distribution_types::{ - BuildableSource, CachedDist, DerivationChain, Dist, DistErrorKind, Hashed, Identifier, - IsBuildBackendError, Name, RemoteSource, Resolution, + BuildableSource, CachedDist, DerivationChain, Dist, DistErrorKind, Hashed, Identifier, Name, + RemoteSource, Resolution, }; use uv_pep508::PackageName; use uv_platform_tags::Tags; @@ -226,24 +226,15 @@ pub enum Error { impl Error { /// Create an [`Error`] from a distribution error. - fn from_dist(dist: Dist, cause: uv_distribution::Error, resolution: &Resolution) -> Self { - let kind = if cause.is_build_backend_error() { - DistErrorKind::BuildBackend - } else { - match &dist { - Dist::Built(_) => DistErrorKind::Download, - Dist::Source(dist) => { - if dist.is_local() { - DistErrorKind::Build - } else { - DistErrorKind::DownloadAndBuild - } - } - } - }; + fn from_dist(dist: Dist, err: uv_distribution::Error, resolution: &Resolution) -> Self { let chain = DerivationChain::from_resolution(resolution, (&dist).into()).unwrap_or_default(); - Self::Dist(kind, Box::new(dist), chain, cause) + Self::Dist( + DistErrorKind::from_dist_and_err(&dist, &err), + Box::new(dist), + chain, + err, + ) } } diff --git a/crates/uv-requirements/src/lib.rs b/crates/uv-requirements/src/lib.rs index 358ff313feda4..adb342e133c6f 100644 --- a/crates/uv-requirements/src/lib.rs +++ b/crates/uv-requirements/src/lib.rs @@ -5,9 +5,7 @@ pub use crate::sources::*; pub use crate::specification::*; pub use crate::unnamed::*; -use uv_distribution_types::{ - Dist, DistErrorKind, GitSourceDist, IsBuildBackendError, SourceDist, -}; +use uv_distribution_types::{Dist, DistErrorKind, GitSourceDist, SourceDist}; use uv_git::GitUrl; use uv_pypi_types::{Requirement, RequirementSource}; @@ -36,22 +34,12 @@ pub enum Error { impl Error { /// Create an [`Error`] from a distribution error. - pub(crate) fn from_dist(dist: Dist, cause: uv_distribution::Error) -> Self { - let kind = if cause.is_build_backend_error() { - DistErrorKind::BuildBackend - } else { - match &dist { - Dist::Built(_) => DistErrorKind::Download, - Dist::Source(dist) => { - if dist.is_local() { - DistErrorKind::Build - } else { - DistErrorKind::DownloadAndBuild - } - } - } - }; - Self::Dist(kind, Box::new(dist), DerivationChain::default(), cause) + pub(crate) fn from_dist(dist: Dist, err: uv_distribution::Error) -> Self { + Self::Dist( + DistErrorKind::from_dist_and_err(&dist, &err), + Box::new(dist), + err, + ) } } diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index c82f574a17acf..ff3aef1cad482 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -25,8 +25,8 @@ use uv_distribution::{ArchiveMetadata, DistributionDatabase}; use uv_distribution_types::{ BuiltDist, CompatibleDist, DerivationChain, Dist, DistErrorKind, DistributionMetadata, IncompatibleDist, IncompatibleSource, IncompatibleWheel, IndexCapabilities, IndexLocations, - IndexUrl, InstalledDist, IsBuildBackendError, PythonRequirementKind, RemoteSource, - ResolvedDist, ResolvedDistRef, SourceDist, VersionOrUrlRef, + IndexUrl, InstalledDist, PythonRequirementKind, RemoteSource, ResolvedDist, ResolvedDistRef, + SourceDist, VersionOrUrlRef, }; use uv_git::GitResolver; use uv_normalize::{ExtraName, GroupName, PackageName}; @@ -956,26 +956,8 @@ impl ResolverState { // TODO(charlie): Add derivation chain for URL dependencies. In practice, this isn't // critical since we fetch URL dependencies _prior_ to invoking the resolver. - let kind = if err.is_build_backend_error() { - DistErrorKind::BuildBackend - } else { - match &**dist { - Dist::Built(BuiltDist::Path(_)) => DistErrorKind::Read, - Dist::Source(SourceDist::Path(_) | SourceDist::Directory(_)) => { - DistErrorKind::Build - } - Dist::Built(_) => DistErrorKind::Download, - Dist::Source(source_dist) => { - if source_dist.is_local() { - DistErrorKind::Build - } else { - DistErrorKind::DownloadAndBuild - } - } - } - }; return Err(ResolveError::Dist( - kind, + DistErrorKind::from_dist_and_err(dist, &**err), dist.clone(), DerivationChain::default(), err.clone(), @@ -1391,25 +1373,12 @@ impl ResolverState { let chain = DerivationChainBuilder::from_state(id, version, pubgrub) .unwrap_or_default(); - let kind = if err.is_build_backend_error() { - DistErrorKind::BuildBackend - } else { - match &**dist { - Dist::Built(BuiltDist::Path(_)) => DistErrorKind::Read, - Dist::Source(SourceDist::Path(_) | SourceDist::Directory(_)) => { - DistErrorKind::Build - } - Dist::Built(_) => DistErrorKind::Download, - Dist::Source(source_dist) => { - if source_dist.is_local() { - DistErrorKind::Build - } else { - DistErrorKind::DownloadAndBuild - } - } - } - }; - return Err(ResolveError::Dist(kind, dist.clone(), chain, err.clone())); + return Err(ResolveError::Dist( + DistErrorKind::from_dist_and_err(dist, &**err), + dist.clone(), + chain, + err.clone(), + )); } }; diff --git a/crates/uv/tests/it/pip_sync.rs b/crates/uv/tests/it/pip_sync.rs index fa0ac8a5fc4f1..9884ed1162270 100644 --- a/crates/uv/tests/it/pip_sync.rs +++ b/crates/uv/tests/it/pip_sync.rs @@ -4169,7 +4169,7 @@ fn require_hashes_wheel_path_mismatch() -> Result<()> { ----- stderr ----- Resolved 1 package in [TIME] - × Failed to download `tqdm @ file://[WORKSPACE]/scripts/links/tqdm-1000.0.0-py3-none-any.whl` + × Failed to read `tqdm @ file://[WORKSPACE]/scripts/links/tqdm-1000.0.0-py3-none-any.whl` ╰─▶ Hash mismatch for `tqdm @ file://[WORKSPACE]/scripts/links/tqdm-1000.0.0-py3-none-any.whl` Expected: From 354d6f03af375574e79198d0cd179f6004b1fe39 Mon Sep 17 00:00:00 2001 From: konstin Date: Fri, 6 Dec 2024 14:17:09 +0100 Subject: [PATCH 5/6] Lint --- crates/uv-resolver/src/resolver/mod.rs | 4 ++-- crates/uv/tests/it/tool_install.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/uv-resolver/src/resolver/mod.rs b/crates/uv-resolver/src/resolver/mod.rs index ff3aef1cad482..cf98c6043fa73 100644 --- a/crates/uv-resolver/src/resolver/mod.rs +++ b/crates/uv-resolver/src/resolver/mod.rs @@ -2212,11 +2212,11 @@ impl ForkState { for_version: &Version, urls: &Urls, indexes: &Indexes, - mut dependencies: Vec, + dependencies: Vec, git: &GitResolver, resolution_strategy: &ResolutionStrategy, ) -> Result<(), ResolveError> { - for dependency in &mut dependencies { + for dependency in &dependencies { let PubGrubDependency { package, version, diff --git a/crates/uv/tests/it/tool_install.rs b/crates/uv/tests/it/tool_install.rs index 902f6853b72e4..e951cafc2f403 100644 --- a/crates/uv/tests/it/tool_install.rs +++ b/crates/uv/tests/it/tool_install.rs @@ -1484,7 +1484,7 @@ fn tool_install_uninstallable() { We are sorry, but this package is not installable with pip. Please read the installation instructions at: - + https://github.com/pyenv/pyenv#installation # From 65c749c61b50260e7d4fac81e0522d2df47abada Mon Sep 17 00:00:00 2001 From: konstin Date: Fri, 6 Dec 2024 15:04:46 +0100 Subject: [PATCH 6/6] Move error type --- crates/uv-build-frontend/src/error.rs | 6 +- crates/uv-build-frontend/src/lib.rs | 4 +- crates/uv-dispatch/src/lib.rs | 8 ++- .../uv-distribution-types/src/dist_error.rs | 62 +----------------- crates/uv-distribution/src/error.rs | 3 +- crates/uv-types/src/traits.rs | 64 ++++++++++++++++++- crates/uv/src/commands/build_frontend.rs | 4 +- crates/uv/src/commands/venv.rs | 4 +- 8 files changed, 79 insertions(+), 76 deletions(-) diff --git a/crates/uv-build-frontend/src/error.rs b/crates/uv-build-frontend/src/error.rs index 11ccd37379983..81fda9d877b5a 100644 --- a/crates/uv-build-frontend/src/error.rs +++ b/crates/uv-build-frontend/src/error.rs @@ -5,17 +5,17 @@ use std::path::PathBuf; use std::process::ExitStatus; use std::sync::LazyLock; +use crate::PythonRunnerOutput; use owo_colors::OwoColorize; use regex::Regex; use thiserror::Error; use tracing::error; use uv_configuration::BuildOutput; -use uv_distribution_types::{AnyErrorBuild, IsBuildBackendError}; +use uv_distribution_types::IsBuildBackendError; use uv_fs::Simplified; use uv_pep440::Version; use uv_pep508::PackageName; - -use crate::PythonRunnerOutput; +use uv_types::AnyErrorBuild; /// e.g. `pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory` static MISSING_HEADER_RE_GCC: LazyLock = LazyLock::new(|| { diff --git a/crates/uv-build-frontend/src/lib.rs b/crates/uv-build-frontend/src/lib.rs index 2877612b887e6..75ea2f6382f29 100644 --- a/crates/uv-build-frontend/src/lib.rs +++ b/crates/uv-build-frontend/src/lib.rs @@ -28,14 +28,14 @@ use tracing::{debug, info_span, instrument, Instrument}; use uv_configuration::{BuildKind, BuildOutput, ConfigSettings, LowerBound, SourceStrategy}; use uv_distribution::BuildRequires; -use uv_distribution_types::{AnyErrorBuild, IndexLocations, Resolution}; +use uv_distribution_types::{IndexLocations, Resolution}; use uv_fs::{PythonExt, Simplified}; use uv_pep440::Version; use uv_pep508::PackageName; use uv_pypi_types::{Requirement, VerbatimParsedUrl}; use uv_python::{Interpreter, PythonEnvironment}; use uv_static::EnvVars; -use uv_types::{BuildContext, BuildIsolation, SourceBuildTrait}; +use uv_types::{AnyErrorBuild, BuildContext, BuildIsolation, SourceBuildTrait}; pub use crate::error::{Error, MissingHeaderCause}; diff --git a/crates/uv-dispatch/src/lib.rs b/crates/uv-dispatch/src/lib.rs index 371e92cb24da6..5fe3a7fecd2bd 100644 --- a/crates/uv-dispatch/src/lib.rs +++ b/crates/uv-dispatch/src/lib.rs @@ -23,8 +23,8 @@ use uv_configuration::{BuildOutput, Concurrency}; use uv_distribution::DistributionDatabase; use uv_distribution_filename::DistFilename; use uv_distribution_types::{ - AnyErrorBuild, CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, - IsBuildBackendError, Name, Resolution, SourceDist, VersionOrUrlRef, + CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, IsBuildBackendError, Name, + Resolution, SourceDist, VersionOrUrlRef, }; use uv_git::GitResolver; use uv_installer::{Installer, Plan, Planner, Preparer, SitePackages}; @@ -34,7 +34,9 @@ use uv_resolver::{ ExcludeNewer, FlatIndex, Flexibility, InMemoryIndex, Manifest, OptionsBuilder, PythonRequirement, Resolver, ResolverEnvironment, }; -use uv_types::{BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight}; +use uv_types::{ + AnyErrorBuild, BuildContext, BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight, +}; #[derive(Debug, Error)] pub enum BuildDispatchError { diff --git a/crates/uv-distribution-types/src/dist_error.rs b/crates/uv-distribution-types/src/dist_error.rs index 4954e1f2b363a..9bfcf8e974bb0 100644 --- a/crates/uv-distribution-types/src/dist_error.rs +++ b/crates/uv-distribution-types/src/dist_error.rs @@ -4,76 +4,16 @@ use petgraph::Direction; use rustc_hash::FxHashSet; use std::collections::VecDeque; use std::fmt::{Debug, Display, Formatter}; -use std::ops::Deref; use uv_normalize::{ExtraName, GroupName, PackageName}; use uv_pep440::Version; use version_ranges::Ranges; -/// Workaround for inspecting errors while avoiding cyclical crate dependencies. -/// -/// The `uv-resolver`, `uv-installer` and `uv-build-frontend` error types all reference each other: -/// Resolution and installation may need to build packages, while the build frontend needs to -/// resolve and install for the PEP 517 build environment (See also: `BuildContext`). We use an -/// opaque `dyn` error type, with this trait allowing to inspect it. +/// Inspect whether an error type is a build error. pub trait IsBuildBackendError: std::error::Error + Send + Sync + 'static { /// Returns whether the build backend failed to build the package, so it's not a uv error. fn is_build_backend_error(&self) -> bool; } -/// `anyhow::Error`-like wrapper type to make `IsBuildBackendError` work as `thiserror` `#[source]`. -/// -/// The `uv-resolver`, `uv-installer` and `uv-build-frontend` error types all reference each other: -/// Resolution and installation may need to build packages, while the build frontend needs to -/// resolve and install for the PEP 517 build environment (See also: `BuildContext`). We use an -/// opaque `dyn` error type with [`IsBuildBackendError`] making the error inspectable. -/// -/// `thiserror` does not recognize `Box` as -/// error source by itself, it complains about the internal `AsDynError` not being implemented. -/// This struct is an otherwise transparent wrapper that thiserror recognizes. -pub struct AnyErrorBuild(Box); - -impl Debug for AnyErrorBuild { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Debug::fmt(&self.0, f) - } -} - -impl Display for AnyErrorBuild { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(&self.0, f) - } -} - -impl std::error::Error for AnyErrorBuild { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.0.source() - } - - #[allow(deprecated)] - fn description(&self) -> &str { - self.0.description() - } - - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn std::error::Error> { - self.0.cause() - } -} - -impl From for AnyErrorBuild { - fn from(err: T) -> Self { - Self(Box::new(err)) - } -} - -impl Deref for AnyErrorBuild { - type Target = dyn IsBuildBackendError; - - fn deref(&self) -> &Self::Target { - &*self.0 - } -} - /// The operation(s) that failed when reporting an error with a distribution. #[derive(Debug)] pub enum DistErrorKind { diff --git a/crates/uv-distribution/src/error.rs b/crates/uv-distribution/src/error.rs index 5403d79feabca..88d912a130098 100644 --- a/crates/uv-distribution/src/error.rs +++ b/crates/uv-distribution/src/error.rs @@ -8,11 +8,12 @@ use zip::result::ZipError; use crate::metadata::MetadataError; use uv_client::WrappedReqwestError; use uv_distribution_filename::WheelFilenameError; -use uv_distribution_types::{AnyErrorBuild, IsBuildBackendError}; +use uv_distribution_types::IsBuildBackendError; use uv_fs::Simplified; use uv_normalize::PackageName; use uv_pep440::{Version, VersionSpecifiers}; use uv_pypi_types::{HashAlgorithm, HashDigest, ParsedUrlError}; +use uv_types::AnyErrorBuild; #[derive(Debug, thiserror::Error)] pub enum Error { diff --git a/crates/uv-types/src/traits.rs b/crates/uv-types/src/traits.rs index 19dce092abfb2..fc88cab097fd0 100644 --- a/crates/uv-types/src/traits.rs +++ b/crates/uv-types/src/traits.rs @@ -1,4 +1,6 @@ +use std::fmt::{Debug, Display, Formatter}; use std::future::Future; +use std::ops::Deref; use std::path::{Path, PathBuf}; use uv_distribution_filename::DistFilename; @@ -9,8 +11,8 @@ use uv_configuration::{ BuildKind, BuildOptions, BuildOutput, ConfigSettings, LowerBound, SourceStrategy, }; use uv_distribution_types::{ - AnyErrorBuild, CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, - InstalledDist, IsBuildBackendError, Resolution, SourceDist, + CachedDist, DependencyMetadata, IndexCapabilities, IndexLocations, InstalledDist, + IsBuildBackendError, Resolution, SourceDist, }; use uv_git::GitResolver; use uv_pep508::PackageName; @@ -184,3 +186,61 @@ impl InstalledPackagesProvider for EmptyInstalledPackages { std::iter::empty() } } + +/// `anyhow::Error`-like wrapper type for [`BuildDispatch`] method return values, that also makes +/// `IsBuildBackendError` work as `thiserror` `#[source]`. +/// +/// The errors types have the same problem as [`BuildDispatch`] generally: The `uv-resolver`, +/// `uv-installer` and `uv-build-frontend` error types all reference each other: +/// Resolution and installation may need to build packages, while the build frontend needs to +/// resolve and install for the PEP 517 build environment. +/// +/// Usually, `anyhow::Error` is opaque error type of choice. In this case though, we error type +/// that we can inspect on whether it's a build backend error with [`IsBuildBackendError`], and +/// `anyhow::Error` does not allow attaching more traits. The next choice would be +/// `Box`, but `thiserror` +/// complains about the internal `AsDynError` not being implemented when being used as `#[source]`. +/// This struct is an otherwise transparent error wrapper that thiserror recognizes. +pub struct AnyErrorBuild(Box); + +impl Debug for AnyErrorBuild { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl Display for AnyErrorBuild { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl std::error::Error for AnyErrorBuild { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } + + #[allow(deprecated)] + fn description(&self) -> &str { + self.0.description() + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn std::error::Error> { + self.0.cause() + } +} + +impl From for AnyErrorBuild { + fn from(err: T) -> Self { + Self(Box::new(err)) + } +} + +impl Deref for AnyErrorBuild { + type Target = dyn IsBuildBackendError; + + fn deref(&self) -> &Self::Target { + &*self.0 + } +} diff --git a/crates/uv/src/commands/build_frontend.rs b/crates/uv/src/commands/build_frontend.rs index 71cb2761fa204..2b3c0a99dade5 100644 --- a/crates/uv/src/commands/build_frontend.rs +++ b/crates/uv/src/commands/build_frontend.rs @@ -29,7 +29,7 @@ use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution_filename::{ DistFilename, SourceDistExtension, SourceDistFilename, WheelFilename, }; -use uv_distribution_types::{AnyErrorBuild, DependencyMetadata, Index, IndexLocations, SourceDist}; +use uv_distribution_types::{DependencyMetadata, Index, IndexLocations, SourceDist}; use uv_fs::{relative_to, Simplified}; use uv_install_wheel::linker::LinkMode; use uv_normalize::PackageName; @@ -42,7 +42,7 @@ use uv_python::{ use uv_requirements::RequirementsSource; use uv_resolver::{ExcludeNewer, FlatIndex, RequiresPython}; use uv_settings::PythonInstallMirrors; -use uv_types::{BuildContext, BuildIsolation, HashStrategy}; +use uv_types::{AnyErrorBuild, BuildContext, BuildIsolation, HashStrategy}; use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceError}; #[derive(Debug, Error)] diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index 3f0a773543068..e54f7bccfd703 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -16,7 +16,7 @@ use uv_configuration::{ LowerBound, NoBinary, NoBuild, PreviewMode, SourceStrategy, TrustedHost, }; use uv_dispatch::{BuildDispatch, SharedState}; -use uv_distribution_types::{AnyErrorBuild, DependencyMetadata, Index, IndexLocations}; +use uv_distribution_types::{DependencyMetadata, Index, IndexLocations}; use uv_fs::Simplified; use uv_install_wheel::linker::LinkMode; use uv_pypi_types::Requirement; @@ -26,7 +26,7 @@ use uv_python::{ use uv_resolver::{ExcludeNewer, FlatIndex}; use uv_settings::PythonInstallMirrors; use uv_shell::{shlex_posix, shlex_windows, Shell}; -use uv_types::{BuildContext, BuildIsolation, HashStrategy}; +use uv_types::{AnyErrorBuild, BuildContext, BuildIsolation, HashStrategy}; use uv_warnings::{warn_user, warn_user_once}; use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceError};