Merge branch 'fix/idf_tools_download_issues' into 'master'

Tools: Improve download error messages coming from idf_tools.py

Closes IDFGH-8120

See merge request espressif/esp-idf!20325
pull/9900/head^2
Roland Dobai 2022-09-26 16:55:48 +08:00
commit 02605f1a31
1 zmienionych plików z 31 dodań i 12 usunięć

Wyświetl plik

@ -248,6 +248,25 @@ def info(text, f=None, *args): # type: (str, Optional[IO[str]], str) -> None
f.write(text + '\n', *args) f.write(text + '\n', *args)
def print_hints_on_download_error(err): # type: (str) -> None
info('Please make sure you have a working Internet connection.')
if 'CERTIFICATE' in err:
info('Certificate issues are usually caused by an outdated certificate database on your computer.')
info('Please check the documentation of your operating system for how to upgrade it.')
if sys.platform == 'darwin':
info('Running "./Install\\ Certificates.command" might be able to fix this issue.')
info('Running "{} -m pip install --upgrade certifi" can also resolve this issue in some cases.'.format(sys.executable))
# Certificate issue on Windows can be hidden under different errors which might be even translated,
# e.g. "[WinError -2146881269] ASN1 valor de tag inválido encontrado"
if sys.platform == 'win32':
info('By downloading and using the offline installer from https://dl.espressif.com/dl/esp-idf '
'you might be able to work around this issue.')
def run_cmd_check_output(cmd, input_text=None, extra_paths=None): def run_cmd_check_output(cmd, input_text=None, extra_paths=None):
# type: (List[str], Optional[str], Optional[List[str]]) -> bytes # type: (List[str], Optional[str], Optional[List[str]]) -> bytes
# If extra_paths is given, locate the executable in one of these directories. # If extra_paths is given, locate the executable in one of these directories.
@ -404,7 +423,7 @@ def urlretrieve_ctx(url, filename, reporthook=None, data=None, context=None):
return result return result
def download(url, destination): # type: (str, str) -> None def download(url, destination): # type: (str, str) -> Optional[Exception]
info(f'Downloading {url}') info(f'Downloading {url}')
info(f'Destination: {destination}') info(f'Destination: {destination}')
try: try:
@ -422,10 +441,10 @@ def download(url, destination): # type: (str, str) -> None
urlretrieve_ctx(url, destination, report_progress if not global_non_interactive else None, context=ctx) urlretrieve_ctx(url, destination, report_progress if not global_non_interactive else None, context=ctx)
sys.stdout.write('\rDone\n') sys.stdout.write('\rDone\n')
return None
except Exception as e: except Exception as e:
# urlretrieve could throw different exceptions, e.g. IOError when the server is down # urlretrieve could throw different exceptions, e.g. IOError when the server is down
# Errors are ignored because the downloaded file is checked a couple of lines later. return e
warn('Download failure {}'.format(e))
finally: finally:
sys.stdout.flush() sys.stdout.flush()
@ -485,10 +504,6 @@ class ToolExecError(RuntimeError):
pass pass
class DownloadError(RuntimeError):
pass
class IDFToolDownload(object): class IDFToolDownload(object):
def __init__(self, platform_name, url, size, sha256): # type: (str, str, int, str) -> None def __init__(self, platform_name, url, size, sha256): # type: (str, str, int, str) -> None
self.platform_name = platform_name self.platform_name = platform_name
@ -742,7 +757,7 @@ class IDFTool(object):
download_obj = self.versions[version].get_download_for_platform(self._platform) download_obj = self.versions[version].get_download_for_platform(self._platform)
if not download_obj: if not download_obj:
fatal('No packages for tool {} platform {}!'.format(self.name, self._platform)) fatal('No packages for tool {} platform {}!'.format(self.name, self._platform))
raise DownloadError() raise SystemExit(1)
url = download_obj.url url = download_obj.url
archive_name = os.path.basename(url) archive_name = os.path.basename(url)
@ -760,8 +775,9 @@ class IDFTool(object):
downloaded = False downloaded = False
local_temp_path = local_path + '.tmp' local_temp_path = local_path + '.tmp'
for retry in range(DOWNLOAD_RETRY_COUNT): for retry in range(DOWNLOAD_RETRY_COUNT):
download(url, local_temp_path) err = download(url, local_temp_path)
if not os.path.isfile(local_temp_path) or not self.check_download_file(download_obj, local_temp_path): if not os.path.isfile(local_temp_path) or not self.check_download_file(download_obj, local_temp_path):
warn('Download failure: {}'.format(err))
warn('Failed to download {} to {}'.format(url, local_temp_path)) warn('Failed to download {} to {}'.format(url, local_temp_path))
continue continue
rename_with_retry(local_temp_path, local_path) rename_with_retry(local_temp_path, local_path)
@ -769,7 +785,8 @@ class IDFTool(object):
break break
if not downloaded: if not downloaded:
fatal('Failed to download, and retry count has expired') fatal('Failed to download, and retry count has expired')
raise DownloadError() print_hints_on_download_error(str(err))
raise SystemExit(1)
def install(self, version): # type: (str) -> None def install(self, version): # type: (str) -> None
# Currently this is called after calling 'download' method, so here are a few asserts # Currently this is called after calling 'download' method, so here are a few asserts
@ -1880,8 +1897,9 @@ def get_constraints(idf_version): # type: (str) -> str
pass pass
for _ in range(DOWNLOAD_RETRY_COUNT): for _ in range(DOWNLOAD_RETRY_COUNT):
download(constraint_url, temp_path) err = download(constraint_url, temp_path)
if not os.path.isfile(temp_path): if not os.path.isfile(temp_path):
warn('Download failure: {}'.format(err))
warn('Failed to download {} to {}'.format(constraint_url, temp_path)) warn('Failed to download {} to {}'.format(constraint_url, temp_path))
continue continue
if os.path.isfile(constraint_path): if os.path.isfile(constraint_path):
@ -1895,8 +1913,9 @@ def get_constraints(idf_version): # type: (str) -> str
return constraint_path return constraint_path
else: else:
fatal('Failed to download, and retry count has expired') fatal('Failed to download, and retry count has expired')
print_hints_on_download_error(str(err))
info('See the help on how to disable constraints in order to work around this issue.') info('See the help on how to disable constraints in order to work around this issue.')
raise DownloadError() raise SystemExit(1)
def install_legacy_python_virtualenv(path): # type: (str) -> None def install_legacy_python_virtualenv(path): # type: (str) -> None