pip compile install a python interpreter when necessary

This commit is contained in:
Tomasz (Tom) Kramkowski 2025-12-22 15:04:10 +00:00
parent 137edcf239
commit c070b77cf5
3 changed files with 63 additions and 11 deletions

View file

@ -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<PythonVersion>,
python_platform: Option<TargetTriple>,
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.

View file

@ -645,6 +645,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
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,

View file

@ -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(())
}