Skip to content

Commit 7b7d6eb

Browse files
committed
Factor out downloading of files to EasyBlock method
1 parent 83d9443 commit 7b7d6eb

1 file changed

Lines changed: 76 additions & 60 deletions

File tree

easybuild/framework/easyblock.py

Lines changed: 76 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def __init__(self, ec, logfile=None):
217217
self.skip = None
218218
self.module_extra_extensions = '' # extra stuff for module file required by extensions
219219

220-
# indicates whether or not this instance represents an extension or not;
220+
# indicates whether or not this instance represents an extension
221221
# may be set to True by ExtensionEasyBlock
222222
self.is_extension = False
223223

@@ -1023,64 +1023,18 @@ def obtain_file(self, filename, extension=False, urls=None, download_filename=No
10231023

10241024
mkdir(targetdir, parents=True)
10251025

1026-
for url in source_urls:
1027-
1028-
if extension:
1029-
targetpath = os.path.join(targetdir, "extensions", filename)
1030-
else:
1031-
targetpath = os.path.join(targetdir, filename)
1032-
1033-
url_filename = download_filename or filename
1034-
1035-
if isinstance(url, str):
1036-
if url[-1] in ['=', '/']:
1037-
fullurl = "%s%s" % (url, url_filename)
1038-
else:
1039-
fullurl = "%s/%s" % (url, url_filename)
1040-
elif isinstance(url, tuple):
1041-
# URLs that require a suffix, e.g., SourceForge download links
1042-
# e.g. http://sourceforge.net/projects/math-atlas/files/Stable/3.8.4/atlas3.8.4.tar.bz2/download
1043-
fullurl = "%s/%s/%s" % (url[0], url_filename, url[1])
1044-
else:
1045-
self.log.warning("Source URL %s is of unknown type, so ignoring it." % url)
1046-
continue
1047-
1048-
# PyPI URLs may need to be converted due to change in format of these URLs,
1049-
# cfr. https://bitbucket.org/pypa/pypi/issues/438
1050-
if PYPI_PKG_URL_PATTERN in fullurl and not is_alt_pypi_url(fullurl):
1051-
alt_url = derive_alt_pypi_url(fullurl)
1052-
if alt_url:
1053-
_log.debug("Using alternative PyPI URL for %s: %s", fullurl, alt_url)
1054-
fullurl = alt_url
1055-
else:
1056-
_log.debug("Failed to derive alternative PyPI URL for %s, so retaining the original",
1057-
fullurl)
1058-
1059-
if self.dry_run:
1060-
self.dry_run_msg(" * %s will be downloaded to %s", filename, targetpath)
1061-
if extension and urls:
1062-
# extensions typically have custom source URLs specified, only mention first
1063-
self.dry_run_msg(" (from %s, ...)", fullurl)
1064-
downloaded = True
1065-
1066-
else:
1067-
self.log.debug("Trying to download file %s from %s to %s ..." % (filename, fullurl, targetpath))
1068-
downloaded = False
1069-
try:
1070-
if download_file(filename, fullurl, targetpath):
1071-
downloaded = True
1026+
if extension:
1027+
target_path = os.path.join(targetdir, "extensions", filename)
1028+
else:
1029+
target_path = os.path.join(targetdir, filename)
1030+
downloaded, failed_urls = self.download_file(target_path, download_filename=download_filename or filename,
1031+
urls=source_urls,
1032+
# Only log when extensions have explicit URLs
1033+
log_url_in_dry_run=extension and urls)
1034+
if downloaded:
1035+
return target_path
10721036

1073-
except IOError as err:
1074-
self.log.debug("Failed to download %s from %s: %s" % (filename, url, err))
1075-
failedpaths.append(fullurl)
1076-
continue
1077-
1078-
if downloaded:
1079-
# if fetching from source URL worked, we're done
1080-
self.log.info("Successfully downloaded source file %s from %s" % (filename, fullurl))
1081-
return targetpath
1082-
else:
1083-
failedpaths.append(fullurl)
1037+
failedpaths.extend(failed_urls)
10841038

10851039
if self.dry_run:
10861040
self.dry_run_msg(" * %s (MISSING)", filename)
@@ -1107,20 +1061,82 @@ def obtain_file(self, filename, extension=False, urls=None, download_filename=No
11071061
self.log.warning(error_msg, filename)
11081062
return None
11091063

1064+
def download_file(self, target_path, download_filename, urls, log_url_in_dry_run):
1065+
"""Try downloading a file from multiple source URLs, until one works.
1066+
1067+
:param target_path: Full path where the file should be stored (including filename)
1068+
:param download_filename: Name of the file on the server (which may differ from the target filename)
1069+
:param source_urls: list of URLs to try downloading from
1070+
:param log_url_in_dry_run: whether to log each URL in dry-run mode
1071+
1072+
Returns a tuple of (success, failed_urls), where
1073+
success is True if the file was successfully downloaded from any of the source URLs
1074+
failed_urls is a list of (full) URLs that were attempted but failed to download from
1075+
"""
1076+
failed_urls = []
1077+
filename = os.path.basename(target_path)
1078+
for url in urls:
1079+
if isinstance(url, str):
1080+
if url[-1] in ['=', '/']:
1081+
full_url = "%s%s" % (url, download_filename)
1082+
else:
1083+
full_url = "%s/%s" % (url, download_filename)
1084+
elif isinstance(url, tuple):
1085+
# URLs that require a suffix, e.g., SourceForge download links
1086+
# e.g. http://sourceforge.net/projects/math-atlas/files/Stable/3.8.4/atlas3.8.4.tar.bz2/download
1087+
full_url = "%s/%s/%s" % (url[0], download_filename, url[1])
1088+
else:
1089+
self.log.warning("Source URL %s is of unknown type, so ignoring it." % url)
1090+
continue
1091+
1092+
# PyPI URLs may need to be converted due to change in format of these URLs,
1093+
# cfr. https://bitbucket.org/pypa/pypi/issues/438
1094+
if PYPI_PKG_URL_PATTERN in full_url and not is_alt_pypi_url(full_url):
1095+
alt_url = derive_alt_pypi_url(full_url)
1096+
if alt_url:
1097+
_log.debug("Using alternative PyPI URL for %s: %s", full_url, alt_url)
1098+
full_url = alt_url
1099+
else:
1100+
_log.debug("Failed to derive alternative PyPI URL for %s, so retaining the original",
1101+
full_url)
1102+
1103+
if self.dry_run:
1104+
msg = f" * {filename} will be downloaded to {target_path}"
1105+
if self.log_url_in_dry_run:
1106+
msg += f" from {full_url}"
1107+
self.dry_run_msg(msg)
1108+
downloaded = True
1109+
1110+
else:
1111+
self.log.debug("Trying to download file %s from %s to %s ..." % (filename, full_url, target_path))
1112+
try:
1113+
downloaded = download_file(filename, full_url, target_path)
1114+
except IOError as err:
1115+
self.log.debug("Failed to download %s from %s: %s" % (filename, url, err))
1116+
downloaded = False
1117+
1118+
if downloaded:
1119+
# if fetching from source URL worked, we're done
1120+
self.log.info("Successfully downloaded source file %s from %s" % (filename, full_url))
1121+
return True, failed_urls
1122+
else:
1123+
failed_urls.append(full_url)
1124+
return False, failed_urls
1125+
11101126
#
11111127
# GETTER/SETTER UTILITY FUNCTIONS
11121128
#
11131129
@property
11141130
def name(self):
11151131
"""
1116-
Shortcut the get the module name.
1132+
Shortcut to get the module name.
11171133
"""
11181134
return self.cfg['name']
11191135

11201136
@property
11211137
def version(self):
11221138
"""
1123-
Shortcut the get the module version.
1139+
Shortcut to get the module version.
11241140
"""
11251141
return self.cfg['version']
11261142

0 commit comments

Comments
 (0)