change(tools): ruff formatting core_ext.py

This commit is contained in:
Marek Fiala
2025-09-04 14:43:43 +02:00
committed by BOT
parent e8f75f97b0
commit 8f4c52a358

View File

@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import fnmatch
import glob
@@ -10,9 +10,6 @@ import shutil
import subprocess
import sys
from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from urllib.error import URLError
from urllib.request import Request
from urllib.request import urlopen
@@ -20,25 +17,26 @@ from webbrowser import open_new_tab
import click
from click.core import Context
from idf_py_actions.constants import GENERATORS
from idf_py_actions.constants import PREVIEW_TARGETS
from idf_py_actions.constants import SUPPORTED_TARGETS
from idf_py_actions.constants import URL_TO_DOC
from idf_py_actions.errors import FatalError
from idf_py_actions.global_options import global_options
from idf_py_actions.tools import PropertyDict
from idf_py_actions.tools import TargetChoice
from idf_py_actions.tools import ensure_build_directory
from idf_py_actions.tools import generate_hints
from idf_py_actions.tools import get_target
from idf_py_actions.tools import idf_version
from idf_py_actions.tools import merge_action_lists
from idf_py_actions.tools import print_warning
from idf_py_actions.tools import PropertyDict
from idf_py_actions.tools import run_target
from idf_py_actions.tools import TargetChoice
from idf_py_actions.tools import yellow_print
def action_extensions(base_actions: Dict, project_path: str) -> Any:
def action_extensions(base_actions: dict, project_path: str) -> Any:
def build_target(target_name: str, ctx: Context, args: PropertyDict) -> None:
"""
Execute the target build system to build target 'target_name'
@@ -49,8 +47,15 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
ensure_build_directory(args, ctx.info_name)
run_target(target_name, args, force_progression=GENERATORS[args.generator].get('force_progression', False))
def size_target(target_name: str, ctx: Context, args: PropertyDict, output_format: str,
output_file: str, diff_map_file: str, legacy: bool) -> None:
def size_target(
target_name: str,
ctx: Context,
args: PropertyDict,
output_format: str,
output_file: str,
diff_map_file: str,
legacy: bool,
) -> None:
"""
Builds the app and then executes a size-related target passed in 'target_name'.
`tool_error_handler` handler is used to suppress errors during the build,
@@ -61,7 +66,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
for hint in generate_hints(stdout, stderr):
yellow_print(hint)
env: Dict[str, Any] = {}
env: dict[str, Any] = {}
if not legacy and output_format != 'json':
try:
@@ -93,7 +98,9 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
# The diff_map_file argument is a directory. Try to look for the map
# file directly in it, in case it's a build directory or in one level below
# if it's a project directory.
files = glob.glob(os.path.join(diff_map_file, '*.map')) or glob.glob(os.path.join(diff_map_file, '*/*.map'))
files = glob.glob(os.path.join(diff_map_file, '*.map')) or glob.glob(
os.path.join(diff_map_file, '*/*.map')
)
if not files:
raise FatalError(f'No diff map file found in {diff_map_file} directory')
if len(files) > 1:
@@ -104,8 +111,12 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
env['SIZE_DIFF_FILE'] = diff_map_file
ensure_build_directory(args, ctx.info_name)
run_target('all', args, force_progression=GENERATORS[args.generator].get('force_progression', False),
custom_error_handler=tool_error_handler)
run_target(
'all',
args,
force_progression=GENERATORS[args.generator].get('force_progression', False),
custom_error_handler=tool_error_handler,
)
run_target(target_name, args, env=env)
def list_build_system_targets(target_name: str, ctx: Context, args: PropertyDict) -> None:
@@ -121,13 +132,15 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
try:
import curses # noqa: F401
except ImportError:
raise FatalError('\n'.join(
['', "menuconfig failed to import the standard Python 'curses' library.",
'Please re-run the install script which might be able to fix the issue.']))
if sys.version_info[0] < 3:
# The subprocess lib cannot accept environment variables as "unicode".
# This encoding step is required only in Python 2.
style = style.encode(sys.getfilesystemencoding() or 'utf-8')
raise FatalError(
'\n'.join(
[
'',
"menuconfig failed to import the standard Python 'curses' library.",
'Please re-run the install script which might be able to fix the issue.',
]
)
)
os.environ['MENUCONFIG_STYLE'] = style
args.no_hints = True
build_target(target_name, ctx, args)
@@ -151,15 +164,16 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
except Exception:
if target_name in ['clang-check', 'clang-html-report']:
raise FatalError('command "{}" requires an additional plugin "pyclang". '
'Please install it via "pip install --upgrade pyclang"'.format(target_name))
raise FatalError(
f'command "{target_name}" requires an additional plugin "pyclang". '
'Please install it via "pip install --upgrade pyclang"'
)
raise FatalError(
'command "%s" is not known to idf.py and is not a %s target' % (target_name, args.generator))
raise FatalError(f'command "{target_name}" is not known to idf.py and is not a {args.generator} target')
run_target(target_name, args)
def verbose_callback(ctx: Context, param: List, value: str) -> Optional[str]:
def verbose_callback(ctx: Context, param: list, value: str) -> str | None:
if not value or ctx.resilient_parsing:
return None
@@ -170,36 +184,39 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
def clean(action: str, ctx: Context, args: PropertyDict) -> None:
if not os.path.isdir(args.build_dir):
print("Build directory '%s' not found. Nothing to clean." % args.build_dir)
print(f"Build directory '{args.build_dir}' not found. Nothing to clean.")
return
build_target('clean', ctx, args)
def fullclean(action: str, ctx: Context, args: PropertyDict) -> None:
build_dir = args.build_dir
if not os.path.isdir(build_dir):
print("Build directory '%s' not found. Nothing to clean." % build_dir)
print(f"Build directory '{build_dir}' not found. Nothing to clean.")
return
if len(os.listdir(build_dir)) == 0:
print("Build directory '%s' is empty. Nothing to clean." % build_dir)
print(f"Build directory '{build_dir}' is empty. Nothing to clean.")
return
if not os.path.exists(os.path.join(build_dir, 'CMakeCache.txt')):
raise FatalError(
"Directory '%s' doesn't seem to be a CMake build directory. Refusing to automatically "
"delete files in this directory. Delete the directory manually to 'clean' it." % build_dir)
f"Directory '{build_dir}' doesn't seem to be a CMake build directory. Refusing to automatically "
"delete files in this directory. Delete the directory manually to 'clean' it."
)
red_flags = ['CMakeLists.txt', '.git', '.svn']
for red in red_flags:
red = os.path.join(build_dir, red)
if os.path.exists(red):
raise FatalError(
"Refusing to automatically delete files in directory containing '%s'. Delete files manually if you're sure."
% red)
f"Refusing to automatically delete files in directory containing '{red}'. "
"Delete files manually if you're sure."
)
if args.verbose and len(build_dir) > 1:
print('The following symlinks were identified and removed:\n%s' % '\n'.join(build_dir))
symlinks_list = '\n'.join(build_dir)
print(f'The following symlinks were identified and removed:\n{symlinks_list}')
for f in os.listdir(build_dir): # TODO: once we are Python 3 only, this can be os.scandir()
f = os.path.join(build_dir, f)
if args.verbose:
print('Removing: %s' % f)
print(f'Removing: {f}')
if os.path.isdir(f):
shutil.rmtree(f)
else:
@@ -211,19 +228,20 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
if d == '__pycache__':
dir_to_delete = os.path.join(root, d)
if args.verbose:
print('Removing: %s' % dir_to_delete)
print(f'Removing: {dir_to_delete}')
shutil.rmtree(dir_to_delete)
for filename in fnmatch.filter(filenames, '*.py[co]'):
file_to_delete = os.path.join(root, filename)
if args.verbose:
print('Removing: %s' % file_to_delete)
print(f'Removing: {file_to_delete}')
os.remove(file_to_delete)
def set_target(action: str, ctx: Context, args: PropertyDict, idf_target: str) -> None:
if (not args['preview'] and idf_target in PREVIEW_TARGETS):
if not args['preview'] and idf_target in PREVIEW_TARGETS:
raise FatalError(
"%s is still in preview. You have to append '--preview' option after idf.py to use any preview feature."
% idf_target)
f"{idf_target} is still in preview. You have to append '--preview' option after "
'idf.py to use any preview feature.'
)
args.define_cache_entry.append('IDF_TARGET=' + idf_target)
print(f'Set Target to: {idf_target}, new sdkconfig will be created.')
env = {'_IDF_PY_SET_TARGET_ACTION': '1'}
@@ -232,12 +250,13 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
def reconfigure(action: str, ctx: Context, args: PropertyDict) -> None:
ensure_build_directory(args, ctx.info_name, True)
def validate_root_options(ctx: Context, args: PropertyDict, tasks: List) -> None:
def validate_root_options(ctx: Context, args: PropertyDict, tasks: list) -> None:
args.project_dir = os.path.realpath(args.project_dir)
if args.build_dir is not None and args.project_dir == os.path.realpath(args.build_dir):
raise FatalError(
'Setting the build directory to the project directory is not supported. Suggest dropping '
"--build-dir option, the default is a 'build' subdirectory inside the project directory.")
"--build-dir option, the default is a 'build' subdirectory inside the project directory."
)
if args.build_dir is None:
args.build_dir = os.path.join(args.project_dir, 'build')
args.build_dir = os.path.realpath(args.build_dir)
@@ -251,10 +270,10 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
if not version:
raise FatalError('ESP-IDF version cannot be determined')
print('ESP-IDF %s' % version)
print(f'ESP-IDF {version}')
sys.exit(0)
def list_targets_callback(ctx: Context, param: List, value: int) -> None:
def list_targets_callback(ctx: Context, param: list, value: int) -> None:
if not value or ctx.resilient_parsing:
return
@@ -267,7 +286,16 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
sys.exit(0)
def show_docs(action: str, ctx: Context, args: PropertyDict, no_browser: bool, language: str, starting_page: str, version: str, target: str) -> None:
def show_docs(
action: str,
ctx: Context,
args: PropertyDict,
no_browser: bool,
language: str,
starting_page: str,
version: str,
target: str,
) -> None:
if language == 'cn':
language = 'zh_CN'
if not version:
@@ -288,7 +316,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
except URLError:
print("We can't check the link's functionality because you don't have an internet connection")
if redirect_link:
print('Target', target, 'doesn\'t exist for version', version)
print('Target', target, "doesn't exist for version", version)
link = '/'.join([URL_TO_DOC, language, version, starting_page or ''])
if not no_browser:
print('Opening documentation in the default browser:')
@@ -306,7 +334,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
language = 'en'
return language
def help_and_exit(action: str, ctx: Context, param: List, json_option: bool, add_options: bool) -> None:
def help_and_exit(action: str, ctx: Context, param: list, json_option: bool, add_options: bool) -> None:
if json_option:
output_dict = {}
output_dict['target'] = get_target(param.project_dir) # type: ignore
@@ -355,8 +383,10 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
},
{
'names': ['-w/-n', '--cmake-warn-uninitialized/--no-warnings'],
'help': ('Enable CMake uninitialized variable warnings for CMake files inside the project directory. '
"(--no-warnings is now the default, and doesn't need to be specified.)"),
'help': (
'Enable CMake uninitialized variable warnings for CMake files inside the project directory. '
"(--no-warnings is now the default, and doesn't need to be specified.)"
),
'envvar': 'IDF_CMAKE_WARN_UNINITIALIZED',
'is_flag': True,
'default': False,
@@ -399,8 +429,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'names': ['--no-hints'],
'help': 'Disable hints on how to resolve errors and logging.',
'is_flag': True,
'default': False
}
'default': False,
},
],
'global_action_callbacks': [validate_root_options],
}
@@ -408,19 +438,31 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
# 'default' is introduced instead of simply setting 'text' as the default so that we know
# if the user explicitly specified the format or not. If the format is not specified, then
# the legacy OUTPUT_JSON CMake variable will be taken into account.
size_options = [{'names': ['--format', 'output_format'],
'type': click.Choice(['default', 'text', 'csv', 'json', 'json2', 'tree', 'raw']),
'help': 'Specify output format: text (same as "default"), csv, json, json2, tree or raw.',
'default': 'default'},
{'names': ['--legacy', '-l'],
'is_flag': True,
'default': os.environ.get('ESP_IDF_SIZE_LEGACY', '0') == '1',
'help': 'Use legacy esp-idf-size version'},
{'names': ['--diff', 'diff_map_file'],
'help': ('Show the differences in comparison with another project. '
'Argument can be map file or project directory.')},
{'names': ['--output-file', 'output_file'],
'help': 'Print output to the specified file instead of to the standard output'}]
size_options = [
{
'names': ['--format', 'output_format'],
'type': click.Choice(['default', 'text', 'csv', 'json', 'json2', 'tree', 'raw']),
'help': 'Specify output format: text (same as "default"), csv, json, json2, tree or raw.',
'default': 'default',
},
{
'names': ['--legacy', '-l'],
'is_flag': True,
'default': os.environ.get('ESP_IDF_SIZE_LEGACY', '0') == '1',
'help': 'Use legacy esp-idf-size version',
},
{
'names': ['--diff', 'diff_map_file'],
'help': (
'Show the differences in comparison with another project. '
'Argument can be map file or project directory.'
),
},
{
'names': ['--output-file', 'output_file'],
'help': 'Print output to the specified file instead of to the standard output',
},
]
build_actions = {
'actions': {
@@ -437,7 +479,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'and generate build files for the main build tool.\n\n'
'3. Run the main build tool (Ninja or GNU Make). '
'By default, the build tool is automatically detected '
'but it can be explicitly set by passing the -G option to idf.py.\n\n'),
'but it can be explicitly set by passing the -G option to idf.py.\n\n'
),
'options': global_options,
'order_dependencies': [
'reconfigure',
@@ -449,7 +492,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'menuconfig': {
'callback': menuconfig,
'help': 'Run "menuconfig" project configuration tool.',
'options': global_options + [
'options': global_options
+ [
{
'names': ['--style', '--color-scheme', 'style'],
'help': (
@@ -460,7 +504,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'- aquatic - a blue theme.\n\n'
'It is possible to customize these themes further'
' as it is described in the Color schemes section of the kconfiglib documentation.\n'
'The default value is \"aquatic\".'),
'The default value is "aquatic".'
),
'envvar': 'MENUCONFIG_STYLE',
'default': 'aquatic',
}
@@ -499,13 +544,13 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
},
'efuse-common-table': {
'callback': build_target,
'help': 'Generate C-source for IDF\'s eFuse fields.',
'help': "Generate C-source for IDF's eFuse fields.",
'order_dependencies': ['reconfigure'],
'options': global_options,
},
'efuse-custom-table': {
'callback': build_target,
'help': 'Generate C-source for user\'s eFuse fields.',
'help': "Generate C-source for user's eFuse fields.",
'order_dependencies': ['reconfigure'],
'options': global_options,
},
@@ -534,41 +579,37 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'callback': show_docs,
'help': 'Open web browser with documentation for ESP-IDF',
'options': [
{
'names': ['--no-browser', '-nb'],
'is_flag': True,
'help': 'Don\'t open browser.'
},
{'names': ['--no-browser', '-nb'], 'is_flag': True, 'help': "Don't open browser."},
{
'names': ['--language', '-l'],
'default': get_default_language(),
'type': click.Choice(['en', 'zh_CN', 'cn']),
'help': 'Documentation language. Your system language by default (en or cn)'
'help': 'Documentation language. Your system language by default (en or cn)',
},
{
'names': ['--starting-page', '-sp'],
'help': 'Documentation page (get-started, api-reference etc).'
},
{
'names': ['--version', '-v'],
'help': 'Version of ESP-IDF.'
'help': 'Documentation page (get-started, api-reference etc).',
},
{'names': ['--version', '-v'], 'help': 'Version of ESP-IDF.'},
{
'names': ['--target', '-t'],
'type': TargetChoice(SUPPORTED_TARGETS + PREVIEW_TARGETS + ['']),
'help': 'Chip target.'
}
]
'help': 'Chip target.',
},
],
},
'save-defconfig': {
'callback': save_defconfig,
'help': 'Generate a sdkconfig.defaults with options different from the default ones',
'options': global_options + [{
'names': ['--add-menu-labels'],
'is_flag': True,
'help': 'Add menu labels to minimal config.',
}]
}
'options': global_options
+ [
{
'names': ['--add-menu-labels'],
'is_flag': True,
'help': 'Add menu labels to minimal config.',
}
],
},
}
}
@@ -582,8 +623,9 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
"This isn't necessary during normal usage, "
'but can be useful after adding/removing files from the source tree, '
'or when modifying CMake cache variables. '
"For example, \"idf.py -DNAME='VALUE' reconfigure\" "
'can be used to set variable "NAME" in CMake cache to value "VALUE".'),
'For example, "idf.py -DNAME=\'VALUE\' reconfigure" '
'can be used to set variable "NAME" in CMake cache to value "VALUE".'
),
'options': global_options,
'order_dependencies': ['menuconfig', 'fullclean'],
},
@@ -594,8 +636,9 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'Set the chip target to build. This will remove the '
'existing sdkconfig file and corresponding CMakeCache and '
'create new ones according to the new target.\nFor example, '
"\"idf.py set-target esp32\" will select esp32 as the new chip "
'target.'),
'"idf.py set-target esp32" will select esp32 as the new chip '
'target.'
),
'arguments': [
{
'names': ['idf-target'],
@@ -612,7 +655,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'Delete build output files from the build directory, '
"forcing a 'full rebuild' the next time "
"the project is built. Cleaning doesn't delete "
'CMake configuration output and some other files'),
'CMake configuration output and some other files'
),
'order_dependencies': ['fullclean'],
},
'fullclean': {
@@ -625,7 +669,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'CMake will configure it from scratch. '
'Note that this option recursively deletes all files '
'in the build directory, so use with care.'
'Project configuration is not deleted.')
'Project configuration is not deleted.'
),
},
'python-clean': {
'callback': python_clean,
@@ -633,7 +678,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
'help': (
'Delete generated Python byte code from the IDF directory '
'which may cause issues when switching between IDF and Python versions. '
'It is advised to run this target after switching versions.')
'It is advised to run this target after switching versions.'
),
},
}
}
@@ -648,13 +694,13 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
{
'names': ['--json', 'json_option'],
'is_flag': True,
'help': 'Print out actions in machine-readable format for selected target.'
'help': 'Print out actions in machine-readable format for selected target.',
},
{
'names': ['--add-options'],
'is_flag': True,
'help': 'Add options about actions to machine-readable format.'
}
'help': 'Add options about actions to machine-readable format.',
},
],
}
}