| #!/usr/bin/env python3 | |
| # Copyright 2021 The IREE Authors | |
| # | |
| # Licensed under the Apache License v2.0 with LLVM Exceptions. | |
| # See https://llvm.org/LICENSE.txt for license information. | |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| """Downloads a file from the web and decompresses it if necessary. NEVER Use | |
| this tool to download from untrusted sources, it doesn't unpack the file | |
| safely. | |
| """ | |
| import argparse | |
| import gzip | |
| import os | |
| import shutil | |
| import tarfile | |
| import urllib.request | |
| import urllib.error | |
| import logging | |
| import time | |
| DEFAULT_MAX_TRIES = 3 | |
| RETRY_COOLDOWN_TIME = 1.0 | |
| def parse_arguments(): | |
| """Parses command line arguments.""" | |
| parser = argparse.ArgumentParser( | |
| description="Downloads a file from the web " | |
| "and decompresses it if necessary. NEVER Use this tool to download from " | |
| "untrusted sources, it doesn't unpack the file safely." | |
| ) | |
| parser.add_argument( | |
| "source_url", type=str, metavar="<source-url>", help="Source URL to download" | |
| ) | |
| parser.add_argument( | |
| "-o", | |
| "--output", | |
| type=str, | |
| required=True, | |
| metavar="<output-file>", | |
| help="Output file path", | |
| ) | |
| parser.add_argument( | |
| "--unpack", | |
| action="store_true", | |
| default=False, | |
| help="Unpack the downloaded file if it's an archive", | |
| ) | |
| parser.add_argument( | |
| "--max-tries", | |
| metavar="<max-tries>", | |
| type=int, | |
| default=DEFAULT_MAX_TRIES, | |
| help="Number of tries before giving up", | |
| ) | |
| return parser.parse_args() | |
| def download_and_extract(source_url: str, output: str, unpack: bool): | |
| # Open the URL and get the file-like streaming object. | |
| with urllib.request.urlopen(source_url) as response: | |
| if response.status != 200: | |
| raise RuntimeError( | |
| f"Failed to download file with status {response.status} {response.msg}" | |
| ) | |
| if unpack: | |
| if source_url.endswith(".tar.gz"): | |
| # Open tar.gz in the streaming mode. | |
| with tarfile.open(fileobj=response, mode="r|*") as tar_file: | |
| if os.path.exists(output): | |
| shutil.rmtree(output) | |
| os.makedirs(output) | |
| tar_file.extractall(output) | |
| return | |
| elif source_url.endswith(".gz"): | |
| # Open gzip from a file-like object, which will be in the streaming mode. | |
| with gzip.open(filename=response, mode="rb") as input_file: | |
| with open(output, "wb") as output_file: | |
| shutil.copyfileobj(input_file, output_file) | |
| return | |
| # Fallback to download the file only. | |
| with open(output, "wb") as output_file: | |
| # Streaming copy. | |
| shutil.copyfileobj(response, output_file) | |
| def main(args): | |
| output_dir = os.path.dirname(args.output) | |
| if not os.path.isdir(output_dir): | |
| os.makedirs(output_dir) | |
| remaining_tries = args.max_tries | |
| while remaining_tries > 0: | |
| try: | |
| download_and_extract(args.source_url, args.output, args.unpack) | |
| break | |
| except (ConnectionResetError, ConnectionRefusedError, urllib.error.URLError): | |
| remaining_tries -= 1 | |
| if remaining_tries == 0: | |
| raise | |
| else: | |
| logging.warning( | |
| f"Connection error, remaining {remaining_tries} tries", | |
| exc_info=True, | |
| ) | |
| time.sleep(RETRY_COOLDOWN_TIME) | |
| if __name__ == "__main__": | |
| main(parse_arguments()) |