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:
Roland Dobai
2023-05-19 15:45:04 +08:00
2 changed files with 68 additions and 41 deletions

View File

@@ -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),

View File

@@ -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,13 +107,15 @@ 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)
for file_name in filenames: return hints
with open(file_name, 'r') as file:
output = ' '.join(line.strip() for line in file if line.strip())
def generate_hints_buffer(output: str, hints: list) -> Generator:
"""Helper function to process hints within a string buffer"""
for hint in hints: for hint in hints:
variables_list = hint.get('variables') variables_list = hint.get('variables')
hint_list, hint_vars, re_vars = [], [], [] hint_list, hint_vars, re_vars = [], [], []
@@ -140,14 +142,22 @@ def print_hints(*filenames: str) -> None:
sys.exit(1) sys.exit(1)
if hint_list: if hint_list:
for message in hint_list: for message in hint_list:
yellow_print('HINT:', message) yield ' '.join(['HINT:', message])
elif match: elif match:
extra_info = ', '.join(match.groups()) if hint.get('match_to_output', '') else '' extra_info = ', '.join(match.groups()) if hint.get('match_to_output', '') else ''
try: try:
yellow_print(' '.join(['HINT:', hint['hint'].format(extra_info)])) yield ' '.join(['HINT:', hint['hint'].format(extra_info)])
except KeyError as e: except KeyError:
red_print('Argument {} missing in {}. Check hints.yml file.'.format(e, hint)) raise KeyError("Argument 'hint' missing in {}. Check hints.yml file.".format(hint))
sys.exit(1)
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:
with open(file_name, 'r') as file:
output = ' '.join(line.strip() for line in file if line.strip())
yield from generate_hints_buffer(output, hints)
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('<>')))