From e3ac647675035d40877b43636843f4f737edcd42 Mon Sep 17 00:00:00 2001 From: Konstantin Gukov Date: Sat, 13 Mar 2021 12:49:30 +0100 Subject: [PATCH] Fix feedback minification. The previous implementation had 3 bugs: - IndexError when only 1 line in error_string; - infinite loop when repeatedly can't strip down error_string; - ", ln" instead of ", in" in one matching expression that rendered it useless. Also the logic itself was "magical": it removed 4 lines at a time, starting from a middle line but if possible (never successfully) from a line before that has an exception start in it... This change should fix all these problems. --- .../cli/command_modules/feedback/custom.py | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/feedback/custom.py b/src/azure-cli/azure/cli/command_modules/feedback/custom.py index 4b82c6f732f..e125ec4c73e 100644 --- a/src/azure-cli/azure/cli/command_modules/feedback/custom.py +++ b/src/azure-cli/azure/cli/command_modules/feedback/custom.py @@ -356,7 +356,7 @@ class ErrorMinifier: def __init__(self, errors_list): self._errors_list = errors_list - self._capacity = None + self._capacity = None # to how many symbols should minify self._minified_error = "\n".join(self._errors_list) def set_capacity(self, capacity): @@ -399,11 +399,13 @@ def _get_minified_errors(self): # pylint: disable=too-many-return-statements if len(errors_string) <= self._capacity: return errors_string - # last resort keep removing middle lines - while len(errors_string) > self._capacity: - errors_string = self._minify_by_removing_lines(errors_string) + # remove some lines + errors_string = self._minify_by_removing_lines(errors_string, desired_length=self._capacity) + if len(errors_string) <= self._capacity: + return errors_string - return errors_string + # last resort: strip off the suffix + return self._minify_by_removing_suffix(errors_string, desired_length=self._capacity) @staticmethod def _minify_by_shortening_file_names(error_string, levels=5): @@ -459,23 +461,41 @@ def _minify_by_removing_nested_exceptions(error_string): return "\n".join(lines) @staticmethod - def _minify_by_removing_lines(error_string): - error_string = error_string.replace(ErrorMinifier._CONTINUATION_STR, "") - lines = error_string.splitlines() + def _minify_by_removing_lines(error_string, desired_length): + """ + Repeatedly remove the lines from the middle, as a last resort remove even the + first line but keep the last one, in the effort to strip down the error string + to the desired length. + """ + if len(error_string) <= desired_length: + return error_string - mid = int(len(lines) / 2) + 1 - if not (".py" in lines[mid] and ", ln" in lines[mid]): - mid -= 1 + symbols_to_remove = len(error_string) - desired_length + len(ErrorMinifier._CONTINUATION_STR) + lines = error_string.splitlines(keepends=True) - new_lines = [] - for i, line in enumerate(lines): - if i == mid: - new_lines.append(ErrorMinifier._CONTINUATION_STR.strip()) - if i in range(mid, mid + 4): - continue - new_lines.append(line) + if len(lines) <= 1 or symbols_to_remove <= 0: + # nothing to remove + return error_string - return "\n".join(new_lines) + mid = 0 + while len(lines) > 1 and symbols_to_remove > 0: + # remove the middle line, when even prefer to remove the one closer to the start + mid = (len(lines) - 1) // 2 + symbols_to_remove -= len(lines.pop(mid)) + + lines.insert(mid, ErrorMinifier._CONTINUATION_STR) + return "".join(lines) + + @staticmethod + def _minify_by_removing_suffix(error_string, desired_length): + """ + Strip off the suffix of the error string, force it to be <= desired length. + """ + if len(error_string) <= desired_length: + return error_string + + continuation = ErrorMinifier._CONTINUATION_STR.strip()[:desired_length] + return error_string[:desired_length - len(continuation)] + continuation def __str__(self): if self._minified_error: