@@ -26,9 +26,36 @@ def _get_http_response_size(resp: Response) -> Optional[int]:
26
26
except (ValueError , KeyError , TypeError ):
27
27
return None
28
28
29
+ def _get_http_accept_ranges (resp : Response ) -> Optional [str ]:
30
+ return resp .headers .get ("accept-ranges" , None )
31
+
32
+ def _resume_if_incomplete (
33
+ resp : Response ,
34
+ session : PipSession ,
35
+ link : Link
36
+ ) -> Iterable [bytes ]:
37
+ chunks = response_chunks (resp , CONTENT_CHUNK_SIZE )
38
+ received_length = 0
39
+
40
+ for chunk in chunks :
41
+ received_length += len (chunk )
42
+ yield chunk
43
+
44
+ total_length = _get_http_response_size (resp )
45
+ accept_ranges = _get_http_accept_ranges (resp )
46
+
47
+ if total_length is not None and accept_ranges == 'bytes' :
48
+ while received_length < total_length :
49
+ logger .info ("Resuming incomplete download (%s received)" , format_size (received_length ))
50
+ resume_resp = _http_get_download (session , link , range_start = received_length )
51
+ resume_chunks = response_chunks (resume_resp , CONTENT_CHUNK_SIZE )
52
+ for chunk in resume_chunks :
53
+ received_length += len (chunk )
54
+ yield chunk
29
55
30
56
def _prepare_download (
31
57
resp : Response ,
58
+ session : PipSession ,
32
59
link : Link ,
33
60
progress_bar : str ,
34
61
) -> Iterable [bytes ]:
@@ -60,7 +87,7 @@ def _prepare_download(
60
87
else :
61
88
show_progress = False
62
89
63
- chunks = response_chunks (resp , CONTENT_CHUNK_SIZE )
90
+ chunks = _resume_if_incomplete (resp , session , link )
64
91
65
92
if not show_progress :
66
93
return chunks
@@ -112,9 +139,13 @@ def _get_http_response_filename(resp: Response, link: Link) -> str:
112
139
return filename
113
140
114
141
115
- def _http_get_download (session : PipSession , link : Link ) -> Response :
142
+ def _http_get_download (session : PipSession , link : Link , range_start : Optional [ int ] = None ) -> Response :
116
143
target_url = link .url .split ("#" , 1 )[0 ]
117
- resp = session .get (target_url , headers = HEADERS , stream = True )
144
+ if range_start is not None :
145
+ headers = {** HEADERS , "Range" : "bytes={}-" .format (range_start )}
146
+ else :
147
+ headers = HEADERS
148
+ resp = session .get (target_url , headers = headers , stream = True )
118
149
raise_for_status (resp )
119
150
return resp
120
151
@@ -142,7 +173,7 @@ def __call__(self, link: Link, location: str) -> Tuple[str, str]:
142
173
filename = _get_http_response_filename (resp , link )
143
174
filepath = os .path .join (location , filename )
144
175
145
- chunks = _prepare_download (resp , link , self ._progress_bar )
176
+ chunks = _prepare_download (resp , self . _session , link , self ._progress_bar )
146
177
with open (filepath , "wb" ) as content_file :
147
178
for chunk in chunks :
148
179
content_file .write (chunk )
@@ -178,7 +209,7 @@ def __call__(
178
209
filename = _get_http_response_filename (resp , link )
179
210
filepath = os .path .join (location , filename )
180
211
181
- chunks = _prepare_download (resp , link , self ._progress_bar )
212
+ chunks = _prepare_download (resp , self . _session , link , self ._progress_bar )
182
213
with open (filepath , "wb" ) as content_file :
183
214
for chunk in chunks :
184
215
content_file .write (chunk )
0 commit comments