summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorsten Marek <tmarek@google.com>2013-06-19 16:29:18 +0200
committerTorsten Marek <tmarek@google.com>2013-06-19 16:29:18 +0200
commitf6bddc94f23ef417ae1577b6416c412a82aff9c7 (patch)
tree14a4b526b3ac568efe4f18ac058ba1e4a7f827fa
parentc5e3e1ff04b2d8f3d2525d24b426ef1c7e73a21a (diff)
downloadpylint-f6bddc94f23ef417ae1577b6416c412a82aff9c7.tar.gz
Turn reporters into proper plugins rather than classes that are loaded explicitly.
-rw-r--r--checkers/__init__.py35
-rw-r--r--lint.py59
-rw-r--r--reporters/__init__.py5
-rw-r--r--reporters/html.py5
-rw-r--r--reporters/text.py17
-rw-r--r--test/unittest_lint.py4
-rw-r--r--utils.py31
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')
diff --git a/lint.py b/lint.py
index 19ca37c..52ec61c 100644
--- a/lint.py
+++ b/lint.py
@@ -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')
diff --git a/utils.py b/utils.py
index 2dac831..a13faff 100644
--- a/utils.py
+++ b/utils.py
@@ -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