|
24 | 24 | import sys
|
25 | 25 | import tempfile
|
26 | 26 | import urllib.request
|
| 27 | +import http.client |
| 28 | +import time |
27 | 29 | from textwrap import dedent
|
28 | 30 | from typing import Optional, Dict, List, Tuple, \
|
29 | 31 | NewType, BinaryIO, NamedTuple
|
@@ -136,6 +138,20 @@ def __str__(self):
|
136 | 138 | f' found: {self.found_sha256}',
|
137 | 139 | ])
|
138 | 140 |
|
| 141 | +def download_with_retry(url, output_path, retries=3, delay=2): |
| 142 | + for attempt in range(retries): |
| 143 | + try: |
| 144 | + with urllib.request.urlopen(url, timeout=10) as resp: |
| 145 | + with output_path.open('wb') as out_file: |
| 146 | + shutil.copyfileobj(resp, out_file) |
| 147 | + return # success |
| 148 | + except http.client.IncompleteRead as e: |
| 149 | + print(f"IncompleteRead error (attempt {attempt + 1}): {e}") |
| 150 | + except Exception as e: |
| 151 | + print(f"Other error (attempt {attempt + 1}): {e}") |
| 152 | + time.sleep(delay) |
| 153 | + raise Exception(f"Failed to download after {retries} attempts.") |
| 154 | + |
139 | 155 | def package_url(package: PackageName, version: Version) -> str:
|
140 | 156 | return f'http://hackage.haskell.org/package/{package}-{version}/{package}-{version}.tar.gz'
|
141 | 157 |
|
@@ -351,8 +367,7 @@ def fetch_from_plan(plan : FetchPlan, output_dir : Path):
|
351 | 367 | sha = plan[path].sha256
|
352 | 368 | if not output_path.exists():
|
353 | 369 | print(f'Fetching {url}...')
|
354 |
| - with urllib.request.urlopen(url, timeout = 10) as resp: |
355 |
| - shutil.copyfileobj(resp, output_path.open('wb')) |
| 370 | + download_with_retry(url, output_path) |
356 | 371 | verify_sha256(sha, output_path)
|
357 | 372 |
|
358 | 373 | def gen_fetch_plan(info : BootstrapInfo) -> FetchPlan :
|
|
0 commit comments