11#!/usr/bin/env python3
22
33import argparse
4- import contextlib
5- import hashlib
6- import io
7- import json
84import os
95import pathlib
106import shutil
1511import zipfile
1612
1713
18- # Mapping of binary dependency tag to GitHub release asset ID
19- TAG_TO_ASSET_ID = {
20- 'llvm-20.1.8.0' : 301710576 ,
21- }
2214
2315
2416def request_with_retry (request_func , * args , max_retries = 7 ,
@@ -46,15 +38,6 @@ def retrieve_with_retries(download_location, output_path, reporthook):
4638 )
4739
4840
49- def get_with_retries (url , headers ):
50- req = urllib .request .Request (url = url , headers = headers , method = 'GET' )
51- return request_with_retry (
52- urllib .request .urlopen ,
53- req ,
54- err_msg = f'Request to { url } failed.'
55- )
56-
57-
5841def fetch_zip (commit_hash , zip_dir , * , org = 'python' , binary = False , verbose = False ):
5942 repo = 'cpython-bin-deps' if binary else 'cpython-source-deps'
6043 url = f'https://github.com/{ org } /{ repo } /archive/{ commit_hash } .zip'
@@ -70,42 +53,14 @@ def fetch_zip(commit_hash, zip_dir, *, org='python', binary=False, verbose=False
7053 return filename
7154
7255
73- def fetch_release_asset (asset_id , output_path , org ):
74- """Download a GitHub release asset.
75-
76- Release assets need the Content-Type header set to
77- application/octet-stream to download the binary, so we can't use
78- urlretrieve. Code here is based on urlretrieve.
79- """
80- url = f'https://api.github.com/repos/{ org } /cpython-bin-deps/releases/assets/{ asset_id } '
81- metadata_resp = get_with_retries (url ,
82- headers = {'Accept' : 'application/vnd.github+json' })
83- json_data = json .loads (metadata_resp .read ())
84- hash_info = json_data .get ('digest' )
85- if not hash_info :
86- raise RuntimeError (f'Release asset { asset_id } missing digest field in metadata' )
87- algorithm , hashsum = hash_info .split (':' )
88- if algorithm != 'sha256' :
89- raise RuntimeError (f'Unknown hash algorithm { algorithm } for asset { asset_id } ' )
90- with contextlib .closing (
91- get_with_retries (url , headers = {'Accept' : 'application/octet-stream' })
92- ) as resp :
93- hasher = hashlib .sha256 ()
94- with open (output_path , 'wb' ) as fp :
95- while block := resp .read (io .DEFAULT_BUFFER_SIZE ):
96- hasher .update (block )
97- fp .write (block )
98- if hasher .hexdigest () != hashsum :
99- raise RuntimeError ('Downloaded content hash did not match!' )
100-
101-
10256def fetch_release (tag , tarball_dir , * , org = 'python' , verbose = False ):
57+ url = f'https://github.com/{ org } /cpython-bin-deps/releases/download/{ tag } /{ tag } .tar.xz'
58+ reporthook = None
59+ if verbose :
60+ reporthook = print
10361 tarball_dir .mkdir (parents = True , exist_ok = True )
104- asset_id = TAG_TO_ASSET_ID .get (tag )
105- if asset_id is None :
106- raise ValueError (f'Unknown tag for binary dependencies { tag } ' )
10762 output_path = tarball_dir / f'{ tag } .tar.xz'
108- fetch_release_asset ( asset_id , output_path , org )
63+ retrieve_with_retries ( url , output_path , reporthook )
10964 return output_path
11065
11166
@@ -126,6 +81,8 @@ def parse_args():
12681 p .add_argument ('-v' , '--verbose' , action = 'store_true' )
12782 p .add_argument ('-b' , '--binary' , action = 'store_true' ,
12883 help = 'Is the dependency in the binary repo?' )
84+ p .add_argument ('-r' , '--release' , action = 'store_true' ,
85+ help = 'Download from GitHub release assets instead of branch' )
12986 p .add_argument ('-O' , '--organization' ,
13087 help = 'Organization owning the deps repos' , default = 'python' )
13188 p .add_argument ('-e' , '--externals-dir' , type = pathlib .Path ,
@@ -149,7 +106,7 @@ def main():
149106
150107 # Determine download method: release artifacts for large deps (like LLVM),
151108 # otherwise zip download from GitHub branches
152- if args .tag in TAG_TO_ASSET_ID :
109+ if args .release :
153110 tarball_path = fetch_release (
154111 args .tag ,
155112 args .externals_dir / 'tarballs' ,
0 commit comments