change(idf_tools): switch from old string formatting to f-strings and parentheses

This commit is contained in:
Jan Beran
2024-02-16 16:39:38 +01:00
parent 6e88ba4832
commit 9ebba1b5c2

View File

@@ -96,7 +96,7 @@ IDF_TOOLS_EXPORT_CMD = os.environ.get('IDF_TOOLS_INSTALL_CMD')
IDF_DL_URL = 'https://dl.espressif.com/dl/esp-idf' IDF_DL_URL = 'https://dl.espressif.com/dl/esp-idf'
IDF_PIP_WHEELS_URL = os.environ.get('IDF_PIP_WHEELS_URL', 'https://dl.espressif.com/pypi') IDF_PIP_WHEELS_URL = os.environ.get('IDF_PIP_WHEELS_URL', 'https://dl.espressif.com/pypi')
PYTHON_PLATFORM = platform.system() + '-' + platform.machine() PYTHON_PLATFORM = f'{platform.system()}-{platform.machine()}'
# Identifiers used in tools.json for different platforms. # Identifiers used in tools.json for different platforms.
PLATFORM_WIN32 = 'win32' PLATFORM_WIN32 = 'win32'
@@ -349,7 +349,7 @@ def fatal(text: str, *args: str) -> None:
Writes ERROR: + text to sys.stderr. Writes ERROR: + text to sys.stderr.
""" """
if not g.quiet: if not g.quiet:
sys.stderr.write('ERROR: ' + text + '\n', *args) sys.stderr.write(f'ERROR: {text}\n', *args)
def warn(text: str, *args: str) -> None: def warn(text: str, *args: str) -> None:
@@ -357,7 +357,7 @@ def warn(text: str, *args: str) -> None:
Writes WARNING: + text to sys.stderr. Writes WARNING: + text to sys.stderr.
""" """
if not g.quiet: if not g.quiet:
sys.stderr.write('WARNING: ' + text + '\n', *args) sys.stderr.write(f'WARNING: {text}\n', *args)
def info(text: str, f: Optional[IO[str]]=None, *args: str) -> None: def info(text: str, f: Optional[IO[str]]=None, *args: str) -> None:
@@ -367,7 +367,7 @@ def info(text: str, f: Optional[IO[str]]=None, *args: str) -> None:
if not g.quiet: if not g.quiet:
if f is None: if f is None:
f = sys.stdout f = sys.stdout
f.write(text + '\n', *args) f.write(f'{text}\n', *args)
def print_hints_on_download_error(err: str) -> None: def print_hints_on_download_error(err: str) -> None:
@@ -383,7 +383,7 @@ def print_hints_on_download_error(err: str) -> None:
if sys.platform == 'darwin': if sys.platform == 'darwin':
info('Running "./Install\\ Certificates.command" might be able to fix this issue.') 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)) info(f'Running "{sys.executable} -m pip install --upgrade certifi" can also resolve this issue in some cases.')
# Certificate issue on Windows can be hidden under different errors which might be even translated, # 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" # e.g. "[WinError -2146881269] ASN1 valor de tag inválido encontrado"
@@ -496,7 +496,7 @@ def unpack(filename: str, destination: str) -> None:
""" """
Extracts file specified by filename into destination depending on its type. Extracts file specified by filename into destination depending on its type.
""" """
info('Extracting {0} to {1}'.format(filename, destination)) info(f'Extracting {filename} to {destination}')
if filename.endswith(('.tar.gz', '.tgz')): if filename.endswith(('.tar.gz', '.tgz')):
archive_obj: Union[TarFile, ZipFile] = tarfile.open(filename, 'r:gz') archive_obj: Union[TarFile, ZipFile] = tarfile.open(filename, 'r:gz')
elif filename.endswith(('.tar.xz')): elif filename.endswith(('.tar.xz')):
@@ -629,9 +629,9 @@ def rename_with_retry(path_from: str, path_to: str) -> None:
except OSError: except OSError:
msg = f'Rename {path_from} to {path_to} failed' msg = f'Rename {path_from} to {path_to} failed'
if retry == retry_count - 1: if retry == retry_count - 1:
fatal(msg + '. Antivirus software might be causing this. Disabling it temporarily could solve the issue.') fatal(f'{msg}. Antivirus software might be causing this. Disabling it temporarily could solve the issue.')
raise raise
warn(msg + ', retrying...') warn(f'{msg}, retrying...')
# Sleep before the next try in order to pass the antivirus check on Windows # Sleep before the next try in order to pass the antivirus check on Windows
time.sleep(0.5) time.sleep(0.5)
@@ -643,7 +643,7 @@ def do_strip_container_dirs(path: str, levels: int) -> None:
""" """
assert levels > 0 assert levels > 0
# move the original directory out of the way (add a .tmp suffix) # move the original directory out of the way (add a .tmp suffix)
tmp_path = path + '.tmp' tmp_path = f'{path}.tmp'
if os.path.exists(tmp_path): if os.path.exists(tmp_path):
shutil.rmtree(tmp_path) shutil.rmtree(tmp_path)
rename_with_retry(path, tmp_path) rename_with_retry(path, tmp_path)
@@ -653,10 +653,10 @@ def do_strip_container_dirs(path: str, levels: int) -> None:
for level in range(levels): for level in range(levels):
contents = os.listdir(base_path) contents = os.listdir(base_path)
if len(contents) > 1: if len(contents) > 1:
raise RuntimeError('at level {}, expected 1 entry, got {}'.format(level, contents)) raise RuntimeError(f'at level {level}, expected 1 entry, got {contents}')
base_path = os.path.join(base_path, contents[0]) base_path = os.path.join(base_path, contents[0])
if not os.path.isdir(base_path): if not os.path.isdir(base_path):
raise RuntimeError('at level {}, {} is not a directory'.format(level, contents[0])) raise RuntimeError(f'at level {level}, {contents[0]} is not a directory')
# get the list of directories/files to move # get the list of directories/files to move
contents = os.listdir(base_path) contents = os.listdir(base_path)
for name in contents: for name in contents:
@@ -900,16 +900,15 @@ class IDFTool(object):
# There is no command available, so return early. It seems that # There is no command available, so return early. It seems that
# within some very strange context empty [''] may actually execute # within some very strange context empty [''] may actually execute
# something https://github.com/espressif/esp-idf/issues/11880 # something https://github.com/espressif/esp-idf/issues/11880
raise ToolNotFoundError('Tool {} not found'.format(self.name)) raise ToolNotFoundError(f'Tool {self.name} not found')
try: try:
version_cmd_result = run_cmd_check_output(cmd, None, extra_paths) version_cmd_result = run_cmd_check_output(cmd, None, extra_paths)
except OSError: except OSError:
# tool is not on the path # tool is not on the path
raise ToolNotFoundError('Tool {} not found'.format(self.name)) raise ToolNotFoundError(f'Tool {self.name} not found')
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
raise ToolExecError('returned non-zero exit code ({}) with error message:\n{}'.format( raise ToolExecError(f'returned non-zero exit code ({e.returncode}) with error message:\n{e.stderr.decode("utf-8",errors="ignore")}') # type: ignore
e.returncode, e.stderr.decode('utf-8',errors='ignore'))) # type: ignore
in_str = version_cmd_result.decode('utf-8') in_str = version_cmd_result.decode('utf-8')
match = re.search(self._current_options.version_regex, in_str) # type: ignore match = re.search(self._current_options.version_regex, in_str) # type: ignore
@@ -999,8 +998,7 @@ class IDFTool(object):
# not in PATH # not in PATH
pass pass
except ToolExecError as e: except ToolExecError as e:
fatal('tool {} found in path, but {}'.format( fatal(f'tool {self.name} found in path, but {e}')
self.name, e))
tool_error = True tool_error = True
else: else:
self.version_in_path = ver_str self.version_in_path = ver_str
@@ -1020,16 +1018,13 @@ class IDFTool(object):
try: try:
ver_str = self.get_version(self.get_export_paths(version)) ver_str = self.get_version(self.get_export_paths(version))
except ToolNotFoundError: except ToolNotFoundError:
warn('directory for tool {} version {} is present, but tool was not found'.format( warn(f'directory for tool {self.name} version {version} is present, but tool was not found')
self.name, version))
except ToolExecError as e: except ToolExecError as e:
fatal('tool {} version {} is installed, but {}'.format( fatal(f'tool {self.name} version {version} is installed, but {e}')
self.name, version, e))
tool_error = True tool_error = True
else: else:
if ver_str != version: if ver_str != version:
warn('tool {} version {} is installed, but has reported version {}'.format( warn(f'tool {self.name} version {version} is installed, but has reported version {ver_str}')
self.name, version, ver_str))
else: else:
self.versions_installed.append(version) self.versions_installed.append(version)
if tool_error: if tool_error:
@@ -1067,7 +1062,7 @@ class IDFTool(object):
assert version in self.versions assert version in self.versions
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(f'No packages for tool {self.name} platform {self._platform}!')
raise SystemExit(1) raise SystemExit(1)
url = download_obj.url url = download_obj.url
@@ -1077,19 +1072,19 @@ class IDFTool(object):
if os.path.isfile(local_path): if os.path.isfile(local_path):
if not self.check_download_file(download_obj, local_path): if not self.check_download_file(download_obj, local_path):
warn('removing downloaded file {0} and downloading again'.format(archive_name)) warn(f'removing downloaded file {archive_name} and downloading again')
os.unlink(local_path) os.unlink(local_path)
else: else:
info('file {0} is already downloaded'.format(archive_name)) info(f'file {archive_name} is already downloaded')
return return
downloaded = False downloaded = False
local_temp_path = local_path + '.tmp' local_temp_path = f'{local_path}.tmp'
for retry in range(DOWNLOAD_RETRY_COUNT): for retry in range(DOWNLOAD_RETRY_COUNT):
err = 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(f'Download failure: {err}')
warn('Failed to download {} to {}'.format(url, local_temp_path)) warn(f'Failed to download {url} to {local_temp_path}')
continue continue
rename_with_retry(local_temp_path, local_path) rename_with_retry(local_temp_path, local_path)
downloaded = True downloaded = True
@@ -1130,10 +1125,10 @@ class IDFTool(object):
expected_size = download_obj.size expected_size = download_obj.size
file_size, file_sha256 = get_file_size_sha256(local_path) file_size, file_sha256 = get_file_size_sha256(local_path)
if file_size != expected_size: if file_size != expected_size:
warn('file size mismatch for {}, expected {}, got {}'.format(local_path, expected_size, file_size)) warn(f'file size mismatch for {local_path}, expected {expected_size}, got {file_size}')
return False return False
if file_sha256 != expected_sha256: if file_sha256 != expected_sha256:
warn('hash mismatch for {}, expected {}, got {}'.format(local_path, expected_sha256, file_sha256)) warn(f'hash mismatch for {local_path}, expected {expected_sha256}, got {file_sha256}')
return False return False
return True return True
@@ -1153,55 +1148,55 @@ class IDFTool(object):
is_executable = tool_dict.get('is_executable', True) # type: ignore is_executable = tool_dict.get('is_executable', True) # type: ignore
if not isinstance(is_executable, bool): if not isinstance(is_executable, bool):
raise RuntimeError('is_executable for tool %s is not a bool' % tool_name) raise RuntimeError(f'is_executable for tool {tool_name} is not a bool')
version_cmd = tool_dict.get('version_cmd') version_cmd = tool_dict.get('version_cmd')
if type(version_cmd) is not list: if type(version_cmd) is not list:
raise RuntimeError('version_cmd for tool %s is not a list of strings' % tool_name) raise RuntimeError(f'version_cmd for tool {tool_name} is not a list of strings')
version_regex = tool_dict.get('version_regex') version_regex = tool_dict.get('version_regex')
if not isinstance(version_regex, str) or (not version_regex and is_executable): if not isinstance(version_regex, str) or (not version_regex and is_executable):
raise RuntimeError('version_regex for tool %s is not a non-empty string' % tool_name) raise RuntimeError(f'version_regex for tool {tool_name} is not a non-empty string')
version_regex_replace = tool_dict.get('version_regex_replace') version_regex_replace = tool_dict.get('version_regex_replace')
if version_regex_replace and not isinstance(version_regex_replace, str): if version_regex_replace and not isinstance(version_regex_replace, str):
raise RuntimeError('version_regex_replace for tool %s is not a string' % tool_name) raise RuntimeError(f'version_regex_replace for tool {tool_name} is not a string')
export_paths = tool_dict.get('export_paths') export_paths = tool_dict.get('export_paths')
if type(export_paths) is not list: if type(export_paths) is not list:
raise RuntimeError('export_paths for tool %s is not a list' % tool_name) raise RuntimeError(f'export_paths for tool {tool_name} is not a list')
export_vars = tool_dict.get('export_vars', {}) # type: ignore export_vars = tool_dict.get('export_vars', {}) # type: ignore
if type(export_vars) is not dict: if type(export_vars) is not dict:
raise RuntimeError('export_vars for tool %s is not a mapping' % tool_name) raise RuntimeError(f'export_vars for tool {tool_name} is not a mapping')
versions = tool_dict.get('versions') versions = tool_dict.get('versions')
if type(versions) is not list: if type(versions) is not list:
raise RuntimeError('versions for tool %s is not an array' % tool_name) raise RuntimeError(f'versions for tool {tool_name} is not an array')
install = tool_dict.get('install', False) # type: ignore install = tool_dict.get('install', False) # type: ignore
if not isinstance(install, str): if not isinstance(install, str):
raise RuntimeError('install for tool %s is not a string' % tool_name) raise RuntimeError(f'install for tool {tool_name} is not a string')
info_url = tool_dict.get('info_url', False) # type: ignore info_url = tool_dict.get('info_url', False) # type: ignore
if not isinstance(info_url, str): if not isinstance(info_url, str):
raise RuntimeError('info_url for tool %s is not a string' % tool_name) raise RuntimeError(f'info_url for tool {tool_name} is not a string')
license = tool_dict.get('license', False) # type: ignore license = tool_dict.get('license', False) # type: ignore
if not isinstance(license, str): if not isinstance(license, str):
raise RuntimeError('license for tool %s is not a string' % tool_name) raise RuntimeError(f'license for tool {tool_name} is not a string')
strip_container_dirs = tool_dict.get('strip_container_dirs', 0) strip_container_dirs = tool_dict.get('strip_container_dirs', 0)
if strip_container_dirs and type(strip_container_dirs) is not int: if strip_container_dirs and type(strip_container_dirs) is not int:
raise RuntimeError('strip_container_dirs for tool %s is not an int' % tool_name) raise RuntimeError(f'strip_container_dirs for tool {tool_name} is not an int')
overrides_list = tool_dict.get('platform_overrides', []) # type: ignore overrides_list = tool_dict.get('platform_overrides', []) # type: ignore
if type(overrides_list) is not list: if type(overrides_list) is not list:
raise RuntimeError('platform_overrides for tool %s is not a list' % tool_name) raise RuntimeError(f'platform_overrides for tool {tool_name} is not a list')
supported_targets = tool_dict.get('supported_targets') supported_targets = tool_dict.get('supported_targets')
if not isinstance(supported_targets, list): if not isinstance(supported_targets, list):
raise RuntimeError('supported_targets for tool %s is not a list of strings' % tool_name) raise RuntimeError(f'supported_targets for tool {tool_name} is not a list of strings')
# Create the object # Create the object
tool_obj: 'IDFTool' = cls(tool_name, description, install, info_url, license, # type: ignore tool_obj: 'IDFTool' = cls(tool_name, description, install, info_url, license, # type: ignore
@@ -1251,7 +1246,7 @@ class IDFTool(object):
for version_dict in versions: # type: ignore for version_dict in versions: # type: ignore
version = version_dict.get('name') # type: ignore version = version_dict.get('name') # type: ignore
if not isinstance(version, str): if not isinstance(version, str):
raise RuntimeError('version name for tool {} is not a string'.format(tool_name)) raise RuntimeError(f'version name for tool {tool_name} is not a string')
version_status = version_dict.get('status') # type: ignore version_status = version_dict.get('status') # type: ignore
if not isinstance(version_status, str) and version_status not in IDFToolVersion.STATUS_VALUES: if not isinstance(version_status, str) and version_status not in IDFToolVersion.STATUS_VALUES:
@@ -1278,11 +1273,9 @@ class IDFTool(object):
tool_obj.add_version(version_obj) tool_obj.add_version(version_obj)
for platform_id, version_list in recommended_versions.items(): for platform_id, version_list in recommended_versions.items():
if len(version_list) > 1: if len(version_list) > 1:
raise RuntimeError('tool {} for platform {} has {} recommended versions'.format( raise RuntimeError(f'tool {tool_name} for platform {platform_id} has {len(recommended_versions)} recommended versions')
tool_name, platform_id, len(recommended_versions)))
if install != IDFTool.INSTALL_NEVER and len(recommended_versions) == 0: if install != IDFTool.INSTALL_NEVER and len(recommended_versions) == 0:
raise RuntimeError('required/optional tool {} for platform {} has no recommended versions'.format( raise RuntimeError(f'required/optional tool {tool_name} for platform {platform_id} has no recommended versions')
tool_name, platform_id))
tool_obj._update_current_options() tool_obj._update_current_options()
return tool_obj return tool_obj
@@ -1471,13 +1464,13 @@ class IDFEnv:
# the directory doesn't exist if this is run on a clean system the first time # the directory doesn't exist if this is run on a clean system the first time
mkdir_p(g.idf_tools_path) mkdir_p(g.idf_tools_path)
with open(idf_env_file_path, 'w', encoding='utf-8') as w: with open(idf_env_file_path, 'w', encoding='utf-8') as w:
info('Updating {}'.format(idf_env_file_path)) info(f'Updating {idf_env_file_path}')
json.dump(dict(self), w, cls=IDFEnvEncoder, ensure_ascii=False, indent=4) # type: ignore json.dump(dict(self), w, cls=IDFEnvEncoder, ensure_ascii=False, indent=4) # type: ignore
except (IOError, OSError): except (IOError, OSError):
if not os.access(g.idf_tools_path, os.W_OK): if not os.access(g.idf_tools_path, os.W_OK):
raise OSError('IDF_TOOLS_PATH {} is not accessible to write.\ raise OSError(f'IDF_TOOLS_PATH {g.idf_tools_path} is not accessible to write. '
Required changes have not been saved'.format(g.idf_tools_path)) 'Required changes have not been saved')
raise OSError('File {} is not accessible to write or corrupted. Required changes have not been saved'.format(idf_env_file_path)) raise OSError(f'File {idf_env_file_path} is not accessible to write or corrupted. Required changes have not been saved')
def get_active_idf_record(self) -> IDFRecord: def get_active_idf_record(self) -> IDFRecord:
return self.idf_installed[active_repo_id()] return self.idf_installed[active_repo_id()]
@@ -1506,7 +1499,7 @@ class IDFEnv:
try: try:
idf_installed_verified[idf] = IDFRecord.get_idf_record_from_dict(idf_installed[idf]) idf_installed_verified[idf] = IDFRecord.get_idf_record_from_dict(idf_installed[idf])
except ValueError as err: except ValueError as err:
warn('{} "{}" found in {}, removing this record.' .format(err, idf, idf_env_file_path)) warn(f'{err} "{idf}" found in {idf_env_file_path}, removing this record.')
# Combine ESP-IDF loaded records with the one in constructor, to be sure that there is an active ESP-IDF record in the idf_installed # Combine ESP-IDF loaded records with the one in constructor, to be sure that there is an active ESP-IDF record in the idf_installed
# If the active record is already in idf_installed, it is not overwritten # If the active record is already in idf_installed, it is not overwritten
idf_env_obj.idf_installed = dict(idf_env_obj.idf_installed, **idf_installed_verified) idf_env_obj.idf_installed = dict(idf_env_obj.idf_installed, **idf_installed_verified)
@@ -1548,17 +1541,17 @@ class ENVState:
def save(self) -> str: def save(self) -> str:
try: try:
if self.deactivate_file_path and os.path.basename(self.deactivate_file_path).endswith('idf_' + str(os.getppid())): if self.deactivate_file_path and os.path.basename(self.deactivate_file_path).endswith(f'idf_{str(os.getppid())}'):
# If exported file path/name exists and belongs to actual opened shell # If exported file path/name exists and belongs to actual opened shell
with open(self.deactivate_file_path, 'w') as w: with open(self.deactivate_file_path, 'w') as w:
json.dump(self.idf_variables, w, ensure_ascii=False, indent=4) # type: ignore json.dump(self.idf_variables, w, ensure_ascii=False, indent=4) # type: ignore
else: else:
with tempfile.NamedTemporaryFile(delete=False, suffix='idf_' + str(os.getppid())) as fp: with tempfile.NamedTemporaryFile(delete=False, suffix=f'idf_{str(os.getppid())}') as fp:
self.deactivate_file_path = fp.name self.deactivate_file_path = fp.name
fp.write(json.dumps(self.idf_variables, ensure_ascii=False, indent=4).encode('utf-8')) fp.write(json.dumps(self.idf_variables, ensure_ascii=False, indent=4).encode('utf-8'))
except (IOError, OSError): except (IOError, OSError):
warn('File storing IDF env variables {} is not accessible to write. ' warn(f'File storing IDF env variables {self.deactivate_file_path} is not accessible to write. '
'Potentional switching ESP-IDF versions may cause problems'.format(self.deactivate_file_path)) 'Potentional switching ESP-IDF versions may cause problems')
return self.deactivate_file_path return self.deactivate_file_path
@@ -1659,7 +1652,7 @@ def get_idf_version() -> str:
else: else:
warn('Reading IDF version from C header file failed!') warn('Reading IDF version from C header file failed!')
except Exception as e: except Exception as e:
warn('Is it not possible to determine the IDF version: {}'.format(e)) warn(f'Is it not possible to determine the IDF version: {e}')
if idf_version is None: if idf_version is None:
fatal('IDF version cannot be determined') fatal('IDF version cannot be determined')
@@ -1672,11 +1665,11 @@ def get_python_env_path() -> Tuple[str, str, str, str]:
""" """
Returns tuple of Python environment path, Python env. path with subdir and full path from Python (i.e. with executable). Returns tuple of Python environment path, Python env. path with subdir and full path from Python (i.e. with executable).
""" """
python_ver_major_minor = '{}.{}'.format(sys.version_info.major, sys.version_info.minor) python_ver_major_minor = f'{sys.version_info.major}.{sys.version_info.minor}'
idf_version = get_idf_version() idf_version = get_idf_version()
idf_python_env_path = os.getenv('IDF_PYTHON_ENV_PATH') or os.path.join(g.idf_tools_path, 'python_env', idf_python_env_path = os.getenv('IDF_PYTHON_ENV_PATH') or os.path.join(g.idf_tools_path, 'python_env',
'idf{}_py{}_env'.format(idf_version, python_ver_major_minor)) f'idf{idf_version}_py{python_ver_major_minor}_env')
python_exe, subdir = get_python_exe_and_subdir() python_exe, subdir = get_python_exe_and_subdir()
idf_python_export_path = os.path.join(idf_python_env_path, subdir) idf_python_export_path = os.path.join(idf_python_env_path, subdir)
@@ -1735,7 +1728,7 @@ def parse_targets_arg(targets_str: str) -> List[str]:
else: else:
invalid_targets = [t for t in targets if t not in targets_from_tools_json] invalid_targets = [t for t in targets if t not in targets_from_tools_json]
if invalid_targets: if invalid_targets:
warn('Targets: "{}" are not supported. Only allowed options are: {}.'.format(', '.join(invalid_targets), ', '.join(targets_from_tools_json))) warn(f'Targets: \"{", ".join(invalid_targets)}\" are not supported. Only allowed options are: {", ".join(targets_from_tools_json)}.')
raise SystemExit(1) raise SystemExit(1)
return targets return targets
@@ -1753,7 +1746,7 @@ def feature_to_requirements_path(feature: str) -> str:
""" """
Convert feature (ci, core, docs, gdbgui, pytest, ...) to the path to its requirements.txt. Convert feature (ci, core, docs, gdbgui, pytest, ...) to the path to its requirements.txt.
""" """
return os.path.join(g.idf_path, 'tools', 'requirements', 'requirements.{}.txt'.format(feature)) return os.path.join(g.idf_path, 'tools', 'requirements', f'requirements.{feature}.txt')
def process_and_check_features(idf_env_obj: IDFEnv, features_str: str) -> List[str]: def process_and_check_features(idf_env_obj: IDFEnv, features_str: str) -> List[str]:
@@ -1902,9 +1895,9 @@ def active_repo_id() -> str:
""" """
try: try:
# g.idf_path is forcefully casted to str just to make type linters happy # g.idf_path is forcefully casted to str just to make type linters happy
return str(g.idf_path) + '-v' + get_idf_version() return f'{str(g.idf_path)}-v{get_idf_version()}'
except ReferenceError: except ReferenceError:
return 'UNKNOWN_PATH' + '-v' + get_idf_version() return f'UNKNOWN_PATH-v{get_idf_version()}'
def list_default(args): # type: ignore def list_default(args): # type: ignore
@@ -1917,14 +1910,14 @@ def list_default(args): # type: ignore
if tool.get_install_type() == IDFTool.INSTALL_NEVER: if tool.get_install_type() == IDFTool.INSTALL_NEVER:
continue continue
optional_str = ' (optional)' if tool.get_install_type() == IDFTool.INSTALL_ON_REQUEST else '' optional_str = ' (optional)' if tool.get_install_type() == IDFTool.INSTALL_ON_REQUEST else ''
info('* {}: {}{}'.format(name, tool.description, optional_str)) info(f'* {name}: {tool.description}{optional_str}')
try: try:
tool.find_installed_versions() tool.find_installed_versions()
except ToolBinaryError: except ToolBinaryError:
tool_error = True tool_error = True
versions_for_platform = {k: v for k, v in tool.versions.items() if v.compatible_with_platform()} versions_for_platform = {k: v for k, v in tool.versions.items() if v.compatible_with_platform()}
if not versions_for_platform: if not versions_for_platform:
info(' (no versions compatible with platform {})'.format(PYTHON_PLATFORM)) info(f' (no versions compatible with platform {PYTHON_PLATFORM})')
continue continue
versions_sorted = sorted(versions_for_platform.keys(), key=tool.versions.get, reverse=True) # type: ignore versions_sorted = sorted(versions_for_platform.keys(), key=tool.versions.get, reverse=True) # type: ignore
for version in versions_sorted: for version in versions_sorted:
@@ -1978,24 +1971,24 @@ def action_check(args): # type: ignore
if tool.get_install_type() == IDFTool.INSTALL_NEVER: if tool.get_install_type() == IDFTool.INSTALL_NEVER:
continue continue
tool_found_somewhere = False tool_found_somewhere = False
info('Checking tool %s' % name) info(f'Checking tool {name}')
try: try:
tool.find_installed_versions() tool.find_installed_versions()
except ToolBinaryError: except ToolBinaryError:
tool_error = True tool_error = True
if tool.version_in_path: if tool.version_in_path:
info(' version found in PATH: %s' % tool.version_in_path) info(f' version found in PATH: {tool.version_in_path}')
tool_found_somewhere = True tool_found_somewhere = True
else: else:
info(' no version found in PATH') info(' no version found in PATH')
for version in tool.versions_installed: for version in tool.versions_installed:
info(' version installed in tools directory: %s' % version) info(f' version installed in tools directory: {version}')
tool_found_somewhere = True tool_found_somewhere = True
if not tool_found_somewhere and tool.get_install_type() == IDFTool.INSTALL_ALWAYS: if not tool_found_somewhere and tool.get_install_type() == IDFTool.INSTALL_ALWAYS:
not_found_list.append(name) not_found_list.append(name)
if not_found_list: if not_found_list:
fatal('The following required tools were not found: ' + ' '.join(not_found_list)) fatal(f'The following required tools were not found: {" ".join(not_found_list)}')
raise SystemExit(1) raise SystemExit(1)
if tool_error: if tool_error:
raise SystemExit(1) raise SystemExit(1)
@@ -2014,8 +2007,7 @@ def handle_recommended_version_to_use(
tool_export_paths = tool.get_export_paths(version_to_use) tool_export_paths = tool.get_export_paths(version_to_use)
tool_export_vars = tool.get_export_vars(version_to_use) tool_export_vars = tool.get_export_vars(version_to_use)
if tool.version_in_path and tool.version_in_path not in tool.versions: if tool.version_in_path and tool.version_in_path not in tool.versions:
info('Not using an unsupported version of tool {} found in PATH: {}.'.format( info(f'Not using an unsupported version of tool {tool.name} found in PATH: {tool.version_in_path}.' + prefer_system_hint, f=sys.stderr)
tool.name, tool.version_in_path) + prefer_system_hint, f=sys.stderr)
return tool_export_paths, tool_export_vars return tool_export_paths, tool_export_vars
@@ -2026,12 +2018,12 @@ def handle_supported_or_deprecated_version(tool: IDFTool, tool_name: str) -> Non
""" """
version_obj: IDFToolVersion = tool.versions[tool.version_in_path] # type: ignore version_obj: IDFToolVersion = tool.versions[tool.version_in_path] # type: ignore
if version_obj.status == IDFToolVersion.STATUS_SUPPORTED: if version_obj.status == IDFToolVersion.STATUS_SUPPORTED:
info('Using a supported version of tool {} found in PATH: {}.'.format(tool_name, tool.version_in_path), info(f'Using a supported version of tool {tool_name} found in PATH: {tool.version_in_path}.',
f=sys.stderr) f=sys.stderr)
info('However the recommended version is {}.'.format(tool.get_recommended_version()), info(f'However the recommended version is {tool.get_recommended_version()}.',
f=sys.stderr) f=sys.stderr)
elif version_obj.status == IDFToolVersion.STATUS_DEPRECATED: elif version_obj.status == IDFToolVersion.STATUS_DEPRECATED:
warn('using a deprecated version of tool {} found in PATH: {}'.format(tool_name, tool.version_in_path)) warn(f'using a deprecated version of tool {tool_name} found in PATH: {tool.version_in_path}')
# The following function is used in process_tool which is a part of the action_export. # The following function is used in process_tool which is a part of the action_export.
@@ -2044,10 +2036,9 @@ def handle_missing_versions(
""" """
Prints the info about missing tool to stderr if tool has no supported versions installed. Prints the info about missing tool to stderr if tool has no supported versions installed.
""" """
fatal('tool {} has no installed versions. Please run \'{}\' to install it.'.format( fatal(f'tool {tool.name} has no installed versions. Please run \'{install_cmd}\' to install it.')
tool.name, install_cmd))
if tool.version_in_path and tool.version_in_path not in tool.versions: if tool.version_in_path and tool.version_in_path not in tool.versions:
info('An unsupported version of tool {} was found in PATH: {}. '.format(tool_name, tool.version_in_path) + info(f'An unsupported version of tool {tool_name} was found in PATH: {tool.version_in_path}. ' +
prefer_system_hint, f=sys.stderr) prefer_system_hint, f=sys.stderr)
@@ -2089,8 +2080,7 @@ def process_tool(
if tool.version_in_path not in tool.versions: if tool.version_in_path not in tool.versions:
# unsupported version # unsupported version
if args.prefer_system: # type: ignore if args.prefer_system: # type: ignore
warn('using an unsupported version of tool {} found in PATH: {}'.format( warn(f'using an unsupported version of tool {tool.name} found in PATH: {tool.version_in_path}')
tool.name, tool.version_in_path))
return tool_export_paths, tool_export_vars, tool_found return tool_export_paths, tool_export_vars, tool_found
else: else:
# unsupported version in path # unsupported version in path
@@ -2124,10 +2114,10 @@ def action_export(args: Any) -> None:
export_vars: Dict[str, str] = {} export_vars: Dict[str, str] = {}
paths_to_export = [] paths_to_export = []
self_restart_cmd = f'{sys.executable} {__file__}{(" --tools-json " + args.tools_json) if args.tools_json else ""}' self_restart_cmd = f'{sys.executable} {__file__}{(" --tools-json {args.tools_json}") if args.tools_json else ""}'
self_restart_cmd = to_shell_specific_paths([self_restart_cmd])[0] self_restart_cmd = to_shell_specific_paths([self_restart_cmd])[0]
prefer_system_hint = '' if IDF_TOOLS_EXPORT_CMD else f' To use it, run \'{self_restart_cmd} export --prefer-system\'' prefer_system_hint = '' if IDF_TOOLS_EXPORT_CMD else f' To use it, run \'{self_restart_cmd} export --prefer-system\''
install_cmd = to_shell_specific_paths([IDF_TOOLS_INSTALL_CMD])[0] if IDF_TOOLS_INSTALL_CMD else self_restart_cmd + ' install' install_cmd = to_shell_specific_paths([IDF_TOOLS_INSTALL_CMD])[0] if IDF_TOOLS_INSTALL_CMD else f'{self_restart_cmd} install'
for name, tool in tools_info.items(): for name, tool in tools_info.items():
if tool.get_install_type() == IDFTool.INSTALL_NEVER: if tool.get_install_type() == IDFTool.INSTALL_NEVER:
@@ -2173,7 +2163,7 @@ def action_export(args: Any) -> None:
# Correct PATH order check for Windows platform # Correct PATH order check for Windows platform
# idf-exe has to be before \tools in PATH # idf-exe has to be before \tools in PATH
if sys.platform == 'win32': if sys.platform == 'win32':
paths_to_check = rf"{export_vars['PATH']}{os.environ['PATH']}" paths_to_check = rf'{export_vars["PATH"]}{os.environ["PATH"]}'
try: try:
if paths_to_check.index(r'\tools;') < paths_to_check.index(r'\idf-exe'): if paths_to_check.index(r'\tools;') < paths_to_check.index(r'\idf-exe'):
warn('The PATH is not in correct order (idf-exe should be before esp-idf\\tools)') warn('The PATH is not in correct order (idf-exe should be before esp-idf\\tools)')
@@ -2210,19 +2200,19 @@ def apply_mirror_prefix_map(args: Any, idf_download_url: str) -> str:
mirror_prefix_map = mirror_prefix_map_env.split(';') mirror_prefix_map = mirror_prefix_map_env.split(';')
if IDF_MAINTAINER and args and args.mirror_prefix_map: if IDF_MAINTAINER and args and args.mirror_prefix_map:
if mirror_prefix_map: if mirror_prefix_map:
warn('Both IDF_MIRROR_PREFIX_MAP environment variable and --mirror-prefix-map flag are specified, ' + warn('Both IDF_MIRROR_PREFIX_MAP environment variable and --mirror-prefix-map flag are specified, '
'will use the value from the command line.') 'will use the value from the command line.')
mirror_prefix_map = args.mirror_prefix_map mirror_prefix_map = args.mirror_prefix_map
if mirror_prefix_map: if mirror_prefix_map:
for item in mirror_prefix_map: for item in mirror_prefix_map:
if URL_PREFIX_MAP_SEPARATOR not in item: if URL_PREFIX_MAP_SEPARATOR not in item:
warn('invalid mirror-prefix-map item (missing \'{}\') {}'.format(URL_PREFIX_MAP_SEPARATOR, item)) warn(f'invalid mirror-prefix-map item (missing \'{URL_PREFIX_MAP_SEPARATOR}\') {item}')
continue continue
search, replace = item.split(URL_PREFIX_MAP_SEPARATOR, 1) search, replace = item.split(URL_PREFIX_MAP_SEPARATOR, 1)
replace = replace.replace('\\', '\\\\') # On windows replace single \ with double \\ replace = replace.replace('\\', '\\\\') # On windows replace single \ with double \\
new_url = re.sub(search, replace, idf_download_url) new_url = re.sub(search, replace, idf_download_url)
if new_url != idf_download_url: if new_url != idf_download_url:
info('Changed download URL: {} => {}'.format(idf_download_url, new_url)) info(f'Changed download URL: {idf_download_url} => {new_url}')
break break
return new_url return new_url
@@ -2246,9 +2236,9 @@ def apply_github_assets_option(idf_download_url: str) -> str:
# Strip any trailing / from the mirror URL # Strip any trailing / from the mirror URL
github_assets = github_assets.rstrip('/') github_assets = github_assets.rstrip('/')
new_url = re.sub(r'^https://github.com/', 'https://{}/'.format(github_assets), idf_download_url) new_url = re.sub(r'^https://github.com/', f'https://{github_assets}/', idf_download_url)
if new_url != idf_download_url: if new_url != idf_download_url:
info('Using GitHub assets mirror for URL: {} => {}'.format(idf_download_url, new_url)) info(f'Using GitHub assets mirror for URL: {idf_download_url} => {new_url}')
return new_url return new_url
@@ -2269,7 +2259,7 @@ def get_tools_spec_and_platform_info(selected_platform: str, targets: List[str],
tools_info_for_platform[name] = tool_for_platform tools_info_for_platform[name] = tool_for_platform
tools_spec = expand_tools_arg(tools_spec, tools_info_for_platform, targets) tools_spec = expand_tools_arg(tools_spec, tools_info_for_platform, targets)
info('Downloading tools for {}: {}'.format(selected_platform, ', '.join(tools_spec))) info(f'Downloading tools for {selected_platform}: {", ".join(tools_spec)}')
finally: finally:
g.quiet = old_global_quiet g.quiet = old_global_quiet
@@ -2293,7 +2283,7 @@ def action_download(args): # type: ignore
except OSError as err: except OSError as err:
if args.targets in targets: if args.targets in targets:
targets.remove(args.targets) targets.remove(args.targets)
warn('Downloading tools for targets was not successful with error: {}'.format(err)) warn(f'Downloading tools for targets was not successful with error: {err}')
# Taking into account ESP_targets but not saving them for individual tools (specified list of tools) # Taking into account ESP_targets but not saving them for individual tools (specified list of tools)
else: else:
targets = parse_targets_arg(args.targets) targets = parse_targets_arg(args.targets)
@@ -2308,20 +2298,20 @@ def action_download(args): # type: ignore
else: else:
tool_name, tool_version = tool_spec.split('@', 1) tool_name, tool_version = tool_spec.split('@', 1)
if tool_name not in tools_info_for_platform: if tool_name not in tools_info_for_platform:
fatal('unknown tool name: {}'.format(tool_name)) fatal(f'unknown tool name: {tool_name}')
raise SystemExit(1) raise SystemExit(1)
tool_obj = tools_info_for_platform[tool_name] tool_obj = tools_info_for_platform[tool_name]
if tool_version is not None and tool_version not in tool_obj.versions: if tool_version is not None and tool_version not in tool_obj.versions:
fatal('unknown version for tool {}: {}'.format(tool_name, tool_version)) fatal(f'unknown version for tool {tool_name}: {tool_version}')
raise SystemExit(1) raise SystemExit(1)
if tool_version is None: if tool_version is None:
tool_version = tool_obj.get_recommended_version() tool_version = tool_obj.get_recommended_version()
if tool_version is None: if tool_version is None:
fatal('tool {} not found for {} platform'.format(tool_name, platform)) fatal(f'tool {tool_name} not found for {platform} platform')
raise SystemExit(1) raise SystemExit(1)
tool_spec = '{}@{}'.format(tool_name, tool_version) tool_spec = f'{tool_name}@{tool_version}'
info('Downloading {}'.format(tool_spec)) info(f'Downloading {tool_spec}')
_idf_tool_obj = tool_obj.versions[tool_version].get_download_for_platform(platform) _idf_tool_obj = tool_obj.versions[tool_version].get_download_for_platform(platform)
_idf_tool_obj.url = get_idf_download_url_apply_mirrors(args, _idf_tool_obj.url) _idf_tool_obj.url = get_idf_download_url_apply_mirrors(args, _idf_tool_obj.url)
@@ -2345,16 +2335,16 @@ def action_install(args): # type: ignore
except OSError as err: except OSError as err:
if args.targets in targets: if args.targets in targets:
targets.remove(args.targets) targets.remove(args.targets)
warn('Installing targets was not successful with error: {}'.format(err)) warn(f'Installing targets was not successful with error: {err}')
info('Selected targets are: {}'.format(', '.join(targets))) info(f'Selected targets are: {", ".join(targets)}')
# Taking into account ESP_targets but not saving them for individual tools (specified list of tools) # Taking into account ESP_targets but not saving them for individual tools (specified list of tools)
else: else:
targets = parse_targets_arg(args.targets) targets = parse_targets_arg(args.targets)
info('Current system platform: {}'.format(CURRENT_PLATFORM)) info(f'Current system platform: {CURRENT_PLATFORM}')
tools_info = load_tools_info() tools_info = load_tools_info()
tools_spec = expand_tools_arg(tools_spec, tools_info, targets) tools_spec = expand_tools_arg(tools_spec, tools_info, targets)
info('Installing tools: {}'.format(', '.join(tools_spec))) info(f'Installing tools: {", ".join(tools_spec)}')
tool_error = False tool_error = False
for tool_spec in tools_spec: for tool_spec in tools_spec:
if '@' not in tool_spec: if '@' not in tool_spec:
@@ -2363,14 +2353,14 @@ def action_install(args): # type: ignore
else: else:
tool_name, tool_version = tool_spec.split('@', 1) tool_name, tool_version = tool_spec.split('@', 1)
if tool_name not in tools_info: if tool_name not in tools_info:
fatal('unknown tool name: {}'.format(tool_name)) fatal(f'unknown tool name: {tool_name}')
raise SystemExit(1) raise SystemExit(1)
tool_obj = tools_info[tool_name] tool_obj = tools_info[tool_name]
if not tool_obj.compatible_with_platform(): if not tool_obj.compatible_with_platform():
fatal('tool {} does not have versions compatible with platform {}'.format(tool_name, CURRENT_PLATFORM)) fatal(f'tool {tool_name} does not have versions compatible with platform {CURRENT_PLATFORM}')
raise SystemExit(1) raise SystemExit(1)
if tool_version is not None and tool_version not in tool_obj.versions: if tool_version is not None and tool_version not in tool_obj.versions:
fatal('unknown version for tool {}: {}'.format(tool_name, tool_version)) fatal(f'unknown version for tool {tool_name}: {tool_version}')
raise SystemExit(1) raise SystemExit(1)
if tool_version is None: if tool_version is None:
tool_version = tool_obj.get_recommended_version() tool_version = tool_obj.get_recommended_version()
@@ -2379,12 +2369,12 @@ def action_install(args): # type: ignore
tool_obj.find_installed_versions() tool_obj.find_installed_versions()
except ToolBinaryError: except ToolBinaryError:
tool_error = True tool_error = True
tool_spec = '{}@{}'.format(tool_name, tool_version) tool_spec = f'{tool_name}@{tool_version}'
if tool_version in tool_obj.versions_installed: if tool_version in tool_obj.versions_installed:
info('Skipping {} (already installed)'.format(tool_spec)) info(f'Skipping {tool_spec} (already installed)')
continue continue
info('Installing {}'.format(tool_spec)) info(f'Installing {tool_spec}')
_idf_tool_obj = tool_obj.versions[tool_version].get_download_for_platform(PYTHON_PLATFORM) _idf_tool_obj = tool_obj.versions[tool_version].get_download_for_platform(PYTHON_PLATFORM)
_idf_tool_obj.url = get_idf_download_url_apply_mirrors(args, _idf_tool_obj.url) _idf_tool_obj.url = get_idf_download_url_apply_mirrors(args, _idf_tool_obj.url)
@@ -2424,7 +2414,7 @@ def get_requirements(new_features: str) -> List[str]:
except OSError as err: except OSError as err:
if new_features in features: if new_features in features:
features.remove(new_features) features.remove(new_features)
warn('Updating features was not successful with error: {}'.format(err)) warn(f'Updating features was not successful with error: {err}')
return [feature_to_requirements_path(feature) for feature in features] return [feature_to_requirements_path(feature) for feature in features]
@@ -2434,10 +2424,10 @@ def get_constraints(idf_version: str, online: bool = True) -> str:
check success and place it in constraints file location. check success and place it in constraints file location.
""" """
idf_download_url = get_idf_download_url_apply_mirrors() idf_download_url = get_idf_download_url_apply_mirrors()
constraint_file = 'espidf.constraints.v{}.txt'.format(idf_version) constraint_file = f'espidf.constraints.v{idf_version}.txt'
constraint_path = os.path.join(g.idf_tools_path, constraint_file) constraint_path = os.path.join(g.idf_tools_path, constraint_file)
constraint_url = '/'.join([idf_download_url, constraint_file]) constraint_url = '/'.join([idf_download_url, constraint_file])
temp_path = constraint_path + '.tmp' temp_path = f'{constraint_path}.tmp'
if not online: if not online:
if os.path.isfile(constraint_path): if os.path.isfile(constraint_path):
@@ -2461,8 +2451,8 @@ def get_constraints(idf_version: str, online: bool = True) -> str:
for _ in range(DOWNLOAD_RETRY_COUNT): for _ in range(DOWNLOAD_RETRY_COUNT):
err = 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(f'Download failure: {err}')
warn('Failed to download {} to {}'.format(constraint_url, temp_path)) warn(f'Failed to download {constraint_url} to {temp_path}')
continue continue
if os.path.isfile(constraint_path): if os.path.isfile(constraint_path):
# Windows cannot rename to existing file. It needs to be deleted. # Windows cannot rename to existing file. It needs to be deleted.
@@ -2489,8 +2479,8 @@ def install_legacy_python_virtualenv(path: str) -> None:
try: try:
subprocess.check_call([sys.executable, '-m', 'pip', '--version']) subprocess.check_call([sys.executable, '-m', 'pip', '--version'])
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
fatal('Python interpreter at {} doesn\'t have pip installed. ' fatal(f'Python interpreter at {sys.executable} doesn\'t have pip installed. '
'Please check the Getting Started Guides for the steps to install prerequisites for your OS.'.format(sys.executable)) 'Please check the Getting Started Guides for the steps to install prerequisites for your OS.')
raise SystemExit(1) raise SystemExit(1)
virtualenv_installed_via_pip = False virtualenv_installed_via_pip = False
@@ -2511,7 +2501,7 @@ def install_legacy_python_virtualenv(path: str) -> None:
try: try:
major_ver = int(virtualenv.__version__.split('.')[0]) major_ver = int(virtualenv.__version__.split('.')[0])
if major_ver < 20: if major_ver < 20:
warn('Virtualenv version {} is old, please consider upgrading it'.format(virtualenv.__version__)) warn(f'Virtualenv version {virtualenv.__version__} is old, please consider upgrading it')
with_seeder_option = False with_seeder_option = False
except (ValueError, NameError, AttributeError, IndexError): except (ValueError, NameError, AttributeError, IndexError):
pass pass
@@ -2565,7 +2555,7 @@ def action_install_python_env(args): # type: ignore
reinstall = True reinstall = True
if reinstall and os.path.exists(idf_python_env_path): if reinstall and os.path.exists(idf_python_env_path):
warn('Removing the existing Python environment in {}'.format(idf_python_env_path)) warn(f'Removing the existing Python environment in {idf_python_env_path}')
shutil.rmtree(idf_python_env_path) shutil.rmtree(idf_python_env_path)
venv_can_upgrade = False venv_can_upgrade = False
@@ -2579,7 +2569,7 @@ def action_install_python_env(args): # type: ignore
virtualenv_options += ['--upgrade-deps'] virtualenv_options += ['--upgrade-deps']
venv_can_upgrade = True venv_can_upgrade = True
info('Creating a new Python environment in {}'.format(idf_python_env_path)) info(f'Creating a new Python environment in {idf_python_env_path}')
subprocess.check_call([sys.executable, '-m', 'venv', subprocess.check_call([sys.executable, '-m', 'venv',
*virtualenv_options, *virtualenv_options,
idf_python_env_path], idf_python_env_path],
@@ -2618,9 +2608,9 @@ def action_install_python_env(args): # type: ignore
info('Installing Python packages') info('Installing Python packages')
if use_constraints: if use_constraints:
info(' Constraint file: {}'.format(constraint_file)) info(f' Constraint file: {constraint_file}')
info(' Requirement files:') info(' Requirement files:')
info(os.linesep.join(' - {}'.format(path) for path in requirements_file_list)) info(os.linesep.join(f' - {path}' for path in requirements_file_list))
subprocess.check_call(run_args, stdout=sys.stdout, stderr=sys.stderr, env=env_copy) subprocess.check_call(run_args, stdout=sys.stdout, stderr=sys.stderr, env=env_copy)
@@ -2635,18 +2625,17 @@ def action_check_python_dependencies(args): # type: ignore
_, _, virtualenv_python, idf_version = get_python_env_path() _, _, virtualenv_python, idf_version = get_python_env_path()
if not os.path.isfile(virtualenv_python): if not os.path.isfile(virtualenv_python):
fatal('{} doesn\'t exist! Please run the install script or "idf_tools.py install-python-env" in order to ' fatal(f'{virtualenv_python} doesn\'t exist! Please run the install script or "idf_tools.py install-python-env" in order to create it')
'create it'.format(virtualenv_python))
raise SystemExit(1) raise SystemExit(1)
if use_constraints: if use_constraints:
constr_path = get_constraints(idf_version, online=False) # keep offline for checking constr_path = get_constraints(idf_version, online=False) # keep offline for checking
info('Constraint file: {}'.format(constr_path)) info(f'Constraint file: {constr_path}')
info('Requirement files:') info('Requirement files:')
info(os.linesep.join(' - {}'.format(path) for path in req_paths)) info(os.linesep.join(f' - {path}' for path in req_paths))
info('Python being checked: {}'.format(virtualenv_python)) info(f'Python being checked: {virtualenv_python}')
# The dependency checker will be invoked with virtualenv_python. idf_tools.py could have been invoked with a # The dependency checker will be invoked with virtualenv_python. idf_tools.py could have been invoked with a
# different one, therefore, importing is not a suitable option. # different one, therefore, importing is not a suitable option.
@@ -2753,7 +2742,7 @@ def action_add_version(args: Any) -> None:
tool_name = args.tool tool_name = args.tool
tool_obj = tools_info.get(tool_name) tool_obj = tools_info.get(tool_name)
if not tool_obj: if not tool_obj:
info('Creating new tool entry for {}'.format(tool_name)) info(f'Creating new tool entry for {tool_name}')
tool_obj = IDFTool(tool_name, TODO_MESSAGE, IDFTool.INSTALL_ALWAYS, tool_obj = IDFTool(tool_name, TODO_MESSAGE, IDFTool.INSTALL_ALWAYS,
TODO_MESSAGE, TODO_MESSAGE, [TODO_MESSAGE], TODO_MESSAGE, TODO_MESSAGE, TODO_MESSAGE, [TODO_MESSAGE], TODO_MESSAGE,
[TODO_MESSAGE]) [TODO_MESSAGE])
@@ -2765,10 +2754,10 @@ def action_add_version(args: Any) -> None:
version_status = IDFToolVersion.STATUS_RECOMMENDED version_status = IDFToolVersion.STATUS_RECOMMENDED
version_obj = tool_obj.versions.get(version) version_obj = tool_obj.versions.get(version)
if not version_obj: if not version_obj:
info('Creating new version {}'.format(version)) info(f'Creating new version {version}')
version_obj = IDFToolVersion(version, version_status) version_obj = IDFToolVersion(version, version_status)
tool_obj.versions[version] = version_obj tool_obj.versions[version] = version_obj
url_prefix = args.url_prefix or 'https://%s/' % TODO_MESSAGE url_prefix = args.url_prefix or f'https://{TODO_MESSAGE}/'
checksum_info: ChecksumFileParser = (ChecksumFileParser(tool_name, args.checksum_file) checksum_info: ChecksumFileParser = (ChecksumFileParser(tool_name, args.checksum_file)
if args.checksum_file if args.checksum_file
else ChecksumCalculator(args.artifact_file)) # type: ignore else ChecksumCalculator(args.artifact_file)) # type: ignore
@@ -2776,13 +2765,13 @@ def action_add_version(args: Any) -> None:
# Guess which platform this file is for # Guess which platform this file is for
found_platform = Platforms.get_by_filename(file_name) found_platform = Platforms.get_by_filename(file_name)
if found_platform is None: if found_platform is None:
info('Could not guess platform for file {}'.format(file_name)) info(f'Could not guess platform for file {file_name}')
found_platform = TODO_MESSAGE found_platform = TODO_MESSAGE
url = urljoin(url_prefix, file_name) url = urljoin(url_prefix, file_name)
info('Adding download for platform {}'.format(found_platform)) info(f'Adding download for platform {found_platform}')
info(' size: {}'.format(file_size)) info(f' size: {file_size}')
info(' SHA256: {}'.format(file_sha256)) info(f' SHA256: {file_sha256}')
info(' URL: {}'.format(url)) info(f' URL: {url}')
version_obj.add_download(found_platform, url, file_size, file_sha256) version_obj.add_download(found_platform, url, file_size, file_sha256)
json_str = dump_tools_json(tools_info) json_str = dump_tools_json(tools_info)
if not args.output: if not args.output:
@@ -2790,7 +2779,7 @@ def action_add_version(args: Any) -> None:
with open(args.output, 'w') as f: with open(args.output, 'w') as f:
f.write(json_str) f.write(json_str)
f.write('\n') f.write('\n')
info('Wrote output to {}'.format(args.output)) info(f'Wrote output to {args.output}')
def action_rewrite(args): # type: ignore def action_rewrite(args): # type: ignore
@@ -2804,7 +2793,7 @@ def action_rewrite(args): # type: ignore
with open(args.output, 'w') as f: with open(args.output, 'w') as f:
f.write(json_str) f.write(json_str)
f.write('\n') f.write('\n')
info('Wrote output to {}'.format(args.output)) info(f'Wrote output to {args.output}')
def action_uninstall(args: Any) -> None: def action_uninstall(args: Any) -> None:
@@ -2849,7 +2838,7 @@ def action_uninstall(args: Any) -> None:
else: else:
path_to_remove = os.path.join(tools_path, tool) path_to_remove = os.path.join(tools_path, tool)
shutil.rmtree(path_to_remove) shutil.rmtree(path_to_remove)
info(path_to_remove + ' was removed.') info(f'{path_to_remove} was removed.')
except OSError as error: except OSError as error:
warn(f'{error.filename} can not be removed because {error.strerror}.') warn(f'{error.filename} can not be removed because {error.strerror}.')
@@ -2881,7 +2870,7 @@ def action_uninstall(args: Any) -> None:
for archive in downloaded_archives: for archive in downloaded_archives:
if archive not in used_archives: if archive not in used_archives:
os.remove(os.path.join(dist_path, archive)) os.remove(os.path.join(dist_path, archive))
info(os.path.join(dist_path, archive) + ' was removed.') info(f'{os.path.join(dist_path, archive)} was removed.')
def action_validate(args): # type: ignore def action_validate(args): # type: ignore
@@ -2911,7 +2900,7 @@ def action_gen_doc(args): # type: ignore
tools_info = load_tools_info() tools_info = load_tools_info()
def print_out(text: str) -> None: def print_out(text: str) -> None:
f.write(text + '\n') f.write(f'{text}\n')
print_out('.. |zwsp| unicode:: U+200B') print_out('.. |zwsp| unicode:: U+200B')
print_out(' :trim:') print_out(' :trim:')
@@ -2920,10 +2909,10 @@ def action_gen_doc(args): # type: ignore
idf_gh_url = 'https://github.com/espressif/esp-idf' idf_gh_url = 'https://github.com/espressif/esp-idf'
for tool_name, tool_obj in tools_info.items(): for tool_name, tool_obj in tools_info.items():
info_url = tool_obj.options.info_url info_url = tool_obj.options.info_url
if idf_gh_url + '/tree' in info_url: if f'{idf_gh_url}/tree' in info_url:
info_url = re.sub(idf_gh_url + r'/tree/\w+/(.*)', r':idf:`\1`', info_url) info_url = re.sub(f'{idf_gh_url}/tree/\\w+/(.*)', r':idf:`\1`', info_url)
license_url = 'https://spdx.org/licenses/' + tool_obj.options.license license_url = f'https://spdx.org/licenses/{tool_obj.options.license}'
print_out(""" print_out("""
.. _tool-{name}: .. _tool-{name}:
@@ -3035,35 +3024,35 @@ def main(argv: List[str]) -> None:
subparsers.add_parser('check', help='Print summary of tools installed or found in PATH') subparsers.add_parser('check', help='Print summary of tools installed or found in PATH')
export = subparsers.add_parser('export', help='Output command for setting tool paths, suitable for shell') export = subparsers.add_parser('export', help='Output command for setting tool paths, suitable for shell')
export.add_argument('--format', choices=[EXPORT_SHELL, EXPORT_KEY_VALUE], default=EXPORT_SHELL, export.add_argument('--format', choices=[EXPORT_SHELL, EXPORT_KEY_VALUE], default=EXPORT_SHELL,
help='Format of the output: shell (suitable for printing into shell), ' + help=('Format of the output: shell (suitable for printing into shell), '
'or key-value (suitable for parsing by other tools') 'or key-value (suitable for parsing by other tools'))
export.add_argument('--prefer-system', help='Normally, if the tool is already present in PATH, ' + export.add_argument('--prefer-system', help=('Normally, if the tool is already present in PATH, '
'but has an unsupported version, a version from the tools directory ' + 'but has an unsupported version, a version from the tools directory '
'will be used instead. If this flag is given, the version in PATH ' + 'will be used instead. If this flag is given, the version in PATH '
'will be used.', action='store_true') 'will be used.'), action='store_true')
export.add_argument('--deactivate', help='Output command for deactivate different ESP-IDF version, previously set with export', action='store_true') export.add_argument('--deactivate', help='Output command for deactivate different ESP-IDF version, previously set with export', action='store_true')
export.add_argument('--unset', help=argparse.SUPPRESS, action='store_true') export.add_argument('--unset', help=argparse.SUPPRESS, action='store_true')
export.add_argument('--add_paths_extras', help='Add idf-related path extras for deactivate option') export.add_argument('--add_paths_extras', help='Add idf-related path extras for deactivate option')
install = subparsers.add_parser('install', help='Download and install tools into the tools directory') install = subparsers.add_parser('install', help='Download and install tools into the tools directory')
install.add_argument('tools', metavar='TOOL', nargs='*', default=['required'], install.add_argument('tools', metavar='TOOL', nargs='*', default=['required'],
help='Tools to install. ' + help=('Tools to install.\n'
'To install a specific version use <tool_name>@<version> syntax. ' + 'To install a specific version use <tool_name>@<version> syntax. '
'To install tools by pattern use wildcards in <tool_name_pattern> . ' + 'To install tools by pattern use wildcards in <tool_name_pattern>. '
'Use empty or \'required\' to install required tools, not optional ones. ' + 'Use empty or \'required\' to install required tools, not optional ones. '
'Use \'all\' to install all tools, including the optional ones.') 'Use \'all\' to install all tools, including the optional ones.'))
install.add_argument('--targets', default='all', help='A comma separated list of desired chip targets for installing.' + install.add_argument('--targets', default='all', help=('A comma separated list of desired chip targets for installing. '
' It defaults to installing all supported targets.') 'It defaults to installing all supported targets.'))
download = subparsers.add_parser('download', help='Download the tools into the dist directory') download = subparsers.add_parser('download', help='Download the tools into the dist directory')
download.add_argument('--platform', default=CURRENT_PLATFORM, help='Platform to download the tools for') download.add_argument('--platform', default=CURRENT_PLATFORM, help='Platform to download the tools for')
download.add_argument('tools', metavar='TOOL', nargs='*', default=['required'], download.add_argument('tools', metavar='TOOL', nargs='*', default=['required'],
help='Tools to download. ' + help=('Tools to download. '
'To download a specific version use <tool_name>@<version> syntax. ' + 'To download a specific version use <tool_name>@<version> syntax. '
'To download tools by pattern use wildcards in <tool_name_pattern> . ' + 'To download tools by pattern use wildcards in <tool_name_pattern> . '
'Use empty or \'required\' to download required tools, not optional ones. ' + 'Use empty or \'required\' to download required tools, not optional ones. '
'Use \'all\' to download all tools, including the optional ones.') 'Use \'all\' to download all tools, including the optional ones.'))
download.add_argument('--targets', default='all', help='A comma separated list of desired chip targets for installing.' + download.add_argument('--targets', default='all', help=('A comma separated list of desired chip targets for installing. '
' It defaults to installing all supported targets.') ' It defaults to installing all supported targets.'))
uninstall = subparsers.add_parser('uninstall', help='Remove installed tools, that are not used by current version of ESP-IDF.') uninstall = subparsers.add_parser('uninstall', help='Remove installed tools, that are not used by current version of ESP-IDF.')
uninstall.add_argument('--dry-run', help='Print unused tools.', action='store_true') uninstall.add_argument('--dry-run', help='Print unused tools.', action='store_true')
@@ -3074,24 +3063,24 @@ def main(argv: List[str]) -> None:
if IDF_MAINTAINER: if IDF_MAINTAINER:
for subparser in [download, install]: for subparser in [download, install]:
subparser.add_argument('--mirror-prefix-map', nargs='*', subparser.add_argument('--mirror-prefix-map', nargs='*',
help='Pattern to rewrite download URLs, with source and replacement separated by comma.' + help=('Pattern to rewrite download URLs, with source and replacement separated by comma. '
' E.g. http://foo.com,http://test.foo.com') 'E.g. http://foo.com,http://test.foo.com'))
install_python_env = subparsers.add_parser('install-python-env', install_python_env = subparsers.add_parser('install-python-env',
help='Create Python virtual environment and install the ' + help=('Create Python virtual environment and install the '
'required Python packages') 'required Python packages'))
install_python_env.add_argument('--reinstall', help='Discard the previously installed environment', install_python_env.add_argument('--reinstall', help='Discard the previously installed environment',
action='store_true') action='store_true')
install_python_env.add_argument('--extra-wheels-dir', help='Additional directories with wheels ' + install_python_env.add_argument('--extra-wheels-dir', help=('Additional directories with wheels '
'to use during installation') 'to use during installation'))
install_python_env.add_argument('--extra-wheels-url', help='Additional URL with wheels', default=IDF_PIP_WHEELS_URL) install_python_env.add_argument('--extra-wheels-url', help='Additional URL with wheels', default=IDF_PIP_WHEELS_URL)
install_python_env.add_argument('--no-index', help='Work offline without retrieving wheels index') install_python_env.add_argument('--no-index', help='Work offline without retrieving wheels index')
install_python_env.add_argument('--features', default='core', help='A comma separated list of desired features for installing.' install_python_env.add_argument('--features', default='core', help=('A comma separated list of desired features for installing. '
' It defaults to installing just the core funtionality.') 'It defaults to installing just the core funtionality.'))
install_python_env.add_argument('--no-constraints', action='store_true', default=no_constraints_default, install_python_env.add_argument('--no-constraints', action='store_true', default=no_constraints_default,
help='Disable constraint settings. Use with care and only when you want to manage ' help=('Disable constraint settings. Use with care and only when you want to manage '
'package versions by yourself. It can be set with the IDF_PYTHON_CHECK_CONSTRAINTS ' 'package versions by yourself. It can be set with the IDF_PYTHON_CHECK_CONSTRAINTS '
'environment variable.') 'environment variable.'))
if IDF_MAINTAINER: if IDF_MAINTAINER:
add_version = subparsers.add_parser('add-version', help='Add or update download info for a version') add_version = subparsers.add_parser('add-version', help='Add or update download info for a version')
@@ -3136,11 +3125,9 @@ def main(argv: List[str]) -> None:
parser.print_help() parser.print_help()
parser.exit(1) parser.exit(1)
if args.quiet: g.quiet = args.quiet
g.quiet = True
if args.non_interactive: g.non_interactive = args.non_interactive
g.non_interactive = True
if 'unset' in args and args.unset: if 'unset' in args and args.unset:
args.deactivate = True args.deactivate = True
@@ -3163,13 +3150,13 @@ def main(argv: List[str]) -> None:
try: try:
g.idf_tools_path.decode('ascii') # type: ignore g.idf_tools_path.decode('ascii') # type: ignore
except UnicodeDecodeError: except UnicodeDecodeError:
fatal('IDF_TOOLS_PATH contains non-ASCII characters: {}'.format(g.idf_tools_path) + fatal(f'IDF_TOOLS_PATH contains non-ASCII characters: {g.idf_tools_path}'
'\nThis is not supported yet with Python 2. ' + '\nThis is not supported yet with Python 2. '
'Please set IDF_TOOLS_PATH to a directory with an ASCII name, or switch to Python 3.') 'Please set IDF_TOOLS_PATH to a directory with an ASCII name, or switch to Python 3.')
raise SystemExit(1) raise SystemExit(1)
if CURRENT_PLATFORM is None: if CURRENT_PLATFORM is None:
fatal('Platform {} appears to be unsupported'.format(PYTHON_PLATFORM)) fatal(f'Platform {PYTHON_PLATFORM} appears to be unsupported')
raise SystemExit(1) raise SystemExit(1)
if args.tools_json: if args.tools_json:
@@ -3177,7 +3164,7 @@ def main(argv: List[str]) -> None:
else: else:
g.tools_json = os.path.join(g.idf_path, TOOLS_FILE) g.tools_json = os.path.join(g.idf_path, TOOLS_FILE)
action_func_name = 'action_' + args.action.replace('-', '_') action_func_name = f'action_{args.action.replace("-", "_")}'
action_func = globals()[action_func_name] action_func = globals()[action_func_name]
action_func(args) action_func(args)