From 3bbb758bc5ff70e97ae0629830513f62cf4ffaa2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 23 Sep 2019 06:01:09 +0200 Subject: [PATCH] idf_tools.py: add workaround for PermissionError in os.rename Closes https://github.com/espressif/esp-idf/issues/4063 Closes https://github.com/espressif/esp-idf/issues/3819 --- tools/idf_tools.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/tools/idf_tools.py b/tools/idf_tools.py index ef292cf77b..a04cc1c9cf 100755 --- a/tools/idf_tools.py +++ b/tools/idf_tools.py @@ -61,6 +61,12 @@ try: except ImportError: from urllib import urlretrieve +try: + from exceptions import WindowsError +except ImportError: + class WindowsError(OSError): + pass + TOOLS_FILE = 'tools/tools.json' TOOLS_SCHEMA_FILE = 'tools/tools_schema.json' @@ -254,13 +260,34 @@ def unpack(filename, destination): archive_obj.extractall(destination) +# Sometimes renaming a directory on Windows (randomly?) causes a PermissionError. +# This is confirmed to be a workaround: +# https://github.com/espressif/esp-idf/issues/3819#issuecomment-515167118 +# https://github.com/espressif/esp-idf/issues/4063#issuecomment-531490140 +# https://stackoverflow.com/a/43046729 +def rename_with_retry(path_from, path_to): + if sys.platform.startswith('win'): + retry_count = 100 + else: + retry_count = 1 + + for retry in range(retry_count): + try: + os.rename(path_from, path_to) + return + except (OSError, WindowsError): # WindowsError until Python 3.3, then OSError + if retry == retry_count - 1: + raise + warn('Rename {} to {} failed, retrying...'.format(path_from, path_to)) + + def strip_container_dirs(path, levels): assert levels > 0 # move the original directory out of the way (add a .tmp suffix) tmp_path = path + '.tmp' if os.path.exists(tmp_path): shutil.rmtree(tmp_path) - os.rename(path, tmp_path) + rename_with_retry(path, tmp_path) os.mkdir(path) base_path = tmp_path # walk given number of levels down @@ -276,7 +303,7 @@ def strip_container_dirs(path, levels): for name in contents: move_from = os.path.join(base_path, name) move_to = os.path.join(path, name) - os.rename(move_from, move_to) + rename_with_retry(move_from, move_to) shutil.rmtree(tmp_path) @@ -544,7 +571,7 @@ class IDFTool(object): if not self.check_download_file(download_obj, local_temp_path): warn('Failed to download file {}'.format(local_temp_path)) continue - os.rename(local_temp_path, local_path) + rename_with_retry(local_temp_path, local_path) downloaded = True break if not downloaded: