diff options
author | Torsten Marek <tmarek@google.com> | 2013-06-19 16:29:18 +0200 |
---|---|---|
committer | Torsten Marek <tmarek@google.com> | 2013-06-19 16:29:18 +0200 |
commit | f6bddc94f23ef417ae1577b6416c412a82aff9c7 (patch) | |
tree | 14a4b526b3ac568efe4f18ac058ba1e4a7f827fa | |
parent | c5e3e1ff04b2d8f3d2525d24b426ef1c7e73a21a (diff) | |
download | pylint-f6bddc94f23ef417ae1577b6416c412a82aff9c7.tar.gz |
Turn reporters into proper plugins rather than classes that are loaded explicitly.
-rw-r--r-- | checkers/__init__.py | 35 | ||||
-rw-r--r-- | lint.py | 59 | ||||
-rw-r--r-- | reporters/__init__.py | 5 | ||||
-rw-r--r-- | reporters/html.py | 5 | ||||
-rw-r--r-- | reporters/text.py | 17 | ||||
-rw-r--r-- | test/unittest_lint.py | 4 | ||||
-rw-r--r-- | utils.py | 31 |
7 files changed, 98 insertions, 58 deletions
diff --git a/checkers/__init__.py b/checkers/__init__.py index e1254a7..ff9d421 100644 --- a/checkers/__init__.py +++ b/checkers/__init__.py @@ -40,14 +40,13 @@ messages nor reports. XXX not true, emit a 07 report ! import tokenize import warnings -from os import listdir -from os.path import dirname, join, isdir, splitext +from os.path import dirname from astroid.utils import ASTWalker -from logilab.common.modutils import load_module_from_file from logilab.common.configuration import OptionsProviderMixIn from pylint.reporters import diff_string +from pylint.utils import register_plugins def table_lines_from_stats(stats, old_stats, columns): """get values listed in <columns> from <stats> and <old_stats>, @@ -145,34 +144,8 @@ class BaseTokenChecker(BaseChecker): raise NotImplementedError() -PY_EXTS = ('.py', '.pyc', '.pyo', '.pyw', '.so', '.dll') - def initialize(linter): """initialize linter with checkers in this package """ - package_load(linter, __path__[0]) + register_plugins(linter, __path__[0]) -def package_load(linter, directory): - """load all module and package in the given directory, looking for a - 'register' function in each one, used to register pylint checkers - """ - imported = {} - for filename in listdir(directory): - basename, extension = splitext(filename) - if basename in imported or basename == '__pycache__': - continue - if extension in PY_EXTS and basename != '__init__' or ( - not extension and isdir(join(directory, basename))): - try: - module = load_module_from_file(join(directory, filename)) - except ValueError: - # empty module name (usually emacs auto-save files) - continue - except ImportError, exc: - import sys - print >> sys.stderr, "Problem importing module %s: %s" % (filename, exc) - else: - if hasattr(module, 'register'): - module.register(linter) - imported[basename] = 1 - -__all__ = ('BaseChecker', 'initialize', 'package_load') +__all__ = ('BaseChecker', 'initialize') @@ -52,21 +52,14 @@ from pylint.utils import ( expand_modules, tokenize_module) from pylint.interfaces import ILinter, IRawChecker, ITokenChecker, IAstroidChecker from pylint.checkers import (BaseTokenChecker, - table_lines_from_stats) -from pylint.reporters.text import (TextReporter, ParseableTextReporter, - VSTextReporter, ColorizedTextReporter) -from pylint.reporters.html import HTMLReporter + table_lines_from_stats, + initialize as checkers_initialize) +from pylint.reporters import initialize as reporters_initialize from pylint import config from pylint.__pkginfo__ import version -REPORTER_OPT_MAP = {'text': TextReporter, - 'parseable': ParseableTextReporter, - 'msvs': VSTextReporter, - 'colorized': ColorizedTextReporter, - 'html': HTMLReporter,} - def _get_python_path(filepath): dirname = os.path.dirname(os.path.realpath( @@ -281,12 +274,13 @@ This is used by the global evaluation report (RP0004).'}), ('Reports', 'Options related to output formating and reporting'), ) - def __init__(self, options=(), reporter=None, option_groups=(), - pylintrc=None): + def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None): # some stuff has to be done before ancestors initialization... # # checkers / reporter / astroid manager self.reporter = None + self._reporter_name = None + self._reporters = {} self._checkers = {} self._ignore_file = False # visit variables @@ -324,11 +318,16 @@ This is used by the global evaluation report (RP0004).'}), self.register_checker(self) self._dynamic_plugins = set() self.load_provider_defaults() - self.set_reporter(reporter or TextReporter(sys.stdout)) + if reporter: + self.set_reporter(reporter) def load_default_plugins(self): - from pylint import checkers - checkers.initialize(self) + checkers_initialize(self) + reporters_initialize(self) + # Make sure to load the default reporter, because + # the option has been set before the plugins had been loaded. + if not self.reporter: + self._load_reporter() def prepare_import_path(self, args): """Prepare sys.path for running the linter checks.""" @@ -352,6 +351,17 @@ This is used by the global evaluation report (RP0004).'}), module = load_module_from_name(modname) module.register(self) + def _load_reporter(self): + name = self._reporter_name.lower() + if name in self._reporters: + self.set_reporter(self._reporters[name]()) + else: + qname = self._reporter_name + module = load_module_from_name(get_module_part(qname)) + class_name = qname.split('.')[-1] + reporter_class = getattr(module, class_name) + self.set_reporter(reporter_class()) + def set_reporter(self, reporter): """set the reporter used to display messages and reports""" self.reporter = reporter @@ -376,20 +386,20 @@ This is used by the global evaluation report (RP0004).'}), else : meth(value) elif optname == 'output-format': - if value.lower() in REPORTER_OPT_MAP: - self.set_reporter(REPORTER_OPT_MAP[value.lower()]()) - else: - module = load_module_from_name(get_module_part(value)) - class_name = value.split('.')[-1] - reporter_class = getattr(module, class_name) - self.set_reporter(reporter_class()) - + self._reporter_name = value + # If the reporters are already available, load + # the reporter class. + if self._reporters: + self._load_reporter() try: BaseTokenChecker.set_option(self, optname, value, action, optdict) except UnsupportedAction: print >> sys.stderr, 'option %s can\'t be read from config file' % \ optname + def register_reporter(self, reporter_class): + self._reporters[reporter_class.name] = reporter_class + # checkers manipulation methods ############################################ def register_checker(self, checker): @@ -929,8 +939,7 @@ are done by default'''}), 'default': False, 'hide': True, 'help' : 'Profiled execution.'}), - ), option_groups=self.option_groups, - reporter=reporter, pylintrc=self._rcfile) + ), option_groups=self.option_groups, pylintrc=self._rcfile) # register standard checkers linter.load_default_plugins() # load command line plugins diff --git a/reporters/__init__.py b/reporters/__init__.py index 5c36af2..3e87aed 100644 --- a/reporters/__init__.py +++ b/reporters/__init__.py @@ -15,6 +15,8 @@ import sys, locale +from pylint import utils + CMPS = ['=', '-', '+'] # py3k has no more cmp builtin @@ -110,3 +112,6 @@ class BaseReporter(object): pass +def initialize(linter): + """initialize linter with reporters in this package """ + utils.register_plugins(linter, __path__[0]) diff --git a/reporters/html.py b/reporters/html.py index cac08b2..23c1d18 100644 --- a/reporters/html.py +++ b/reporters/html.py @@ -27,6 +27,7 @@ class HTMLReporter(BaseReporter): """report messages and layouts in HTML""" __implements__ = IReporter + name = 'html' extension = 'html' def __init__(self, output=sys.stdout): @@ -64,3 +65,7 @@ class HTMLReporter(BaseReporter): self.msgs = [] HTMLWriter().format(layout, self.out) + +def register(linter): + """Register the reporter classes with the linter.""" + linter.register_reporter(HTMLReporter) diff --git a/reporters/text.py b/reporters/text.py index d5b4a9b..54e13a0 100644 --- a/reporters/text.py +++ b/reporters/text.py @@ -35,8 +35,9 @@ TITLE_UNDERLINES = ['', '=', '-', '.'] class TextReporter(BaseReporter): """reports messages and layouts in plain text """ - + __implements__ = IReporter + name = 'text' extension = 'txt' def __init__(self, output=None): @@ -69,6 +70,8 @@ class ParseableTextReporter(TextReporter): <filename>:<linenum>:<msg> """ + name = 'parseable' + line_format = '%(path)s:%(line)s: [%(sigle)s%(obj)s] %(msg)s' def __init__(self, output=None, relative=True): @@ -92,10 +95,14 @@ class ParseableTextReporter(TextReporter): class VSTextReporter(ParseableTextReporter): """Visual studio text reporter""" line_format = '%(path)s(%(line)s): [%(sigle)s%(obj)s] %(msg)s' + + name = 'msvs' class ColorizedTextReporter(TextReporter): """Simple TextReporter that colorizes text output""" + name = 'colorized' + COLOR_MAPPING = { "I" : ("green", None), 'C' : (None, "bold"), @@ -143,3 +150,11 @@ class ColorizedTextReporter(TextReporter): msg = colorize_ansi(msg, color, style) sigle = colorize_ansi(sigle, color, style) self.writeln('%s:%3s%s: %s' % (sigle, line, obj, 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) diff --git a/test/unittest_lint.py b/test/unittest_lint.py index 64d2d29..a6706b1 100644 --- a/test/unittest_lint.py +++ b/test/unittest_lint.py @@ -29,6 +29,7 @@ from pylint.lint import PyLinter, Run, UnknownMessage, preprocess_options, \ from pylint.utils import sort_msgs, PyLintASTWalker, MSG_STATE_SCOPE_CONFIG, \ MSG_STATE_SCOPE_MODULE, tokenize_module from pylint.testutils import TestReporter +from pylint.reporters import text from pylint import checkers class SortMessagesTC(TestCase): @@ -80,6 +81,7 @@ class PyLinterTC(TestCase): self.linter.config.persistent = 0 # register checkers checkers.initialize(self.linter) + self.linter.set_reporter(TestReporter()) def test_message_help(self): msg = self.linter.get_message_help('F0001', checkerref=True) @@ -239,6 +241,7 @@ class PyLinterTC(TestCase): self.assertTrue(':C0112 (empty-docstring): *Empty %s docstring*' in output) def test_lint_ext_module_with_file_output(self): + self.linter.set_reporter(text.TextReporter()) if sys.version_info < (3, 0): strio = 'StringIO' else: @@ -264,6 +267,7 @@ class PyLinterTC(TestCase): self.assertEqual(self.linter.report_is_enabled('RP0001'), True) def test_report_output_format_aliased(self): + text.register(self.linter) self.linter.set_option('output-format', 'text') self.assertEqual(self.linter.reporter.__class__.__name__, 'TextReporter') @@ -21,11 +21,12 @@ import re import sys import tokenize from warnings import warn +import os from os.path import dirname, basename, splitext, exists, isdir, join, normpath from logilab.common.interface import implements from logilab.common.modutils import modpath_from_file, get_module_files, \ - file_from_modpath + file_from_modpath, load_module_from_file from logilab.common.textutils import normalize_text from logilab.common.configuration import rest_format_section from logilab.common.ureports import Section @@ -42,6 +43,7 @@ class EmptyReport(Exception): """raised when a report is empty and so should not be displayed""" + MSG_TYPES = { 'I' : 'info', 'C' : 'convention', @@ -653,3 +655,30 @@ class PyLintASTWalker(object): self.walk(child) for cb in self.leave_events.get(cid, ()): cb(astroid) + + +PY_EXTS = ('.py', '.pyc', '.pyo', '.pyw', '.so', '.dll') + +def register_plugins(linter, directory): + """load all module and package in the given directory, looking for a + 'register' function in each one, used to register pylint checkers + """ + imported = {} + for filename in os.listdir(directory): + basename, extension = os.path.splitext(filename) + if basename in imported or basename == '__pycache__': + continue + if extension in PY_EXTS and basename != '__init__' or ( + not extension and os.isdir(os.path.join(directory, basename))): + try: + module = load_module_from_file(os.path.join(directory, filename)) + except ValueError: + # empty module name (usually emacs auto-save files) + continue + except ImportError, exc: + import sys + print >> sys.stderr, "Problem importing module %s: %s" % (filename, exc) + else: + if hasattr(module, 'register'): + module.register(linter) + imported[basename] = 1 |