# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """Plain text reporters: :text: the default one grouping messages by module :colorized: an ANSI colorized text reporter """ from __future__ import print_function import warnings import sys import six from pylint.interfaces import IReporter from pylint.reporters import BaseReporter from pylint import utils 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 = '{C}:{line:3d},{column:2d}: {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 = six.text_type(self.linter.config.msg_template or self.line_format) def write_message(self, msg): """Convenience method to write a formated 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' : ("blue", 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) if sys.platform == 'win32': 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)