downloadablefilesource: handle exceptions in _download_file
Don't rely on the ability to get pickled exceptions from blocking_activity
as that doesn't seem to work with any supported python version.
May help with https://github.com/apache/buildstream/issues/1766
diff --git a/src/buildstream/downloadablefilesource.py b/src/buildstream/downloadablefilesource.py
index d36da67..2e261cb 100644
--- a/src/buildstream/downloadablefilesource.py
+++ b/src/buildstream/downloadablefilesource.py
@@ -97,22 +97,36 @@
if etag is not None:
request.add_header("If-None-Match", etag)
- with contextlib.closing(opener.open(request)) as response:
- info = response.info()
+ try:
+ with contextlib.closing(opener.open(request)) as response:
+ info = response.info()
- # some servers don't honor the 'If-None-Match' header
- if etag and info["ETag"] == etag:
- return None, None
+ # some servers don't honor the 'If-None-Match' header
+ if etag and info["ETag"] == etag:
+ return None, None, None
- etag = info["ETag"]
+ etag = info["ETag"]
- filename = info.get_filename(default_name)
- filename = os.path.basename(filename)
- local_file = os.path.join(directory, filename)
- with open(local_file, "wb") as dest:
- shutil.copyfileobj(response, dest)
+ filename = info.get_filename(default_name)
+ filename = os.path.basename(filename)
+ local_file = os.path.join(directory, filename)
+ with open(local_file, "wb") as dest:
+ shutil.copyfileobj(response, dest)
- return local_file, etag
+ except urllib.error.HTTPError as e:
+ if e.code == 304:
+ # 304 Not Modified.
+ # Because we use etag only for matching ref, currently specified ref is what
+ # we would have downloaded.
+ return None, None, None
+
+ return None, None, str(e)
+ except (urllib.error.URLError, urllib.error.ContentTooShortError, OSError, ValueError) as e:
+ # Note that urllib.request.Request in the try block may throw a
+ # ValueError for unknown url types, so we handle it here.
+ return None, None, str(e)
+
+ return local_file, etag, None
class DownloadableFileSource(Source):
@@ -206,50 +220,39 @@
def _ensure_mirror(self, activity_name: str):
# Downloads from the url and caches it according to its sha256sum.
- try:
- with self.tempdir() as td:
- # We do not use etag in case what we have in cache is
- # not matching ref in order to be able to recover from
- # corrupted download.
- if self.ref and not self.is_cached():
- # Do not re-download the file if the ETag matches.
- etag = self._get_etag(self.ref)
- else:
- etag = None
+ with self.tempdir() as td:
+ # We do not use etag in case what we have in cache is
+ # not matching ref in order to be able to recover from
+ # corrupted download.
+ if self.ref and not self.is_cached():
+ # Do not re-download the file if the ETag matches.
+ etag = self._get_etag(self.ref)
+ else:
+ etag = None
- local_file, new_etag = self.blocking_activity(
- _download_file, (self.__get_urlopener(), self.url, etag, td), activity_name
- )
+ local_file, new_etag, error = self.blocking_activity(
+ _download_file, (self.__get_urlopener(), self.url, etag, td), activity_name
+ )
- if local_file is None:
- return self.ref
+ if error:
+ raise SourceError("{}: Error mirroring {}: {}".format(self, self.url, error), temporary=True)
- # Make sure url-specific mirror dir exists.
- if not os.path.isdir(self._mirror_dir):
- os.makedirs(self._mirror_dir)
-
- # Store by sha256sum
- sha256 = utils.sha256sum(local_file)
- # Even if the file already exists, move the new file over.
- # In case the old file was corrupted somehow.
- os.rename(local_file, self._get_mirror_file(sha256))
-
- if new_etag:
- self._store_etag(sha256, new_etag)
- return sha256
-
- except urllib.error.HTTPError as e:
- if e.code == 304:
- # 304 Not Modified.
- # Because we use etag only for matching ref, currently specified ref is what
- # we would have downloaded.
+ if local_file is None:
return self.ref
- raise SourceError("{}: Error mirroring {}: {}".format(self, self.url, e), temporary=True) from e
- except (urllib.error.URLError, urllib.error.ContentTooShortError, OSError, ValueError) as e:
- # Note that urllib.request.Request in the try block may throw a
- # ValueError for unknown url types, so we handle it here.
- raise SourceError("{}: Error mirroring {}: {}".format(self, self.url, e), temporary=True) from e
+ # Make sure url-specific mirror dir exists.
+ if not os.path.isdir(self._mirror_dir):
+ os.makedirs(self._mirror_dir)
+
+ # Store by sha256sum
+ sha256 = utils.sha256sum(local_file)
+ # Even if the file already exists, move the new file over.
+ # In case the old file was corrupted somehow.
+ os.rename(local_file, self._get_mirror_file(sha256))
+
+ if new_etag:
+ self._store_etag(sha256, new_etag)
+ return sha256
def _get_mirror_file(self, sha=None):
if sha is not None: