From 34230426a615cf783de12fe4c04211e58e4f1010 Mon Sep 17 00:00:00 2001 From: "simon.chupin" Date: Mon, 15 Aug 2022 17:34:11 +0200 Subject: [PATCH 1/2] tools: fix multi-byte character appearance in idf.py monitor --- tools/idf_py_actions/tools.py | 42 +++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/tools/idf_py_actions/tools.py b/tools/idf_py_actions/tools.py index 9a8e735aea..43d9aeecc8 100644 --- a/tools/idf_py_actions/tools.py +++ b/tools/idf_py_actions/tools.py @@ -233,13 +233,11 @@ class RunTool: ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') return ansi_escape.sub('', text) - def prepare_for_print(out: bytes) -> str: - # errors='ignore' is here because some chips produce some garbage bytes - result = out.decode(errors='ignore') + def prepare_for_print(out: str) -> str: if not output_stream.isatty(): # delete escape sequence if we printing in environments where ANSI coloring is disabled - return delete_ansi_escape(result) - return result + return delete_ansi_escape(out) + return out def print_progression(output: str) -> None: # Print a new line on top of the previous line @@ -247,20 +245,40 @@ class RunTool: print('\r', end='') print(fit_text_in_terminal(output.strip('\n\r')), end='', file=output_stream) + async def read_stream() -> Optional[str]: + output_b = await input_stream.readline() + if not output_b: + return None + return output_b.decode(errors='ignore') + + async def read_interactive_stream() -> Optional[str]: + buffer = b'' + while True: + output_b = await input_stream.read(1) + if not output_b: + return None + try: + return (buffer + output_b).decode() + except UnicodeDecodeError: + buffer += output_b + if len(buffer) > 4: + # Multi-byte character contain up to 4 bytes and if buffer have more then 4 bytes + # and still can not decode it we can just ignore some bytes + return buffer.decode(errors='ignore') + try: - with open(output_filename, 'w') as output_file: + with open(output_filename, 'w', encoding='utf8') as output_file: while True: if self.interactive: - out = await input_stream.read(1) + output = await read_interactive_stream() else: - out = await input_stream.readline() - if not out: + output = await read_stream() + if not output: break - output = prepare_for_print(out) + output = prepare_for_print(output) output_file.write(output) - - # print output in progression way but only the progression related (that started with '[') and if verbose flag is not set if self.force_progression and output[0] == '[' and '-v' not in self.args and output_stream.isatty(): + # print output in progression way but only the progression related (that started with '[') and if verbose flag is not set print_progression(output) else: output_stream.write(output) From d1c61d29d36b7ccd9edf9a18c70bd73477d2c322 Mon Sep 17 00:00:00 2001 From: Roland Dobai Date: Wed, 31 Aug 2022 16:03:57 +0200 Subject: [PATCH 2/2] Tools: Handle IO error in idf.py output capturing Closes https://github.com/espressif/esp-idf/issues/9649 --- tools/idf_py_actions/tools.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/idf_py_actions/tools.py b/tools/idf_py_actions/tools.py index 43d9aeecc8..d3cbfc265f 100644 --- a/tools/idf_py_actions/tools.py +++ b/tools/idf_py_actions/tools.py @@ -246,10 +246,14 @@ class RunTool: print(fit_text_in_terminal(output.strip('\n\r')), end='', file=output_stream) async def read_stream() -> Optional[str]: - output_b = await input_stream.readline() - if not output_b: + try: + output_b = await input_stream.readline() + return output_b.decode(errors='ignore') + except (asyncio.LimitOverrunError, asyncio.IncompleteReadError) as e: + print(e, file=sys.stderr) + return None + except AttributeError: return None - return output_b.decode(errors='ignore') async def read_interactive_stream() -> Optional[str]: buffer = b''