From f7110e0a071e29425104ddf4c0ef47ff5d5dc51c Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 7 Aug 2024 21:34:19 -0400 Subject: [PATCH] Enable mirror for `python-build-standalone` downloads (#5719) ## Summary This came up again recently, so decided to do it real quick. Closes https://github.com/astral-sh/uv/issues/5224. --- README.md | 10 +++++ crates/uv-python/src/downloads.rs | 51 +++++++++++++++++++++--- crates/uv/src/commands/python/install.rs | 4 +- docs/configuration/environment.md | 10 +++++ 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 693385246..8709d82b2 100644 --- a/README.md +++ b/README.md @@ -618,6 +618,16 @@ uv accepts the following command-line arguments as environment variables: packages. - `UV_EXCLUDE_NEWER`: Equivalent to the `--exclude-newer` command-line argument. If set, uv will exclude distributions published after the specified date. +- `UV_PYTHON_INSTALL_MIRROR`: 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`. +- `UV_PYPY_INSTALL_MIRROR`: Managed PyPy installations are downloaded from + [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`. In each case, the corresponding command-line argument takes precedence over an environment variable. diff --git a/crates/uv-python/src/downloads.rs b/crates/uv-python/src/downloads.rs index 59dd62598..ffea6b7b8 100644 --- a/crates/uv-python/src/downloads.rs +++ b/crates/uv-python/src/downloads.rs @@ -36,10 +36,10 @@ pub enum Error { InvalidPythonVersion(String), #[error("Invalid request key (too many parts): {0}")] TooManyParts(String), - #[error("Download failed")] + #[error(transparent)] NetworkError(#[from] WrappedReqwestError), - #[error("Download failed")] - NetworkMiddlewareError(#[source] anyhow::Error), + #[error(transparent)] + NetworkMiddlewareError(#[from] anyhow::Error), #[error("Failed to extract archive: {0}")] ExtractError(String, #[source] uv_extract::Error), #[error("Failed to hash installation")] @@ -70,6 +70,10 @@ pub enum Error { InvalidRequestPlatform(#[from] platform::Error), #[error("No download found for request: {}", _0.green())] NoDownloadFound(PythonDownloadRequest), + #[error( + "A mirror was provided via `{0}`, but the URL does not match the expected format: {0}" + )] + Mirror(&'static str, &'static str), } #[derive(Debug, PartialEq)] @@ -410,7 +414,7 @@ impl ManagedPythonDownload { cache: &Cache, reporter: Option<&dyn Reporter>, ) -> Result { - let url = Url::parse(self.url)?; + let url = self.download_url()?; let path = parent_path.join(self.key().to_string()); // If it already exists, return it @@ -433,8 +437,8 @@ impl ManagedPythonDownload { let temp_dir = tempfile::tempdir_in(cache.root()).map_err(Error::DownloadDirError)?; debug!( - "Downloading {url} to temporary location {}", - temp_dir.path().display() + "Downloading {url} to temporary location: {}", + temp_dir.path().simplified().display() ); let stream = response @@ -525,6 +529,41 @@ impl ManagedPythonDownload { pub fn python_version(&self) -> PythonVersion { self.key.version() } + + /// 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 { + match self.key.implementation { + LenientImplementationName::Known(ImplementationName::CPython) => { + if let Ok(mirror) = std::env::var("UV_PYTHON_INSTALL_MIRROR") { + let Some(suffix) = self.url.strip_prefix( + "https://github.com/indygreg/python-build-standalone/releases/download/", + ) else { + return Err(Error::Mirror("UV_PYTHON_INSTALL_MIRROR", self.url)); + }; + return Ok(Url::parse( + format!("{}/{}", mirror.trim_end_matches('/'), suffix).as_str(), + )?); + } + } + + LenientImplementationName::Known(ImplementationName::PyPy) => { + if let Ok(mirror) = std::env::var("UV_PYPY_INSTALL_MIRROR") { + let Some(suffix) = self.url.strip_prefix("https://downloads.python.org/pypy/") + else { + return Err(Error::Mirror("UV_PYPY_INSTALL_MIRROR", self.url)); + }; + return Ok(Url::parse( + format!("{}/{}", mirror.trim_end_matches('/'), suffix).as_str(), + )?); + } + } + + _ => {} + } + + Ok(Url::parse(self.url)?) + } } impl From for Error { diff --git a/crates/uv/src/commands/python/install.rs b/crates/uv/src/commands/python/install.rs index dcbd15c57..7d71a2245 100644 --- a/crates/uv/src/commands/python/install.rs +++ b/crates/uv/src/commands/python/install.rs @@ -235,9 +235,9 @@ pub(crate) async fn install( for (key, err) in errors { writeln!( printer.stderr(), - "Failed to install {}: {}", + "{}: Failed to install {}: {err}", + "error".red().bold(), key.green(), - err )?; } return Ok(ExitStatus::Failure); diff --git a/docs/configuration/environment.md b/docs/configuration/environment.md index b01531494..3948f9c7c 100644 --- a/docs/configuration/environment.md +++ b/docs/configuration/environment.md @@ -53,6 +53,16 @@ uv accepts the following command-line arguments as environment variables: packages. - `UV_EXCLUDE_NEWER`: Equivalent to the `--exclude-newer` command-line argument. If set, uv will exclude distributions published after the specified date. +- `UV_PYTHON_INSTALL_MIRROR`: 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`. +- `UV_PYPY_INSTALL_MIRROR`: Managed PyPy installations are downloaded from + [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`. In each case, the corresponding command-line argument takes precedence over an environment variable.