# Copyright (c) 2006-2007, 2010-2014 LOGILAB S.A. (Paris, FRANCE) # Copyright (c) 2012-2014 Google, Inc. # Copyright (c) 2014 Brett Cannon # Copyright (c) 2014 Arun Persaud # Copyright (c) 2015-2018, 2020 Claudiu Popa # Copyright (c) 2015 Florian Bruhin # Copyright (c) 2015 Ionel Cristian Maries # Copyright (c) 2016 y2kbugger # Copyright (c) 2018-2019 Nick Drozd # Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com> # Copyright (c) 2018 Jace Browning # Copyright (c) 2019-2020 Pierre Sassoulas # Copyright (c) 2019 Hugo van Kemenade # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html # For details: https://github.com/PyCQA/pylint/blob/master/COPYING """Plain text reporters: :text: the default one grouping messages by module :colorized: an ANSI colorized text reporter """ import os import sys import warnings from pylint import utils from pylint.interfaces import IReporter from pylint.reporters import BaseReporter from pylint.reporters.ureports.text_writer import TextWriter TITLE_UNDERLINES = ["", "=", "-", "."] ANSI_PREFIX = "\033[" ANSI_END = "m" ANSI_RESET = "\033[0m" ANSI_STYLES = { "reset": "0", "bold": "1", "italic": "3", "underline": "4", "blink": "5", "inverse": "7", "strike": "9", } ANSI_COLORS = { "reset": "0", "black": "30", "red": "31", "green": "32", "yellow": "33", "blue": "34", "magenta": "35", "cyan": "36", "white": "37", } def _get_ansi_code(color=None, style=None): """return ansi escape code corresponding to color and style :type color: str or None :param color: the color name (see `ANSI_COLORS` for available values) or the color number when 256 colors are available :type style: str or None :param style: style string (see `ANSI_COLORS` for available values). To get several style effects at the same time, use a coma as separator. :raise KeyError: if an unexistent color or style identifier is given :rtype: str :return: the built escape code """ ansi_code = [] if style: style_attrs = utils._splitstrip(style) for effect in style_attrs: ansi_code.append(ANSI_STYLES[effect]) if color: if color.isdigit(): ansi_code.extend(["38", "5"]) ansi_code.append(color) else: ansi_code.append(ANSI_COLORS[color]) if ansi_code: return ANSI_PREFIX + ";".join(ansi_code) + ANSI_END return "" def colorize_ansi(msg, color=None, style=None): """colorize message by wrapping it with ansi escape codes :type msg: str or unicode :param msg: the message string to colorize :type color: str or None :param color: the color identifier (see `ANSI_COLORS` for available values) :type style: str or None :param style: style string (see `ANSI_COLORS` for available values). To get several style effects at the same time, use a coma as separator. :raise KeyError: if an unexistent color or style identifier is given :rtype: str or unicode :return: the ansi escaped string """ # If both color and style are not defined, then leave the text as is if color is None and style is None: return msg escape_code = _get_ansi_code(color, style) # If invalid (or unknown) color, don't wrap msg with ansi codes if escape_code: return "%s%s%s" % (escape_code, msg, ANSI_RESET) return msg class TextReporter(BaseReporter): """Reports messages and layouts in plain text""" __implements__ = IReporter name = "text" extension = "txt" line_format = "{path}:{line}:{column}: {msg_id}: {msg} ({symbol})" def __init__(self, output=None): BaseReporter.__init__(self, output) self._modules = set() self._template = None def on_set_current_module(self, module, filepath): self._template = str(self.linter.config.msg_template or self.line_format) def write_message(self, msg): """Convenience method to write a formatted message with class default template""" self.writeln(msg.format(self._template)) def handle_message(self, msg): """manage message of different type and in the context of path""" if msg.module not in self._modules: if msg.module: self.writeln("************* Module %s" % msg.module) self._modules.add(msg.module) else: self.writeln("************* ") self.write_message(msg) def _display(self, layout): """launch layouts display""" print(file=self.out) TextWriter().format(layout, self.out) class ParseableTextReporter(TextReporter): """a reporter very similar to TextReporter, but display messages in a form recognized by most text editors : :: """ name = "parseable" line_format = "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" def __init__(self, output=None): warnings.warn( "%s output format is deprecated. This is equivalent " "to --msg-template=%s" % (self.name, self.line_format), DeprecationWarning, ) TextReporter.__init__(self, output) class VSTextReporter(ParseableTextReporter): """Visual studio text reporter""" name = "msvs" line_format = "{path}({line}): [{msg_id}({symbol}){obj}] {msg}" class ColorizedTextReporter(TextReporter): """Simple TextReporter that colorizes text output""" name = "colorized" COLOR_MAPPING = { "I": ("green", None), "C": (None, "bold"), "R": ("magenta", "bold, italic"), "W": ("magenta", None), "E": ("red", "bold"), "F": ("red", "bold, underline"), "S": ("yellow", "inverse"), # S stands for module Separator } def __init__(self, output=None, color_mapping=None): TextReporter.__init__(self, output) self.color_mapping = color_mapping or dict(ColorizedTextReporter.COLOR_MAPPING) ansi_terms = ["xterm-16color", "xterm-256color"] if os.environ.get("TERM") not in ansi_terms: if sys.platform == "win32": # pylint: disable=import-error,import-outside-toplevel import colorama self.out = colorama.AnsiToWin32(self.out) def _get_decoration(self, msg_id): """Returns the tuple color, style associated with msg_id as defined in self.color_mapping """ try: return self.color_mapping[msg_id[0]] except KeyError: return None, None def handle_message(self, msg): """manage message of different types, and colorize output using ansi escape codes """ if msg.module not in self._modules: color, style = self._get_decoration("S") if msg.module: modsep = colorize_ansi( "************* Module %s" % msg.module, color, style ) else: modsep = colorize_ansi("************* %s" % msg.module, color, style) self.writeln(modsep) self._modules.add(msg.module) color, style = self._get_decoration(msg.C) msg = msg._replace( **{ attr: colorize_ansi(getattr(msg, attr), color, style) for attr in ("msg", "symbol", "category", "C") } ) self.write_message(msg) def register(linter): """Register the reporter classes with the linter.""" linter.register_reporter(TextReporter) linter.register_reporter(ParseableTextReporter) linter.register_reporter(VSTextReporter) linter.register_reporter(ColorizedTextReporter)