diff --git a/crates/uv/src/commands/venv.rs b/crates/uv/src/commands/venv.rs index 6d6e15758..02bc818f8 100644 --- a/crates/uv/src/commands/venv.rs +++ b/crates/uv/src/commands/venv.rs @@ -4,9 +4,7 @@ use std::str::FromStr; use std::sync::Arc; use std::vec; -use anstream::eprint; use anyhow::Result; -use miette::{Diagnostic, IntoDiagnostic}; use owo_colors::OwoColorize; use thiserror::Error; @@ -42,6 +40,21 @@ use crate::settings::NetworkSettings; use super::project::default_dependency_groups; +#[derive(Error, Debug)] +enum VenvError { + #[error("Failed to create virtual environment")] + Creation(#[source] uv_virtualenv::Error), + + #[error("Failed to install seed packages into virtual environment")] + Seed(#[source] AnyErrorBuild), + + #[error("Failed to extract interpreter tags for installing seed packages")] + Tags(#[source] uv_platform_tags::TagsError), + + #[error("Failed to resolve `--find-links` entry")] + FlatIndex(#[source] uv_client::FlatIndexError), +} + /// Create a virtual environment. #[allow(clippy::unnecessary_wraps, clippy::fn_params_excessive_bools)] pub(crate) async fn venv( @@ -70,89 +83,6 @@ pub(crate) async fn venv( relocatable: bool, preview: PreviewMode, ) -> Result { - match venv_impl( - project_dir, - path, - python_request, - install_mirrors, - link_mode, - index_locations, - index_strategy, - dependency_metadata, - keyring_provider, - network_settings, - prompt, - system_site_packages, - seed, - python_preference, - python_downloads, - allow_existing, - exclude_newer, - concurrency, - no_config, - no_project, - cache, - printer, - relocatable, - preview, - ) - .await - { - Ok(status) => Ok(status), - Err(err) => { - eprint!("{err:?}"); - Ok(ExitStatus::Failure) - } - } -} - -#[derive(Error, Debug, Diagnostic)] -enum VenvError { - #[error("Failed to create virtualenv")] - #[diagnostic(code(uv::venv::creation))] - Creation(#[source] uv_virtualenv::Error), - - #[error("Failed to install seed packages")] - #[diagnostic(code(uv::venv::seed))] - Seed(#[source] AnyErrorBuild), - - #[error("Failed to extract interpreter tags")] - #[diagnostic(code(uv::venv::tags))] - Tags(#[source] uv_platform_tags::TagsError), - - #[error("Failed to resolve `--find-links` entry")] - #[diagnostic(code(uv::venv::flat_index))] - FlatIndex(#[source] uv_client::FlatIndexError), -} - -/// Create a virtual environment. -#[allow(clippy::fn_params_excessive_bools)] -async fn venv_impl( - project_dir: &Path, - path: Option, - python_request: Option, - install_mirrors: PythonInstallMirrors, - link_mode: LinkMode, - index_locations: &IndexLocations, - index_strategy: IndexStrategy, - dependency_metadata: DependencyMetadata, - keyring_provider: KeyringProviderType, - network_settings: &NetworkSettings, - prompt: uv_virtualenv::Prompt, - system_site_packages: bool, - seed: bool, - python_preference: PythonPreference, - python_downloads: PythonDownloads, - allow_existing: bool, - exclude_newer: Option, - concurrency: Concurrency, - no_config: bool, - no_project: bool, - cache: &Cache, - printer: Printer, - relocatable: bool, - preview: PreviewMode, -) -> miette::Result { let workspace_cache = WorkspaceCache::default(); let project = if no_project { None @@ -206,7 +136,7 @@ async fn venv_impl( // If the default dependency-groups demand a higher requires-python // we should bias an empty venv to that to avoid churn. let default_groups = match &project { - Some(project) => default_dependency_groups(project.pyproject_toml()).into_diagnostic()?, + Some(project) => default_dependency_groups(project.pyproject_toml())?, None => DefaultGroups::default(), }; let groups = DependencyGroups::default().with_defaults(default_groups); @@ -221,8 +151,7 @@ async fn venv_impl( project_dir, no_config, ) - .await - .into_diagnostic()?; + .await?; // Locate the Python interpreter to use in the environment let interpreter = { @@ -239,9 +168,8 @@ async fn venv_impl( install_mirrors.python_downloads_json_url.as_deref(), preview, ) - .await - .into_diagnostic()?; - report_interpreter(&python, false, printer).into_diagnostic()?; + .await?; + report_interpreter(&python, false, printer)?; python.into_interpreter() }; @@ -268,8 +196,7 @@ async fn venv_impl( "Creating virtual environment {}at: {}", if seed { "with seed packages " } else { "" }, path.user_display().cyan() - ) - .into_diagnostic()?; + )?; let upgradeable = preview.is_enabled() && python_request @@ -307,8 +234,7 @@ async fn venv_impl( } // Instantiate a client. - let client = RegistryClientBuilder::try_from(client_builder) - .into_diagnostic()? + let client = RegistryClientBuilder::try_from(client_builder)? .cache(cache.clone()) .index_locations(index_locations) .index_strategy(index_strategy) @@ -400,9 +326,7 @@ async fn venv_impl( .map_err(|err| VenvError::Seed(err.into()))?; let changelog = Changelog::from_installed(installed); - DefaultInstallLogger - .on_complete(&changelog, printer) - .into_diagnostic()?; + DefaultInstallLogger.on_complete(&changelog, printer)?; } // Determine the appropriate activation command. @@ -431,7 +355,7 @@ async fn venv_impl( Some(Shell::Cmd) => Some(shlex_windows(venv.scripts().join("activate"), Shell::Cmd)), }; if let Some(act) = activation { - writeln!(printer.stderr(), "Activate with: {}", act.green()).into_diagnostic()?; + writeln!(printer.stderr(), "Activate with: {}", act.green())?; } Ok(ExitStatus::Success) diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index f04c16b86..ac3549874 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -17411,11 +17411,11 @@ fn compile_broken_active_venv() -> Result<()> { .arg(&broken_system_python) .arg("venv2"), @r" success: false - exit_code: 1 + exit_code: 2 ----- stdout ----- ----- stderr ----- - × No interpreter found at path `python3.14159` + error: No interpreter found at path `python3.14159` "); // Simulate a removed Python interpreter diff --git a/crates/uv/tests/it/venv.rs b/crates/uv/tests/it/venv.rs index 52291c05d..43cacb640 100644 --- a/crates/uv/tests/it/venv.rs +++ b/crates/uv/tests/it/venv.rs @@ -656,13 +656,13 @@ fn create_venv_respects_group_requires_python() -> Result<()> { uv_snapshot!(context.filters(), context.venv().arg("--python").arg("3.11"), @r" success: false - exit_code: 1 + exit_code: 2 ----- stdout ----- ----- stderr ----- - × Found conflicting Python requirements: - │ - foo: <3.12 - │ - foo:dev: >=3.12 + error: Found conflicting Python requirements: + - foo: <3.12 + - foo:dev: >=3.12 " ); @@ -808,7 +808,7 @@ fn seed_older_python_version() { #[test] fn create_venv_unknown_python_minor() { - let context = TestContext::new_with_versions(&["3.12"]); + let context = TestContext::new_with_versions(&["3.12"]).with_filtered_python_sources(); let mut command = context.venv(); command @@ -819,34 +819,22 @@ fn create_venv_unknown_python_minor() { // Unset this variable to force what the user would see .env_remove(EnvVars::UV_TEST_PYTHON_PATH); - if cfg!(windows) { - uv_snapshot!(&mut command, @r###" - success: false - exit_code: 1 - ----- stdout ----- + uv_snapshot!(context.filters(), &mut command, @r" + success: false + exit_code: 2 + ----- stdout ----- - ----- stderr ----- - × No interpreter found for Python 3.100 in managed installations, search path, or registry - "### - ); - } else { - uv_snapshot!(&mut command, @r###" - success: false - exit_code: 1 - ----- stdout ----- - - ----- stderr ----- - × No interpreter found for Python 3.100 in managed installations or search path - "### - ); - } + ----- stderr ----- + error: No interpreter found for Python 3.100 in [PYTHON SOURCES] + " + ); context.venv.assert(predicates::path::missing()); } #[test] fn create_venv_unknown_python_patch() { - let context = TestContext::new_with_versions(&["3.12"]); + let context = TestContext::new_with_versions(&["3.12"]).with_filtered_python_sources(); let mut command = context.venv(); command @@ -857,27 +845,15 @@ fn create_venv_unknown_python_patch() { // Unset this variable to force what the user would see .env_remove(EnvVars::UV_TEST_PYTHON_PATH); - if cfg!(windows) { - uv_snapshot!(&mut command, @r###" - success: false - exit_code: 1 - ----- stdout ----- + uv_snapshot!(context.filters(), &mut command, @r" + success: false + exit_code: 2 + ----- stdout ----- - ----- stderr ----- - × No interpreter found for Python 3.12.100 in managed installations, search path, or registry - "### - ); - } else { - uv_snapshot!(&mut command, @r" - success: false - exit_code: 1 - ----- stdout ----- - - ----- stderr ----- - × No interpreter found for Python 3.12.100 in managed installations or search path - " - ); - } + ----- stderr ----- + error: No interpreter found for Python 3.12.[X] in [PYTHON SOURCES] + " + ); context.venv.assert(predicates::path::missing()); } @@ -915,19 +891,17 @@ fn file_exists() -> Result<()> { uv_snapshot!(context.filters(), context.venv() .arg(context.venv.as_os_str()) .arg("--python") - .arg("3.12"), @r###" + .arg("3.12"), @r" success: false - exit_code: 1 + exit_code: 2 ----- stdout ----- ----- stderr ----- Using CPython 3.12.[X] interpreter at: [PYTHON-3.12] Creating virtual environment at: .venv - uv::venv::creation - - × Failed to create virtualenv - ╰─▶ File exists at `.venv` - "### + error: Failed to create virtual environment + Caused by: File exists at `.venv` + " ); Ok(()) @@ -970,19 +944,17 @@ fn non_empty_dir_exists() -> Result<()> { uv_snapshot!(context.filters(), context.venv() .arg(context.venv.as_os_str()) .arg("--python") - .arg("3.12"), @r###" + .arg("3.12"), @r" success: false - exit_code: 1 + exit_code: 2 ----- stdout ----- ----- stderr ----- Using CPython 3.12.[X] interpreter at: [PYTHON-3.12] Creating virtual environment at: .venv - uv::venv::creation - - × Failed to create virtualenv - ╰─▶ The directory `.venv` exists, but it's not a virtual environment - "### + error: Failed to create virtual environment + Caused by: The directory `.venv` exists, but it's not a virtual environment + " ); Ok(()) @@ -1000,19 +972,17 @@ fn non_empty_dir_exists_allow_existing() -> Result<()> { uv_snapshot!(context.filters(), context.venv() .arg(context.venv.as_os_str()) .arg("--python") - .arg("3.12"), @r###" + .arg("3.12"), @r" success: false - exit_code: 1 + exit_code: 2 ----- stdout ----- ----- stderr ----- Using CPython 3.12.[X] interpreter at: [PYTHON-3.12] Creating virtual environment at: .venv - uv::venv::creation - - × Failed to create virtualenv - ╰─▶ The directory `.venv` exists, but it's not a virtual environment - "### + error: Failed to create virtual environment + Caused by: The directory `.venv` exists, but it's not a virtual environment + " ); uv_snapshot!(context.filters(), context.venv()