Skip to content

Commit

Permalink
Make Python and PyPy install mirrors configurable in uv.toml (#8695)
Browse files Browse the repository at this point in the history
<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
-->

## Summary

Adds python-install-mirror and pypy-install-mirror as keys for uv.toml,
and cli args for `uv python install`.

Could leave the cli args out if we think the env vars and configs are
sufficient.

Fixes #8186 

<!-- What's the purpose of the change? What does it do, and why? -->

---------

Co-authored-by: Zanie Blue <[email protected]>
  • Loading branch information
owenbrooks and zanieb authored Nov 13, 2024
1 parent 2966471 commit 2ea81b3
Show file tree
Hide file tree
Showing 29 changed files with 573 additions and 27 deletions.
16 changes: 16 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3938,6 +3938,22 @@ pub struct PythonInstallArgs {
/// See `uv help python` to view supported request formats.
pub targets: Vec<String>,

/// Set the URL to use as the source for downloading Python installations.
///
/// The provided URL will replace `https://github.com/indygreg/python-build-standalone/releases/download` in, e.g., `https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz`.
///
/// Distributions can be read from a local directory by using the `file://` URL scheme.
#[arg(long, env = EnvVars::UV_PYTHON_INSTALL_MIRROR)]
pub mirror: Option<String>,

/// Set the URL to use as the source for downloading PyPy installations.
///
/// The provided URL will replace `https://downloads.python.org/pypy` in, e.g., `https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2`.
///
/// Distributions can be read from a local directory by using the `file://` URL scheme.
#[arg(long, env = EnvVars::UV_PYPY_INSTALL_MIRROR)]
pub pypy_mirror: Option<String>,

/// Reinstall the requested Python version, if it's already installed.
///
/// By default, uv will exit successfully if the version is already
Expand Down
14 changes: 10 additions & 4 deletions crates/uv-python/src/downloads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,9 +466,11 @@ impl ManagedPythonDownload {
installation_dir: &Path,
cache_dir: &Path,
reinstall: bool,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
reporter: Option<&dyn Reporter>,
) -> Result<DownloadResult, Error> {
let url = self.download_url()?;
let url = self.download_url(python_install_mirror, pypy_install_mirror)?;
let path = installation_dir.join(self.key().to_string());

// If it is not a reinstall and the dir already exists, return it.
Expand Down Expand Up @@ -585,10 +587,14 @@ impl ManagedPythonDownload {

/// Return the [`Url`] to use when downloading the distribution. If a mirror is set via the
/// appropriate environment variable, use it instead.
fn download_url(&self) -> Result<Url, Error> {
fn download_url(
&self,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
) -> Result<Url, Error> {
match self.key.implementation {
LenientImplementationName::Known(ImplementationName::CPython) => {
if let Ok(mirror) = std::env::var(EnvVars::UV_PYTHON_INSTALL_MIRROR) {
if let Some(mirror) = python_install_mirror {
let Some(suffix) = self.url.strip_prefix(
"https://github.com/indygreg/python-build-standalone/releases/download/",
) else {
Expand All @@ -601,7 +607,7 @@ impl ManagedPythonDownload {
}

LenientImplementationName::Known(ImplementationName::PyPy) => {
if let Ok(mirror) = std::env::var(EnvVars::UV_PYPY_INSTALL_MIRROR) {
if let Some(mirror) = pypy_install_mirror {
let Some(suffix) = self.url.strip_prefix("https://downloads.python.org/pypy/")
else {
return Err(Error::Mirror(EnvVars::UV_PYPY_INSTALL_MIRROR, self.url));
Expand Down
25 changes: 23 additions & 2 deletions crates/uv-python/src/installation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ impl PythonInstallation {
client_builder: &BaseClientBuilder<'a>,
cache: &Cache,
reporter: Option<&dyn Reporter>,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
) -> Result<Self, Error> {
let request = request.unwrap_or_else(|| &PythonRequest::Default);

Expand All @@ -100,7 +102,16 @@ impl PythonInstallation {
{
if let Some(request) = PythonDownloadRequest::from_request(request) {
debug!("Requested Python not found, checking for available download...");
match Self::fetch(request.fill()?, client_builder, cache, reporter).await {
match Self::fetch(
request.fill()?,
client_builder,
cache,
reporter,
python_install_mirror,
pypy_install_mirror,
)
.await
{
Ok(installation) => Ok(installation),
Err(Error::Download(downloads::Error::NoDownloadFound(_))) => {
Err(Error::MissingPython(err))
Expand All @@ -121,6 +132,8 @@ impl PythonInstallation {
client_builder: &BaseClientBuilder<'a>,
cache: &Cache,
reporter: Option<&dyn Reporter>,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,
) -> Result<Self, Error> {
let installations = ManagedPythonInstallations::from_settings()?.init()?;
let installations_dir = installations.root();
Expand All @@ -132,7 +145,15 @@ impl PythonInstallation {

info!("Fetching requested Python...");
let result = download
.fetch(&client, installations_dir, &cache_dir, false, reporter)
.fetch(
&client,
installations_dir,
&cache_dir,
false,
python_install_mirror,
pypy_install_mirror,
reporter,
)
.await?;

let path = match result {
Expand Down
70 changes: 70 additions & 0 deletions crates/uv-settings/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use uv_pep508::Requirement;
use uv_pypi_types::{SupportedEnvironments, VerbatimParsedUrl};
use uv_python::{PythonDownloads, PythonPreference, PythonVersion};
use uv_resolver::{AnnotationStyle, ExcludeNewer, PrereleaseMode, ResolutionMode};
use uv_static::EnvVars;

/// A `pyproject.toml` with an (optional) `[tool.uv]` section.
#[allow(dead_code)]
Expand Down Expand Up @@ -41,6 +42,9 @@ pub struct Options {
#[serde(flatten)]
pub top_level: ResolverInstallerOptions,

#[serde(flatten)]
pub install_mirrors: PythonInstallMirrors,

#[serde(flatten)]
pub publish: PublishOptions,

Expand Down Expand Up @@ -676,6 +680,61 @@ pub struct ResolverInstallerOptions {
pub no_binary_package: Option<Vec<PackageName>>,
}

/// Shared settings, relevant to all operations that might create managed python installations.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, CombineOptions, OptionsMetadata)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PythonInstallMirrors {
/// Mirror URL for downloading managed Python installations.
///
/// By default, managed Python installations are downloaded from [`python-build-standalone`](https://github.com/indygreg/python-build-standalone).
/// This variable can be set to a mirror URL to use a different source for Python installations.
/// The provided URL will replace `https://github.com/indygreg/python-build-standalone/releases/download` in, e.g., `https://github.com/indygreg/python-build-standalone/releases/download/20240713/cpython-3.12.4%2B20240713-aarch64-apple-darwin-install_only.tar.gz`.
///
/// Distributions can be read from a local directory by using the `file://` URL scheme.
#[option(
default = "None",
value_type = "str",
example = r#"
python-install-mirror = "https://github.com/indygreg/python-build-standalone/releases/download"
"#
)]
pub python_install_mirror: Option<String>,
/// Mirror URL to use for downloading managed PyPy installations.
///
/// By default, managed PyPy installations are downloaded from [downloads.python.org](https://downloads.python.org/).
/// This variable can be set to a mirror URL to use a different source for PyPy installations.
/// The provided URL will replace `https://downloads.python.org/pypy` in, e.g., `https://downloads.python.org/pypy/pypy3.8-v7.3.7-osx64.tar.bz2`.
///
/// Distributions can be read from a
/// local directory by using the `file://` URL scheme.
#[option(
default = "None",
value_type = "str",
example = r#"
pypy-install-mirror = "https://downloads.python.org/pypy"
"#
)]
pub pypy_install_mirror: Option<String>,
}

impl Default for PythonInstallMirrors {
fn default() -> Self {
PythonInstallMirrors::resolve(None, None)
}
}

impl PythonInstallMirrors {
pub fn resolve(python_mirror: Option<String>, pypy_mirror: Option<String>) -> Self {
let python_mirror_env = std::env::var(EnvVars::UV_PYTHON_INSTALL_MIRROR).ok();
let pypy_mirror_env = std::env::var(EnvVars::UV_PYPY_INSTALL_MIRROR).ok();
PythonInstallMirrors {
python_install_mirror: python_mirror_env.or(python_mirror),
pypy_install_mirror: pypy_mirror_env.or(pypy_mirror),
}
}
}

/// Settings that are specific to the `uv pip` command-line interface.
///
/// These values will be ignored when running commands outside the `uv pip` namespace (e.g.,
Expand Down Expand Up @@ -1544,6 +1603,11 @@ pub struct OptionsWire {
no_binary: Option<bool>,
no_binary_package: Option<Vec<PackageName>>,

// #[serde(flatten)]
// install_mirror: PythonInstallMirrors,
python_install_mirror: Option<String>,
pypy_install_mirror: Option<String>,

// #[serde(flatten)]
// publish: PublishOptions
publish_url: Option<Url>,
Expand Down Expand Up @@ -1581,6 +1645,8 @@ impl From<OptionsWire> for Options {
preview,
python_preference,
python_downloads,
python_install_mirror,
pypy_install_mirror,
concurrent_downloads,
concurrent_builds,
concurrent_installs,
Expand Down Expand Up @@ -1673,6 +1739,10 @@ impl From<OptionsWire> for Options {
override_dependencies,
constraint_dependencies,
environments,
install_mirrors: PythonInstallMirrors::resolve(
python_install_mirror,
pypy_install_mirror,
),
conflicting_groups,
publish: PublishOptions {
publish_url,
Expand Down
8 changes: 8 additions & 0 deletions crates/uv/src/commands/build_frontend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,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_workspace::{DiscoveryOptions, Workspace, WorkspaceError};

Expand All @@ -52,6 +53,7 @@ pub(crate) async fn build_frontend(
build_constraints: Vec<RequirementsSource>,
hash_checking: Option<HashCheckingMode>,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
no_config: bool,
python_preference: PythonPreference,
Expand All @@ -75,6 +77,7 @@ pub(crate) async fn build_frontend(
&build_constraints,
hash_checking,
python.as_deref(),
install_mirrors,
settings.as_ref(),
no_config,
python_preference,
Expand Down Expand Up @@ -116,6 +119,7 @@ async fn build_impl(
build_constraints: &[RequirementsSource],
hash_checking: Option<HashCheckingMode>,
python_request: Option<&str>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettingsRef<'_>,
no_config: bool,
python_preference: PythonPreference,
Expand Down Expand Up @@ -251,6 +255,7 @@ async fn build_impl(
source.clone(),
output_dir,
python_request,
install_mirrors.clone(),
no_config,
workspace.as_ref(),
python_preference,
Expand Down Expand Up @@ -346,6 +351,7 @@ async fn build_package(
source: AnnotatedSource<'_>,
output_dir: Option<&Path>,
python_request: Option<&str>,
install_mirrors: PythonInstallMirrors,
no_config: bool,
workspace: Result<&Workspace, &WorkspaceError>,
python_preference: PythonPreference,
Expand Down Expand Up @@ -424,6 +430,8 @@ async fn build_package(
client_builder,
cache,
Some(&PythonDownloadReporter::single(printer)),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();
Expand Down
7 changes: 7 additions & 0 deletions crates/uv/src/commands/project/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use uv_python::{
use uv_requirements::{NamedRequirementsResolver, RequirementsSource, RequirementsSpecification};
use uv_resolver::{FlatIndex, InstallTarget};
use uv_scripts::{Pep723Item, Pep723Script};
use uv_settings::PythonInstallMirrors;
use uv_types::{BuildIsolation, HashStrategy};
use uv_warnings::warn_user_once;
use uv_workspace::pyproject::{DependencyType, Source, SourceError};
Expand Down Expand Up @@ -71,6 +72,7 @@ pub(crate) async fn add(
extras: Vec<ExtraName>,
package: Option<PackageName>,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverInstallerSettings,
script: Option<PathBuf>,
python_preference: PythonPreference,
Expand Down Expand Up @@ -140,6 +142,7 @@ pub(crate) async fn add(
} else {
let requires_python = init_script_python_requirement(
python.as_deref(),
install_mirrors.clone(),
project_dir,
false,
python_preference,
Expand Down Expand Up @@ -173,6 +176,8 @@ pub(crate) async fn add(
&client_builder,
cache,
Some(&reporter),
install_mirrors.python_install_mirror,
install_mirrors.pypy_install_mirror,
)
.await?
.into_interpreter();
Expand Down Expand Up @@ -227,6 +232,7 @@ pub(crate) async fn add(
connectivity,
native_tls,
allow_insecure_host,
install_mirrors.clone(),
no_config,
cache,
printer,
Expand All @@ -240,6 +246,7 @@ pub(crate) async fn add(
let venv = project::get_or_init_environment(
project.workspace(),
python.as_deref().map(PythonRequest::parse),
install_mirrors.clone(),
python_preference,
python_downloads,
connectivity,
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/commands/project/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use anyhow::{Context, Result};
use itertools::Itertools;
use owo_colors::OwoColorize;
use std::path::{Path, PathBuf};
use uv_settings::PythonInstallMirrors;

use uv_cache::Cache;
use uv_client::Connectivity;
Expand Down Expand Up @@ -42,6 +43,7 @@ pub(crate) async fn export(
frozen: bool,
include_header: bool,
python: Option<String>,
install_mirrors: PythonInstallMirrors,
settings: ResolverSettings,
python_preference: PythonPreference,
python_downloads: PythonDownloads,
Expand Down Expand Up @@ -107,6 +109,7 @@ pub(crate) async fn export(
connectivity,
native_tls,
allow_insecure_host,
install_mirrors,
no_config,
cache,
printer,
Expand Down
Loading

0 comments on commit 2ea81b3

Please sign in to comment.