diff --git a/doc/whatsnew/fragments/9898.feature b/doc/whatsnew/fragments/9898.feature new file mode 100644 index 0000000000..d23462fc82 --- /dev/null +++ b/doc/whatsnew/fragments/9898.feature @@ -0,0 +1,4 @@ +colorized reporter now colorizes messages/categories that have been configured as `fail-on` in red inverse. +This makes it easier to quickly find the errors that are causing pylint CI job failures. + +Closes #9898 diff --git a/pylint/config/config_initialization.py b/pylint/config/config_initialization.py index 5be28e4326..a0210d47ac 100644 --- a/pylint/config/config_initialization.py +++ b/pylint/config/config_initialization.py @@ -139,6 +139,9 @@ def _config_initialization( # pylint: disable=too-many-statements # enable them linter.enable_fail_on_messages() + # Now that fail_on messages are enabled, pass them to colorized reporter + linter.pass_fail_on_config_to_color_reporter() + linter._parse_error_mode() # Link the base Namespace object on the current directory diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py index 5f2bab90d8..149521e0ac 100644 --- a/pylint/lint/pylinter.py +++ b/pylint/lint/pylinter.py @@ -55,7 +55,7 @@ from pylint.message import Message, MessageDefinition, MessageDefinitionStore from pylint.reporters.base_reporter import BaseReporter from pylint.reporters.progress_reporters import ProgressReporter -from pylint.reporters.text import TextReporter +from pylint.reporters.text import ColorizedTextReporter, TextReporter from pylint.reporters.ureports import nodes as report_nodes from pylint.typing import ( DirectoryNamespaceDict, @@ -531,6 +531,15 @@ def enable_fail_on_messages(self) -> None: def any_fail_on_issues(self) -> bool: return any(x in self.fail_on_symbols for x in self.stats.by_msg.keys()) + def pass_fail_on_config_to_color_reporter(self) -> None: + """Pass fail_on symbol configuration to colorized text reporter.""" + if isinstance(self.reporter, ColorizedTextReporter): + self.reporter.set_fail_on_symbols(self.fail_on_symbols) + elif isinstance(self.reporter, reporters.MultiReporter): + for _reporter in self.reporter._sub_reporters: + if isinstance(self.reporter, ColorizedTextReporter): + self.reporter.set_fail_on_symbols(self.fail_on_symbols) + def disable_reporters(self) -> None: """Disable all reporters.""" for _reporters in self._reports.values(): diff --git a/pylint/reporters/text.py b/pylint/reporters/text.py index 894207ad7d..75f4d6995b 100644 --- a/pylint/reporters/text.py +++ b/pylint/reporters/text.py @@ -215,6 +215,7 @@ class ColorizedTextReporter(TextReporter): "W": MessageStyle("magenta"), "E": MessageStyle("red", ("bold",)), "F": MessageStyle("red", ("bold", "underline")), + "X": MessageStyle("red", ("bold", "inverse")), "S": MessageStyle("yellow", ("inverse",)), # S stands for module Separator } @@ -225,6 +226,7 @@ def __init__( ) -> None: super().__init__(output) self.color_mapping = color_mapping or ColorizedTextReporter.COLOR_MAPPING + self.fail_on_symbols: list[str] = [] ansi_terms = ["xterm-16color", "xterm-256color"] if os.environ.get("TERM") not in ansi_terms: if sys.platform == "win32": @@ -237,6 +239,10 @@ def _get_decoration(self, msg_id: str) -> MessageStyle: """Returns the message style as defined in self.color_mapping.""" return self.color_mapping.get(msg_id[0]) or MessageStyle(None) + def set_fail_on_symbols(self, fail_on_symbols: list[str]) -> None: + """Sets the list of configured fail-on symbols.""" + self.fail_on_symbols = fail_on_symbols + def handle_message(self, msg: Message) -> None: """Manage message of different types, and colorize output using ANSI escape codes. @@ -246,7 +252,10 @@ def handle_message(self, msg: Message) -> None: modsep = colorize_ansi(make_header(msg), msg_style) self.writeln(modsep) self._modules.add(msg.module) - msg_style = self._get_decoration(msg.C) + if msg.symbol in self.fail_on_symbols: + msg_style = self._get_decoration("X") + else: + msg_style = self._get_decoration(msg.C) msg.msg = colorize_ansi(msg.msg, msg_style) msg.symbol = colorize_ansi(msg.symbol, msg_style)