diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index e8d86ea73..9ca44540f 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -34,8 +34,8 @@ use uv_normalize::PackageName; use uv_preview::{Preview, PreviewFeatures}; use uv_pypi_types::{Conflicts, SupportedEnvironments}; use uv_python::{ - EnvironmentPreference, PythonEnvironment, PythonInstallation, PythonPreference, PythonRequest, - PythonVersion, VersionRequest, + EnvironmentPreference, PythonDownloads, PythonEnvironment, PythonInstallation, + PythonPreference, PythonRequest, PythonVersion, VersionRequest, }; use uv_requirements::upgrade::{LockedRequirements, read_pylock_toml_requirements}; use uv_requirements::{ @@ -57,6 +57,7 @@ use uv_workspace::pyproject::ExtraBuildDependencies; use crate::commands::pip::loggers::DefaultResolveLogger; use crate::commands::pip::{operations, resolution_markers, resolution_tags}; +use crate::commands::reporters::PythonDownloadReporter; use crate::commands::{ExitStatus, OutputWriter, diagnostics}; use crate::printer::Printer; @@ -109,6 +110,7 @@ pub(crate) async fn pip_compile( install_mirrors: PythonInstallMirrors, mut python_version: Option, python_platform: Option, + python_downloads: PythonDownloads, universal: bool, exclude_newer: ExcludeNewer, sources: SourceStrategy, @@ -303,14 +305,21 @@ pub(crate) async fn pip_compile( .await?; let interpreter = if let Some(python) = python.as_ref() { let request = PythonRequest::parse(python); - PythonInstallation::find( - &request, + let reporter = PythonDownloadReporter::single(printer); + PythonInstallation::find_or_download( + Some(&request), environment_preference, python_preference, - &download_list, + python_downloads, + &client_builder, &cache, + Some(&reporter), + install_mirrors.python_install_mirror.as_deref(), + install_mirrors.pypy_install_mirror.as_deref(), + install_mirrors.python_downloads_json_url.as_deref(), preview, ) + .await } else { // TODO(zanieb): The split here hints at a problem with the request abstraction; we should // be able to use `PythonInstallation::find(...)` here. diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 3c8e19f56..de61d7817 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -645,6 +645,7 @@ async fn run(mut cli: Cli) -> Result { args.settings.install_mirrors, args.settings.python_version, args.settings.python_platform, + globals.python_downloads, args.settings.universal, args.settings.exclude_newer, args.settings.sources, diff --git a/crates/uv/tests/it/pip_compile.rs b/crates/uv/tests/it/pip_compile.rs index 9d01ae9b3..51ab8739b 100644 --- a/crates/uv/tests/it/pip_compile.rs +++ b/crates/uv/tests/it/pip_compile.rs @@ -1556,28 +1556,32 @@ fn compile_fallback_interpreter() -> Result<()> { uv_snapshot!(context.filters(), context.pip_compile() .arg("requirements.in") .arg("-p") - .arg("pypy"), @r###" + .arg("pypy"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- error: No interpreter found for PyPy in [PYTHON SOURCES] - "### + + hint: A managed Python download is available for PyPy, but Python downloads are set to 'never' + " ); // Similarly, we fail if we receive a range request that cannot be satisfied uv_snapshot!(context.filters(), context.pip_compile() .arg("requirements.in") .arg("-p") - .arg(">=3.12"), @r###" + .arg(">=3.12"), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- error: No interpreter found for Python >=3.12 in [PYTHON SOURCES] - "### + + hint: A managed Python download is available for Python >=3.12, but Python downloads are set to 'never' + " ); Ok(()) @@ -1774,14 +1778,16 @@ fn compile_python_build_version_different_than_target() -> Result<()> { .arg("3.12") .arg("-p") .arg("pypy@3.11") - .env_remove(EnvVars::VIRTUAL_ENV), @r###" + .env_remove(EnvVars::VIRTUAL_ENV), @r" success: false exit_code: 2 ----- stdout ----- ----- stderr ----- error: No interpreter found for PyPy 3.11 in [PYTHON SOURCES] - "### + + hint: A managed Python download is available for PyPy 3.11, but Python downloads are set to 'never' + " ); uv_snapshot!(context.filters(), context.pip_compile() @@ -1797,6 +1803,8 @@ fn compile_python_build_version_different_than_target() -> Result<()> { ----- stderr ----- error: No interpreter found for Python 3.13 in [PYTHON SOURCES] + + hint: A managed Python download is available for Python 3.13, but Python downloads are set to 'never' " ); @@ -18068,3 +18076,37 @@ fn compile_with_python_platform_and_built_wheel_for_different_platform() -> Resu Ok(()) } + +#[cfg(feature = "python-managed")] +#[test] +fn compile_missing_python() -> Result<()> { + let context = TestContext::new("3.12") + .with_python_download_cache() + .with_managed_python_dirs(); + + let requirements_in = context.temp_dir.child("requirements.in"); + requirements_in.write_str("anyio==3.7.0")?; + + uv_snapshot!(context + .pip_compile() + .arg("--python").arg("3.13") + .arg("--python-version").arg("3.13") + .arg("requirements.in"), @r" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv pip compile --cache-dir [CACHE_DIR] --python 3.13 --python-version 3.13 requirements.in + anyio==3.7.0 + # via -r requirements.in + idna==3.6 + # via anyio + sniffio==1.3.1 + # via anyio + + ----- stderr ----- + Resolved 3 packages in [TIME] + "); + + Ok(()) +}