@@ -39,6 +39,12 @@ use crate::managed::ManagedPythonInstallation;
3939use crate :: platform:: { self , Arch , Libc , Os } ;
4040use 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 ) ]
4349pub 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
11261156impl Display for ManagedPythonDownload {
@@ -1201,7 +1231,7 @@ where
12011231async 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