Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Python and PyPy install mirrors configurable in uv.toml #8695

Merged
merged 7 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading