Skip to content

Commit c3cd5e8

Browse files
committed
Add more retry context to Python downloads
Currently untestable unfortunately
1 parent 2864d47 commit c3cd5e8

File tree

1 file changed

+47
-8
lines changed

1 file changed

+47
-8
lines changed

crates/uv-python/src/downloads.rs

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ use crate::managed::ManagedPythonInstallation;
3939
use crate::platform::{self, Arch, Libc, Os};
4040
use crate::{Interpreter, PythonRequest, PythonVersion, VersionRequest};
4141

42+
/// Retry context for download operations.
43+
#[derive(Clone, Copy)]
44+
pub struct RetryContext {
45+
pub retries: Option<u32>,
46+
}
47+
4248
#[derive(Error, Debug)]
4349
pub enum Error {
4450
#[error(transparent)]
@@ -770,6 +776,10 @@ impl ManagedPythonDownload {
770776
};
771777

772778
// Extract the downloaded archive into a temporary directory.
779+
let retry_context = RetryContext {
780+
// Cached files don't have retries
781+
retries: None,
782+
};
773783
self.extract_reader(
774784
reader,
775785
temp_dir.path(),
@@ -778,6 +788,7 @@ impl ManagedPythonDownload {
778788
size,
779789
reporter,
780790
Direction::Extract,
791+
&retry_context,
781792
)
782793
.await?;
783794
} else {
@@ -788,7 +799,7 @@ impl ManagedPythonDownload {
788799
temp_dir.path().simplified_display()
789800
);
790801

791-
let (reader, size) = read_url(&url, client).await?;
802+
let (reader, size, retry_context) = read_url(&url, client).await?;
792803
self.extract_reader(
793804
reader,
794805
temp_dir.path(),
@@ -797,6 +808,7 @@ impl ManagedPythonDownload {
797808
size,
798809
reporter,
799810
Direction::Download,
811+
&retry_context,
800812
)
801813
.await?;
802814
}
@@ -861,7 +873,7 @@ impl ManagedPythonDownload {
861873
target_cache_file.simplified_display()
862874
);
863875

864-
let (mut reader, size) = read_url(url, client).await?;
876+
let (mut reader, size, retry_context) = read_url(url, client).await?;
865877
let temp_dir = tempfile::tempdir_in(python_builds_dir)?;
866878
let temp_file = temp_dir.path().join("download");
867879

@@ -876,10 +888,13 @@ impl ManagedPythonDownload {
876888
&mut ProgressReader::new(reader, key, reporter),
877889
&mut archive_writer,
878890
)
879-
.await?;
891+
.await
892+
.map_err(|err| Error::Io(err).with_retries(retry_context.retries))?;
880893
reporter.on_request_complete(Direction::Download, key);
881894
} else {
882-
tokio::io::copy(&mut reader, &mut archive_writer).await?;
895+
tokio::io::copy(&mut reader, &mut archive_writer)
896+
.await
897+
.map_err(|err| Error::Io(err).with_retries(retry_context.retries))?;
883898
}
884899

885900
archive_writer.flush().await?;
@@ -900,6 +915,7 @@ impl ManagedPythonDownload {
900915
size: Option<u64>,
901916
reporter: Option<&dyn Reporter>,
902917
direction: Direction,
918+
retry_context: &RetryContext,
903919
) -> Result<(), Error> {
904920
let mut hashers = self
905921
.sha256
@@ -920,7 +936,10 @@ impl ManagedPythonDownload {
920936
.await
921937
.map_err(|err| Error::ExtractError(filename.to_string(), err))?;
922938
}
923-
hasher.finish().await.map_err(Error::HashExhaustion)?;
939+
hasher
940+
.finish()
941+
.await
942+
.map_err(|err| Error::HashExhaustion(err).with_retries(retry_context.retries))?;
924943

925944
// Check the hash
926945
if let Some(expected) = self.sha256 {
@@ -1121,6 +1140,17 @@ impl Error {
11211140
}
11221141
}
11231142
}
1143+
1144+
pub(crate) fn with_retries(self, retries: Option<u32>) -> Self {
1145+
if let Some(retries) = retries {
1146+
Self::NetworkErrorWithRetries {
1147+
err: Box::new(self),
1148+
retries,
1149+
}
1150+
} else {
1151+
self
1152+
}
1153+
}
11241154
}
11251155

11261156
impl Display for ManagedPythonDownload {
@@ -1201,7 +1231,7 @@ where
12011231
async fn read_url(
12021232
url: &Url,
12031233
client: &BaseClient,
1204-
) -> Result<(impl AsyncRead + Unpin, Option<u64>), Error> {
1234+
) -> Result<(impl AsyncRead + Unpin, Option<u64>, RetryContext), Error> {
12051235
let url = DisplaySafeUrl::from(url.clone());
12061236
if url.scheme() == "file" {
12071237
// Loads downloaded distribution from the given `file://` URL.
@@ -1212,7 +1242,12 @@ async fn read_url(
12121242
let size = fs_err::tokio::metadata(&path).await?.len();
12131243
let reader = fs_err::tokio::File::open(&path).await?;
12141244

1215-
Ok((Either::Left(reader), Some(size)))
1245+
let retry_context = RetryContext {
1246+
// File URLs don't have retries
1247+
retries: None,
1248+
};
1249+
1250+
Ok((Either::Left(reader), Some(size), retry_context))
12161251
} else {
12171252
let response = client
12181253
.for_host(&url)
@@ -1238,6 +1273,10 @@ async fn read_url(
12381273
.map_err(io::Error::other)
12391274
.into_async_read();
12401275

1241-
Ok((Either::Right(stream.compat()), size))
1276+
let retry_context = RetryContext {
1277+
retries: retry_count,
1278+
};
1279+
1280+
Ok((Either::Right(stream.compat()), size, retry_context))
12421281
}
12431282
}

0 commit comments

Comments
 (0)