mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bug/interactive_hints_v5.0' into 'release/v5.0'
tools: fix hints processing in interactive mode (v5.0) See merge request espressif/esp-idf!23804
This commit is contained in:
commit
128435993e
@ -18,8 +18,8 @@ from click.core import Context
|
|||||||
from idf_py_actions.constants import GENERATORS, PREVIEW_TARGETS, SUPPORTED_TARGETS, URL_TO_DOC
|
from idf_py_actions.constants import GENERATORS, PREVIEW_TARGETS, SUPPORTED_TARGETS, URL_TO_DOC
|
||||||
from idf_py_actions.errors import FatalError
|
from idf_py_actions.errors import FatalError
|
||||||
from idf_py_actions.global_options import global_options
|
from idf_py_actions.global_options import global_options
|
||||||
from idf_py_actions.tools import (PropertyDict, TargetChoice, ensure_build_directory, get_target, idf_version,
|
from idf_py_actions.tools import (PropertyDict, TargetChoice, ensure_build_directory, generate_hints, get_target,
|
||||||
merge_action_lists, print_hints, run_target)
|
idf_version, merge_action_lists, run_target, yellow_print)
|
||||||
|
|
||||||
|
|
||||||
def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
||||||
@ -42,7 +42,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def tool_error_handler(e: int, stdout: str, stderr: str) -> None:
|
def tool_error_handler(e: int, stdout: str, stderr: str) -> None:
|
||||||
print_hints(stdout, stderr)
|
for hint in generate_hints(stdout, stderr):
|
||||||
|
yellow_print(hint)
|
||||||
|
|
||||||
ensure_build_directory(args, ctx.info_name)
|
ensure_build_directory(args, ctx.info_name)
|
||||||
run_target('all', args, force_progression=GENERATORS[args.generator].get('force_progression', False),
|
run_target('all', args, force_progression=GENERATORS[args.generator].get('force_progression', False),
|
||||||
|
@ -8,7 +8,7 @@ import sys
|
|||||||
from asyncio.subprocess import Process
|
from asyncio.subprocess import Process
|
||||||
from io import open
|
from io import open
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
from typing import Any, Dict, List, Match, Optional, TextIO, Tuple, Union
|
from typing import Any, Dict, Generator, List, Match, Optional, TextIO, Tuple, Union
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import yaml
|
import yaml
|
||||||
@ -107,47 +107,57 @@ def debug_print_idf_version() -> None:
|
|||||||
print_warning(f'ESP-IDF {idf_version() or "version unknown"}')
|
print_warning(f'ESP-IDF {idf_version() or "version unknown"}')
|
||||||
|
|
||||||
|
|
||||||
def print_hints(*filenames: str) -> None:
|
def load_hints() -> Any:
|
||||||
"""Getting output files and printing hints on how to resolve errors based on the output."""
|
"""Helper function to load hints yml file"""
|
||||||
with open(os.path.join(os.path.dirname(__file__), 'hints.yml'), 'r') as file:
|
with open(os.path.join(os.path.dirname(__file__), 'hints.yml'), 'r') as file:
|
||||||
hints = yaml.safe_load(file)
|
hints = yaml.safe_load(file)
|
||||||
|
return hints
|
||||||
|
|
||||||
|
|
||||||
|
def generate_hints_buffer(output: str, hints: list) -> Generator:
|
||||||
|
"""Helper function to process hints within a string buffer"""
|
||||||
|
for hint in hints:
|
||||||
|
variables_list = hint.get('variables')
|
||||||
|
hint_list, hint_vars, re_vars = [], [], []
|
||||||
|
match: Optional[Match[str]] = None
|
||||||
|
try:
|
||||||
|
if variables_list:
|
||||||
|
for variables in variables_list:
|
||||||
|
hint_vars = variables['hint_variables']
|
||||||
|
re_vars = variables['re_variables']
|
||||||
|
regex = hint['re'].format(*re_vars)
|
||||||
|
if re.compile(regex).search(output):
|
||||||
|
try:
|
||||||
|
hint_list.append(hint['hint'].format(*hint_vars))
|
||||||
|
except KeyError as e:
|
||||||
|
red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint))
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
match = re.compile(hint['re']).search(output)
|
||||||
|
except KeyError as e:
|
||||||
|
red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint))
|
||||||
|
sys.exit(1)
|
||||||
|
except re.error as e:
|
||||||
|
red_print('{} from hints.yml have {} problem. Check hints.yml file.'.format(hint['re'], e))
|
||||||
|
sys.exit(1)
|
||||||
|
if hint_list:
|
||||||
|
for message in hint_list:
|
||||||
|
yield ' '.join(['HINT:', message])
|
||||||
|
elif match:
|
||||||
|
extra_info = ', '.join(match.groups()) if hint.get('match_to_output', '') else ''
|
||||||
|
try:
|
||||||
|
yield ' '.join(['HINT:', hint['hint'].format(extra_info)])
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError("Argument 'hint' missing in {}. Check hints.yml file.".format(hint))
|
||||||
|
|
||||||
|
|
||||||
|
def generate_hints(*filenames: str) -> Generator:
|
||||||
|
"""Getting output files and printing hints on how to resolve errors based on the output."""
|
||||||
|
hints = load_hints()
|
||||||
for file_name in filenames:
|
for file_name in filenames:
|
||||||
with open(file_name, 'r') as file:
|
with open(file_name, 'r') as file:
|
||||||
output = ' '.join(line.strip() for line in file if line.strip())
|
output = ' '.join(line.strip() for line in file if line.strip())
|
||||||
for hint in hints:
|
yield from generate_hints_buffer(output, hints)
|
||||||
variables_list = hint.get('variables')
|
|
||||||
hint_list, hint_vars, re_vars = [], [], []
|
|
||||||
match: Optional[Match[str]] = None
|
|
||||||
try:
|
|
||||||
if variables_list:
|
|
||||||
for variables in variables_list:
|
|
||||||
hint_vars = variables['hint_variables']
|
|
||||||
re_vars = variables['re_variables']
|
|
||||||
regex = hint['re'].format(*re_vars)
|
|
||||||
if re.compile(regex).search(output):
|
|
||||||
try:
|
|
||||||
hint_list.append(hint['hint'].format(*hint_vars))
|
|
||||||
except KeyError as e:
|
|
||||||
red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint))
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
match = re.compile(hint['re']).search(output)
|
|
||||||
except KeyError as e:
|
|
||||||
red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint))
|
|
||||||
sys.exit(1)
|
|
||||||
except re.error as e:
|
|
||||||
red_print('{} from hints.yml have {} problem. Check hints.yml file.'.format(hint['re'], e))
|
|
||||||
sys.exit(1)
|
|
||||||
if hint_list:
|
|
||||||
for message in hint_list:
|
|
||||||
yellow_print('HINT:', message)
|
|
||||||
elif match:
|
|
||||||
extra_info = ', '.join(match.groups()) if hint.get('match_to_output', '') else ''
|
|
||||||
try:
|
|
||||||
yellow_print(' '.join(['HINT:', hint['hint'].format(extra_info)]))
|
|
||||||
except KeyError as e:
|
|
||||||
red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def fit_text_in_terminal(out: str) -> str:
|
def fit_text_in_terminal(out: str) -> str:
|
||||||
@ -210,7 +220,10 @@ class RunTool:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if stderr_output_file and stdout_output_file:
|
if stderr_output_file and stdout_output_file:
|
||||||
print_hints(stderr_output_file, stdout_output_file)
|
# hints in interactive mode were already processed, don't print them again
|
||||||
|
if not self.interactive:
|
||||||
|
for hint in generate_hints(stderr_output_file, stdout_output_file):
|
||||||
|
yellow_print(hint)
|
||||||
raise FatalError('{} failed with exit code {}, output of the command is in the {} and {}'.format(self.tool_name, process.returncode,
|
raise FatalError('{} failed with exit code {}, output of the command is in the {} and {}'.format(self.tool_name, process.returncode,
|
||||||
stderr_output_file, stdout_output_file))
|
stderr_output_file, stdout_output_file))
|
||||||
|
|
||||||
@ -281,6 +294,10 @@ class RunTool:
|
|||||||
# use ANSI color converter for Monitor on Windows
|
# use ANSI color converter for Monitor on Windows
|
||||||
output_converter = get_ansi_converter(output_stream) if self.convert_output else output_stream
|
output_converter = get_ansi_converter(output_stream) if self.convert_output else output_stream
|
||||||
|
|
||||||
|
# used in interactive mode to print hints after matched line
|
||||||
|
hints = load_hints()
|
||||||
|
last_line = ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(output_filename, 'w', encoding='utf8') as output_file:
|
with open(output_filename, 'w', encoding='utf8') as output_file:
|
||||||
while True:
|
while True:
|
||||||
@ -290,6 +307,7 @@ class RunTool:
|
|||||||
output = await read_stream()
|
output = await read_stream()
|
||||||
if not output:
|
if not output:
|
||||||
break
|
break
|
||||||
|
|
||||||
output_noescape = delete_ansi_escape(output)
|
output_noescape = delete_ansi_escape(output)
|
||||||
# Always remove escape sequences when writing the build log.
|
# Always remove escape sequences when writing the build log.
|
||||||
output_file.write(output_noescape)
|
output_file.write(output_noescape)
|
||||||
@ -305,6 +323,14 @@ class RunTool:
|
|||||||
else:
|
else:
|
||||||
output_converter.write(output)
|
output_converter.write(output)
|
||||||
output_converter.flush()
|
output_converter.flush()
|
||||||
|
|
||||||
|
# process hints for last line and print them right away
|
||||||
|
if self.interactive:
|
||||||
|
last_line += output
|
||||||
|
if last_line[-1] == '\n':
|
||||||
|
for hint in generate_hints_buffer(last_line, hints):
|
||||||
|
yellow_print(hint)
|
||||||
|
last_line = ''
|
||||||
except (RuntimeError, EnvironmentError) as e:
|
except (RuntimeError, EnvironmentError) as e:
|
||||||
yellow_print('WARNING: The exception {} was raised and we can\'t capture all your {} and '
|
yellow_print('WARNING: The exception {} was raised and we can\'t capture all your {} and '
|
||||||
'hints on how to resolve errors can be not accurate.'.format(e, output_stream.name.strip('<>')))
|
'hints on how to resolve errors can be not accurate.'.format(e, output_stream.name.strip('<>')))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user