summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSylvain Th?nault <sylvain.thenault@logilab.fr>2010-04-19 11:22:40 +0200
committerSylvain Th?nault <sylvain.thenault@logilab.fr>2010-04-19 11:22:40 +0200
commit62adf7f849a701687d0e3fdddebf596b5994a801 (patch)
tree167b93ca6e99f793eb14d3a73dadaf7c33f8e130
parentcefa5131bc24376108431ff0db404b30f94637bf (diff)
downloadpylint-62adf7f849a701687d0e3fdddebf596b5994a801.tar.gz
cleanup, d-t-w, fix remaining [en|dis] method call
-rw-r--r--checkers/__init__.py13
-rw-r--r--checkers/raw_metrics.py25
-rw-r--r--checkers/similar.py46
-rw-r--r--lint.py157
-rw-r--r--setup.py14
-rw-r--r--test/regrtest_data/package/__init__.py2
-rw-r--r--test/smoketest.py28
-rw-r--r--test/test_func.py26
-rw-r--r--test/test_import_graph.py6
-rw-r--r--test/test_regr.py8
-rw-r--r--test/unittest_lint.py70
-rw-r--r--utils.py116
12 files changed, 257 insertions, 254 deletions
diff --git a/checkers/__init__.py b/checkers/__init__.py
index 144b680..55efce3 100644
--- a/checkers/__init__.py
+++ b/checkers/__init__.py
@@ -64,11 +64,18 @@ def table_lines_from_stats(stats, old_stats, columns):
class BaseChecker(OptionsProviderMixIn, ASTWalker):
"""base class for checkers"""
-
- options = ()
- needs_checkers = ()
+ # checker name (you may reuse an existing one)
name = None
+ # options level (0 will be displaying in --help, 1 in --long-help)
level = 1
+ # ordered list of options to control the ckecker behaviour
+ options = ()
+ # checker that should be run before this one
+ needs_checkers = ()
+ # messages issued by this checker
+ msgs = {}
+ # reports issued by this checker
+ reports = ()
def __init__(self, linter=None):
"""checker instances should have the linter as argument
diff --git a/checkers/raw_metrics.py b/checkers/raw_metrics.py
index 4771207..872ca7b 100644
--- a/checkers/raw_metrics.py
+++ b/checkers/raw_metrics.py
@@ -10,7 +10,7 @@
# 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.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-""" Copyright (c) 2003-2005 LOGILAB S.A. (Paris, FRANCE).
+""" Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
http://www.logilab.fr/ -- mailto:contact@logilab.fr
Raw metrics checker
@@ -28,7 +28,6 @@ from pylint.interfaces import IRawChecker
from pylint.checkers import BaseRawChecker, EmptyReport
from pylint.reporters import diff_string
-
def report_raw_stats(sect, stats, old_stats):
"""calculate percentage of code / doc / comment / empty
"""
@@ -50,7 +49,7 @@ def report_raw_stats(sect, stats, old_stats):
str(old), diff_str)
sect.append(Table(children=lines, cols=5, rheaders=1))
-
+
class RawMetricsChecker(BaseRawChecker):
"""does not check anything but gives some raw metrics :
* total number of lines
@@ -59,7 +58,7 @@ class RawMetricsChecker(BaseRawChecker):
* total number of comments lines
* total number of empty lines
"""
-
+
__implements__ = (IRawChecker,)
# configuration section name
@@ -69,18 +68,18 @@ class RawMetricsChecker(BaseRawChecker):
# messages
msgs = {}
# reports
- reports = ( ('R0701', 'Raw metrics', report_raw_stats), )
-
+ reports = ( ('RP0701', 'Raw metrics', report_raw_stats), )
+
def __init__(self, linter):
BaseRawChecker.__init__(self, linter)
self.stats = None
-
+
def open(self):
"""init statistics"""
self.stats = self.linter.add_stats(total_lines=0, code_lines=0,
empty_lines=0, docstring_lines=0,
comment_lines=0)
-
+
def process_tokens(self, tokens):
"""update stats"""
i = 0
@@ -90,12 +89,11 @@ class RawMetricsChecker(BaseRawChecker):
self.stats['total_lines'] += lines_number
self.stats[line_type] += lines_number
-
+
JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER)
def get_type(tokens, start_index):
- """return the line type : docstring, comment, code, empty
- """
+ """return the line type : docstring, comment, code, empty"""
i = start_index
tok_type = tokens[i][0]
start = tokens[i][2]
@@ -114,15 +112,14 @@ def get_type(tokens, start_index):
else:
line_type = 'code_lines'
i += 1
-
if line_type is None:
line_type = 'empty_lines'
elif i < len(tokens) and tok_type == tokenize.NEWLINE:
i += 1
return i, pos[0] - start[0] + 1, line_type
-
+
def register(linter):
""" required method to auto register this checker """
linter.register_checker(RawMetricsChecker(linter))
-
+
diff --git a/checkers/similar.py b/checkers/similar.py
index 2b342a8..4660323 100644
--- a/checkers/similar.py
+++ b/checkers/similar.py
@@ -18,8 +18,6 @@
"""
from __future__ import generators
-__revision__ = '$Id: similar.py,v 1.14 2006-03-29 08:24:32 syt Exp $'
-
import sys
from logilab.common.compat import set, izip, sum, enumerate
@@ -31,7 +29,7 @@ from pylint.checkers import BaseChecker, table_lines_from_stats
class Similar:
"""finds copy-pasted lines of code in a project"""
-
+
def __init__(self, min_lines=4, ignore_comments=False,
ignore_docstrings=False):
self.min_lines = min_lines
@@ -45,11 +43,11 @@ class Similar:
stream.readlines(),
self.ignore_comments,
self.ignore_docstrings))
-
+
def run(self):
"""start looking for similarities and display results on stdout"""
self._display_sims(self._compute_sims())
-
+
def _compute_sims(self):
"""compute similarities in appended files"""
no_duplicates = {}
@@ -69,12 +67,12 @@ class Similar:
sims.sort()
sims.reverse()
return sims
-
+
def _display_sims(self, sims):
"""display computed similarities on stdout"""
nb_lignes_dupliquees = 0
for num, couples in sims:
- print
+ print
print num, "similar lines in", len(couples), "files"
couples = list(couples)
couples.sort()
@@ -117,7 +115,7 @@ class Similar:
yield num, lineset1, index1, lineset2, index2
skip = max(skip, num)
index1 += skip
-
+
def _iter_sims(self):
"""iterate on similarities among all files, by making a cartesian
product
@@ -156,7 +154,7 @@ class LineSet:
self._stripped_lines = stripped_lines(lines, ignore_comments,
ignore_docstrings)
self._index = self._mk_index()
-
+
def __str__(self):
return '<Lineset for %s>' % self.name
@@ -168,10 +166,10 @@ class LineSet:
def __cmp__(self, other):
return cmp(self.name, other.name)
-
+
def __hash__(self):
return id(self)
-
+
def enumerate_stripped(self, start_at=0):
"""return an iterator on stripped lines, starting from a given index
if specified, else 0
@@ -189,7 +187,7 @@ class LineSet:
def find(self, stripped_line):
"""return positions of the given stripped line in this set"""
return self._index.get(stripped_line, ())
-
+
def _mk_index(self):
"""create the index for this set"""
index = {}
@@ -219,7 +217,7 @@ class SimilarChecker(BaseChecker, Similar):
memory / CPU intensive, so you should disable it if you experiments some
problems.
"""
-
+
__implements__ = (IRawChecker,)
# configuration section name
name = 'similarities'
@@ -241,37 +239,37 @@ class SimilarChecker(BaseChecker, Similar):
)
# reports
reports = ( ('R0801', 'Duplication', report_similarities), )
-
+
def __init__(self, linter=None):
BaseChecker.__init__(self, linter)
Similar.__init__(self, min_lines=4,
ignore_comments=True, ignore_docstrings=True)
self.stats = None
- def set_option(self, opt_name, value, action=None, opt_dict=None):
+ def set_option(self, optname, value, action=None, optdict=None):
"""method called to set an option (registered in the options list)
overridden to report options setting to Similar
"""
- BaseChecker.set_option(self, opt_name, value, action, opt_dict)
- if opt_name == 'min-similarity-lines':
+ BaseChecker.set_option(self, optname, value, action, optdict)
+ if optname == 'min-similarity-lines':
self.min_lines = self.config.min_similarity_lines
- elif opt_name == 'ignore-comments':
+ elif optname == 'ignore-comments':
self.ignore_comments = self.config.ignore_comments
- elif opt_name == 'ignore-docstrings':
+ elif optname == 'ignore-docstrings':
self.ignore_docstrings = self.config.ignore_docstrings
-
+
def open(self):
"""init the checkers: reset linesets and statistics information"""
self.linesets = []
self.stats = self.linter.add_stats(nb_duplicated_lines=0,
percent_duplicated_lines=0)
-
+
def process_module(self, stream):
"""process a module
-
+
the module's content is accessible via the stream object
-
+
stream must implements the readlines method
"""
self.append_stream(self.linter.current_name, stream)
@@ -306,7 +304,7 @@ def usage(status=0):
print 'Usage: similar [-d|--duplicates min_duplicated_lines] \
[--ignore-comments] file1...'
sys.exit(status)
-
+
def run(argv=None):
"""standalone command line access point"""
if argv is None:
diff --git a/lint.py b/lint.py
index f8df661..d5a3cd4 100644
--- a/lint.py
+++ b/lint.py
@@ -34,6 +34,7 @@ import sys
import os
import re
import tokenize
+from warnings import warn
from logilab.common.configuration import UnsupportedAction, OptionsManagerMixIn
from logilab.common.optik_ext import check_csv
@@ -42,13 +43,14 @@ from logilab.common.interface import implements
from logilab.common.textutils import splitstrip
from logilab.common.fileutils import norm_open
from logilab.common.ureports import Table, Text, Section
+from logilab.common.graph import ordered_nodes
from logilab.common.__pkginfo__ import version as common_version
from logilab.astng import MANAGER, nodes
from logilab.astng.__pkginfo__ import version as astng_version
-from pylint.utils import UnknownMessage, MessagesHandlerMixIn, \
- ReportsHandlerMixIn, MSG_TYPES, sort_checkers, expand_modules
+from pylint.utils import PyLintASTWalker, UnknownMessage, MessagesHandlerMixIn,\
+ ReportsHandlerMixIn, MSG_TYPES, expand_modules
from pylint.interfaces import ILinter, IRawChecker, IASTNGChecker
from pylint.checkers import BaseRawChecker, EmptyReport, \
table_lines_from_stats
@@ -67,6 +69,7 @@ REPORTER_OPT_MAP = {'text': TextReporter,
'colorized': ColorizedTextReporter,
'html': HTMLReporter,}
+
# Python Linter class #########################################################
MSGS = {
@@ -112,61 +115,6 @@ MSGS = {
}
-class PyLintASTWalker(object):
- def __init__(self):
- # callbacks per node types
- self.visit_events = {}
- self.leave_events = {}
-
- def add_checker(self, checker):
- hasvdefault = False
- vcids = set()
- hasldefault = False
- lcids = set()
- for member in dir(checker):
- if member.startswith('visit_'):
- cid = member[6:]
- if cid != 'default':
- cbs = self.visit_events.setdefault(cid, [])
- cbs.append(getattr(checker, cid))
- vcids.add(cid)
- else:
- hasvdefault = getattr(checker, cid)
- if member.startswith('leave_'):
- if cid != 'default':
- cbs = self.leave_events.setdefault(cid, [])
- cbs.append(getattr(checker, cid))
- lcids.add(cid)
- else:
- hasldefault = getattr(checker, cid)
- if hasvdefault:
- for cls in nodes.ALL_NODE_CLASSES:
- cid = cls.__name__.lower()
- if cid not in vcids:
- cbs = self.visit_events.setdefault(cid, [])
- cbs.append(getattr(checker, hasvdefault))
- if hasldefault:
- for cls in nodes.ALL_NODE_CLASSES:
- cid = cls.__name__.lower()
- if cid not in lcids:
- cbs = self.leave_events.setdefault(cid, [])
- cbs.append(getattr(checker, hasldefault))
-
- def walk(self, astng):
- """call visit events of astng checkers for the given node, recurse on
- its children, then leave events.
- """
- cid = node.__class__.__name__.lower()
- # generate events for this node on each checker
- for cb in self.visit_events.get(cid, ()):
- cb(astng)
- # recurse on children
- for child in astng.get_children():
- self.walk(child)
- for cb in self.leave_events.get(cid, ()):
- cb(astng)
-
-
class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
BaseRawChecker):
"""lint Python modules using external checkers.
@@ -178,7 +126,7 @@ class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn,
* handle some basic but necessary stats'data (number of classes, methods...)
"""
- __implements__ = (ILinter, IRawChecker, IASTNGChecker)
+ __implements__ = (ILinter, IRawChecker)
name = 'master'
priority = 0
@@ -199,6 +147,7 @@ should be a base name, not a path. You may set this option multiple times.'}),
('load-plugins',
{'type' : 'csv', 'metavar' : '<modules>', 'default' : (),
+ 'level': 1,
'help' : 'List of plugins (as comma separated values of \
python modules names) to load, usually to register additional checkers.'}),
@@ -243,7 +192,7 @@ warning, statement which respectively contain the number of errors / warnings\
('comment',
{'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
- 'group': 'Reports',
+ 'group': 'Reports', 'level': 1,
'help' : 'Add a comment according to your evaluation note. \
This is used by the global evaluation report (R0004).'}),
@@ -338,26 +287,31 @@ This is used by the global evaluation report (R0004).'}),
self.reporter = reporter
reporter.linter = self
- def set_option(self, opt_name, value, action=None, opt_dict=None):
+ def set_option(self, optname, value, action=None, optdict=None):
"""overridden from configuration.OptionsProviderMixin to handle some
special options
"""
- if opt_name in self._options_methods:
+ if optname in self._options_methods or optname in self._bw_options_methods:
if value:
- meth = self._options_methods[opt_name]
- value = check_csv(None, opt_name, value)
+ try:
+ meth = self._options_methods[optname]
+ except KeyError:
+ meth = self._bw_options_methods[optname]
+ warn('%s is deprecated, replace it by %s' % (
+ optname, optname.split('-')[0]), DeprecationWarning)
+ value = check_csv(None, optname, value)
if isinstance(value, (list, tuple)):
for _id in value :
meth(_id)
else :
meth(value)
- elif opt_name == 'output-format':
+ elif optname == 'output-format':
self.set_reporter(REPORTER_OPT_MAP[value.lower()]())
try:
- BaseRawChecker.set_option(self, opt_name, value, action, opt_dict)
+ BaseRawChecker.set_option(self, optname, value, action, optdict)
except UnsupportedAction:
print >> sys.stderr, 'option %s can\'t be read from config file' % \
- opt_name
+ optname
# checkers manipulation methods ############################################
@@ -380,10 +334,10 @@ This is used by the global evaluation report (R0004).'}),
for msgcat, msgids in self._msgs_by_category.iteritems():
if msgcat == 'E':
for msgid in msgids:
- self.enable_message(msgid)
+ self.enable(msgid)
else:
for msgid in msgids:
- self.disable_message(msgid)
+ self.disable(msgid)
# block level option handling #############################################
#
@@ -395,12 +349,9 @@ This is used by the global evaluation report (R0004).'}),
"""
comment = tokenize.COMMENT
newline = tokenize.NEWLINE
- #line_num = 0
for (tok_type, _, start, _, line) in tokens:
if tok_type not in (comment, newline):
continue
- #if start[0] == line_num:
- # continue
match = OPTION_RGX.search(line)
if match is None:
continue
@@ -415,9 +366,14 @@ This is used by the global evaluation report (R0004).'}),
line=start[0])
continue
opt = opt.strip()
- #line_num = start[0]
- if opt in self._options_methods and not opt.endswith('-report'):
- meth = self._options_methods[opt]
+ if opt in self._options_methods or opt in self._bw_options_methods:
+ try:
+ meth = self._options_methods[opt]
+ except KeyError:
+ meth = self._bw_options_methods[opt]
+ warn('%s is deprecated, replace it by %s (%s)' % (
+ opt, opt.split('-')[0], self.current_file),
+ DeprecationWarning)
for msgid in splitstrip(value):
try:
meth(msgid, 'module', start[0])
@@ -433,7 +389,7 @@ This is used by the global evaluation report (R0004).'}),
self.collect_block_lines(child, msg_state)
first = node.fromlineno
last = node.tolineno
- # first child line number used to distinguish between disable-msg
+ # first child line number used to distinguish between disable
# which are the first child of scoped node with those defined later.
# For instance in the code below:
#
@@ -473,6 +429,36 @@ This is used by the global evaluation report (R0004).'}),
# code checking methods ###################################################
+ def sort_checkers(self, checkers=None):
+ if checkers is None:
+ checkers = [checker for checkers in self._checkers.values()
+ for checker in checkers]
+ graph = {}
+ cls_instance = {}
+ for checker in checkers:
+ graph[checker.__class__] = set(checker.needs_checkers)
+ cls_instance[checker.__class__] = checker
+ checkers = [cls_instance.get(cls) for cls in ordered_nodes(graph)]
+ checkers.remove(self)
+ checkers.insert(0, self)
+ return checkers
+
+ def _get_checkers(self):
+ # compute checkers needed according to activated messages and reports
+ neededcheckers = set()
+ for checkers in self._checkers.values():
+ for checker in checkers:
+ for msgid in checker.msgs:
+ if self._msgs_state.get(msgid, True):
+ neededcheckers.add(checker)
+ break
+ else:
+ for reportid, _, _ in checker.reports:
+ if self.is_report_enabled(reportid):
+ neededcheckers.add(checker)
+ break
+ return self.sort_checkers(neededcheckers)
+
def check(self, files_or_modules):
"""main checking entry: check a list of files or modules from their
name.
@@ -480,17 +466,11 @@ This is used by the global evaluation report (R0004).'}),
self.reporter.include_ids = self.config.include_ids
if not isinstance(files_or_modules, (list, tuple)):
files_or_modules = (files_or_modules,)
+ checkers = self._get_checkers()
+ #print 'checkers', checkers
rawcheckers = []
walker = PyLintASTWalker()
- #
- neededcheckers = set()
- for checker in self._checkers.values():
- for msgid in checker.msgs:
- if self._msgs_state.get(msgid, True):
- neededcheckers.add(checker)
- break
# notify global begin
- checkers = sort_checkers(neededcheckers)
for checker in checkers:
checker.open()
if implements(checker, IASTNGChecker):
@@ -702,7 +682,6 @@ def report_messages_by_module_stats(sect, stats, _):
sect.append(Table(children=lines, cols=5, rheaders=1))
-
# utilities ###################################################################
# this may help to import modules using gettext
@@ -919,22 +898,22 @@ been issued by analysing pylint output status code
self.linter.generate_manpage(__pkginfo__)
sys.exit(0)
- def cb_help_message(self, option, opt_name, value, parser):
+ def cb_help_message(self, option, optname, value, parser):
"""optik callback for printing some help about a particular message"""
self.linter.help_message(splitstrip(value))
sys.exit(0)
- def cb_full_documentation(self, option, opt_name, value, parser):
+ def cb_full_documentation(self, option, optname, value, parser):
"""optik callback for printing full documentation"""
self.linter.print_full_documentation()
sys.exit(0)
- def cb_list_messages(self, option, opt_name, value, parser): # FIXME
+ def cb_list_messages(self, option, optname, value, parser): # FIXME
"""optik callback for printing available messages"""
- self.linter.list_sorted_messages()
+ self.linter.list_messages()
sys.exit(0)
-def cb_init_hook(option, opt_name, value, parser):
+def cb_init_hook(option, optname, value, parser):
"""exec arbitrary code to set sys.path for instance"""
exec value
diff --git a/setup.py b/setup.py
index 1b3bb7b..18a7abb 100644
--- a/setup.py
+++ b/setup.py
@@ -39,14 +39,6 @@ try:
except ImportError:
scripts = []
try:
- from __pkginfo__ import data_files
-except ImportError:
- data_files = None
-try:
- from __pkginfo__ import ext_modules
-except ImportError:
- ext_modules = None
-try:
from __pkginfo__ import install_requires
except ImportError:
install_requires = None
@@ -123,7 +115,7 @@ def export(from_dir, to_dir,
raise
walk(from_dir, make_mirror, None)
-
+
def install(**kwargs):
"""setup entry point"""
kwargs['package_dir'] = {modname : '.'}
@@ -144,10 +136,8 @@ def install(**kwargs):
url = web,
classifiers = classifiers,
scripts = ensure_scripts(scripts),
- data_files = data_files,
- ext_modules = ext_modules,
**kwargs
)
-
+
if __name__ == '__main__' :
install()
diff --git a/test/regrtest_data/package/__init__.py b/test/regrtest_data/package/__init__.py
index 8d82b43..7041268 100644
--- a/test/regrtest_data/package/__init__.py
+++ b/test/regrtest_data/package/__init__.py
@@ -8,7 +8,7 @@ __path__ += "folder"
class AudioTime(object):
"""test precedence over the AudioTime submodule"""
-
+
DECIMAL = 3
import subpackage
diff --git a/test/smoketest.py b/test/smoketest.py
index 0278cf9..b1c2c3a 100644
--- a/test/smoketest.py
+++ b/test/smoketest.py
@@ -39,68 +39,68 @@ class RunTC(TestCase):
finally:
sys.stderr = sys.__stderr__
sys.stdout = sys.__stdout__
-
+
@tag('smoke')
def test0(self):
"""make pylint checking itself"""
self._runtest(['pylint.__pkginfo__'], reporter=TextReporter(StringIO()),
code=0)
-
+
@tag('smoke')
def test1(self):
"""make pylint checking itself"""
self._runtest(['--include-ids=y', 'pylint.lint'], reporter=TextReporter(StringIO()))
-
+
@tag('smoke')
def test2(self):
"""make pylint checking itself"""
self._runtest(['pylint.lint'], reporter=ParseableTextReporter(StringIO()))
-
+
@tag('smoke')
def test3(self):
"""make pylint checking itself"""
self._runtest(['pylint.lint'], reporter=HTMLReporter(StringIO()))
-
+
@tag('smoke')
def test4(self):
"""make pylint checking itself"""
self._runtest(['pylint.lint'], reporter=ColorizedTextReporter(StringIO()))
-
+
@tag('smoke')
def test5(self):
"""make pylint checking itself"""
self._runtest(['pylint.lint'], reporter=VSTextReporter(StringIO()))
-
+
@tag('smoke')
def test_no_ext_file(self):
self._runtest([join(HERE, 'input', 'noext')], code=0)
-
+
@tag('smoke')
def test_w0704_ignored(self):
self._runtest([join(HERE, 'input', 'ignore_except_pass_by_default.py')], code=0)
-
+
@tag('smoke', 'help', 'config')
def test_generate_config_option(self):
"""make pylint checking itself"""
self._runtest(['--generate-rcfile'], reporter=HTMLReporter(StringIO()),
code=0)
-
+
@tag('smoke', 'help')
def test_help_message_option(self):
"""make pylint checking itself"""
self._runtest(['--help-msg', 'W0101'], reporter=HTMLReporter(StringIO()),
code=0)
-
+
@tag('smoke', 'help')
def test_error_help_message_option(self):
self._runtest(['--help-msg', 'WX101'], reporter=HTMLReporter(StringIO()),
code=0)
-
+
@tag('smoke', 'usage')
def test_error_missing_arguments(self):
self._runtest([], reporter=HTMLReporter(StringIO()),
code=32)
-
-
+
+
if __name__ == '__main__':
unittest_main()
diff --git a/test/test_func.py b/test/test_func.py
index 74cd3d4..2d67824 100644
--- a/test/test_func.py
+++ b/test/test_func.py
@@ -56,7 +56,7 @@ def exception_str(ex):
"""function used to replace default __str__ method of exception instances"""
return 'in %s\n:: %s' % (ex.file, ', '.join(ex.args))
-class LintTestUsingModule(testlib.TestCase):
+class LintTestUsingModule(testlib.TestCase):
DEFAULT_PACKAGE = 'input'
package = DEFAULT_PACKAGE
linter = linter
@@ -83,7 +83,7 @@ class LintTestUsingModule(testlib.TestCase):
tocheck += [self.package+'.%s' % name.replace('.py', '')
for name, file in self.depends]
self._test(tocheck)
-
+
def _test(self, tocheck):
if INFO_TEST_RGX.match(self.module):
self.linter.enable('I')
@@ -112,8 +112,8 @@ class LintTestUsingModule(testlib.TestCase):
#ex.__str__ = new.instancemethod(exception_str, ex, None)
raise AssertionError('%s: %s' % (self.module, ex)), None, sys.exc_info()[-1]
-class LintTestUsingFile(LintTestUsingModule):
-
+class LintTestUsingFile(LintTestUsingModule):
+
_TEST_TYPE = 'file'
def test_functionality(self):
@@ -161,7 +161,7 @@ class TestTests(testlib.TestCase):
def make_tests(filter_rgx):
"""generate tests classes from test info
-
+
return the list of generated test classes
"""
if filter_rgx:
@@ -181,7 +181,7 @@ def make_tests(filter_rgx):
continue
base = module_file.replace('func_', '').replace('.py', '')
dependencies = get_tests_info(base, '.py')
-
+
class LintTestUsingModuleTC(LintTestUsingModule):
module = module_file.replace('.py', '')
output = messages_file
@@ -191,14 +191,14 @@ def make_tests(filter_rgx):
if MODULES_ONLY:
continue
-
+
class LintTestUsingFileTC(LintTestUsingFile):
module = module_file.replace('.py', '')
output = exists(messages_file + '2') and (messages_file + '2') or messages_file
depends = dependencies or None
tags = testlib.Tags(('generated', 'pylint_input_%s' % module))
tests.append(LintTestUsingFileTC)
-
+
## # special test for f0003
## module_file, messages_file in get_tests_info('func_f0003', '.pyc')
## class LintTestSubclass(LintTest):
@@ -206,16 +206,16 @@ def make_tests(filter_rgx):
## output = messages_file
## depends = dependencies or None
## tests.append(LintTestSubclass)
-
+
class LintBuiltinModuleTest(LintTestUsingModule):
output = 'messages/builtin_module.txt'
module = 'sys'
def test_functionality(self):
self._test(['sys'])
tests.append(LintBuiltinModuleTest)
-
+
if not filter_rgx:
- # test all features are tested :)
+ # test all features are tested :)
tests.append(TestTests)
return tests
@@ -231,8 +231,8 @@ if __name__=='__main__':
if '-m' in sys.argv:
MODULES_ONLY = True
sys.argv.remove('-m')
-
- if len(sys.argv) > 1:
+
+ if len(sys.argv) > 1:
FILTER_RGX = sys.argv[1]
del sys.argv[1]
testlib.unittest_main(defaultTest='suite')
diff --git a/test/test_import_graph.py b/test/test_import_graph.py
index 3347515..0c3dec6 100644
--- a/test/test_import_graph.py
+++ b/test/test_import_graph.py
@@ -15,7 +15,7 @@ class DependenciesGraphTC(unittest.TestCase):
dest = 'dependencies_graph.dot'
def tearDown(self):
os.remove(self.dest)
-
+
def test_dependencies_graph(self):
imports.dependencies_graph(self.dest, {'labas': ['hoho', 'yep'],
'hoho': ['yep']})
@@ -33,12 +33,12 @@ URL="." node[shape="box"]
"yep" -> "labas" [];
}
'''.strip())
-
+
class ImportCheckerTC(unittest.TestCase):
def setUp(self):
self.linter = l = PyLinter(reporter=TestReporter())
initialize(l)
-
+
def test_checker_dep_graphs(self):
l = self.linter
l.global_set_option('persistent', False)
diff --git a/test/test_regr.py b/test/test_regr.py
index dec9908..de5ccb6 100644
--- a/test/test_regr.py
+++ b/test/test_regr.py
@@ -43,12 +43,12 @@ class NonRegrTC(TestCase):
pending messages if a test finished badly
"""
linter.reporter.finalize()
-
+
def test_package___path___manipulation(self):
linter.check('package.__init__')
got = linter.reporter.finalize().strip()
self.failUnlessEqual(got, '')
-
+
def test_package___init___precedence(self):
linter.check('precedence_test')
got = linter.reporter.finalize().strip()
@@ -134,7 +134,7 @@ class NonRegrTC(TestCase):
if fname.endswith('_crash.py'):
linter.check(join('regrtest_data', fname))
linter.reporter.finalize().strip()
-
+
def test_try_finally_disable_msg_crash(self):
linter.check(join('regrtest_data', 'try_finally_disable_msg_crash'))
@@ -144,6 +144,6 @@ class NonRegrTC(TestCase):
messages = linter.reporter.finalize().strip()
self.failIf('__path__' in messages, messages)
-
+
if __name__ == '__main__':
unittest_main()
diff --git a/test/unittest_lint.py b/test/unittest_lint.py
index 1820a7c..2acd8f4 100644
--- a/test/unittest_lint.py
+++ b/test/unittest_lint.py
@@ -22,8 +22,9 @@ from cStringIO import StringIO
from logilab.common.testlib import TestCase, unittest_main, create_files
from logilab.common.compat import sorted
-from pylint.config import get_note_message
-from pylint.lint import PyLinter, Run, sort_checkers, UnknownMessage
+
+from pylint import config
+from pylint.lint import PyLinter, Run, UnknownMessage
from pylint.utils import sort_msgs
from pylint import checkers
@@ -48,11 +49,11 @@ class GetNoteMessageTC(TestCase):
def test(self):
msg = None
for note in range(-1, 11):
- note_msg = get_note_message(note)
+ note_msg = config.get_note_message(note)
self.assertNotEquals(msg, note_msg)
msg = note_msg
if optimized:
- self.assertRaises(AssertionError, get_note_message, 11)
+ self.assertRaises(AssertionError, config.get_note_message, 11)
HERE = abspath(dirname(__file__))
@@ -80,7 +81,7 @@ class PyLinterTC(TestCase):
def setUp(self):
self.linter = PyLinter()
- self.linter.disable_message_category('I')
+ self.linter.disable('I')
self.linter.config.persistent = 0
# register checkers
checkers.initialize(self.linter)
@@ -97,15 +98,15 @@ class PyLinterTC(TestCase):
linter.set_current_module('toto')
self.assert_(linter.is_message_enabled('W0101'))
self.assert_(linter.is_message_enabled('W0102'))
- linter.disable_message('W0101', scope='package')
- linter.disable_message('W0102', scope='module', line=1)
+ linter.disable('W0101', scope='package')
+ linter.disable('W0102', scope='module', line=1)
self.assert_(not linter.is_message_enabled('W0101'))
self.assert_(not linter.is_message_enabled('W0102', 1))
linter.set_current_module('tutu')
self.assert_(not linter.is_message_enabled('W0101'))
self.assert_(linter.is_message_enabled('W0102'))
- linter.enable_message('W0101', scope='package')
- linter.enable_message('W0102', scope='module', line=1)
+ linter.enable('W0101', scope='package')
+ linter.enable('W0102', scope='module', line=1)
self.assert_(linter.is_message_enabled('W0101'))
self.assert_(linter.is_message_enabled('W0102', 1))
@@ -114,18 +115,20 @@ class PyLinterTC(TestCase):
linter.open()
linter.set_current_module('toto')
self.assert_(linter.is_message_enabled('W0101'))
- self.assert_(linter.is_message_enabled('R0102'))
- linter.disable_message_category('W', scope='package')
- linter.disable_message_category('R', scope='module')
+ self.assert_(linter.is_message_enabled('C0121'))
+ linter.disable('W', scope='package')
+ linter.disable('C', scope='module', line=1)
self.assert_(not linter.is_message_enabled('W0101'))
- self.assert_(not linter.is_message_enabled('R0102'))
+ self.assert_(linter.is_message_enabled('C0121'))
+ self.assert_(not linter.is_message_enabled('C0121', line=1))
linter.set_current_module('tutu')
self.assert_(not linter.is_message_enabled('W0101'))
- self.assert_(linter.is_message_enabled('R0102'))
- linter.enable_message_category('W', scope='package')
- linter.enable_message_category('R', scope='module')
+ self.assert_(linter.is_message_enabled('C0121'))
+ linter.enable('W', scope='package')
+ linter.enable('C', scope='module', line=1)
self.assert_(linter.is_message_enabled('W0101'))
- self.assert_(linter.is_message_enabled('R0102'))
+ self.assert_(linter.is_message_enabled('C0121'))
+ self.assert_(linter.is_message_enabled('C0121', line=1))
def test_enable_message_block(self):
linter = self.linter
@@ -194,41 +197,32 @@ class PyLinterTC(TestCase):
pass
def test_enable_report(self):
- self.assertEquals(self.linter.is_report_enabled('R0001'), True)
- self.linter.disable_report('R0001')
- self.assertEquals(self.linter.is_report_enabled('R0001'), False)
- self.linter.enable_report('R0001')
- self.assertEquals(self.linter.is_report_enabled('R0001'), True)
+ self.assertEquals(self.linter.is_report_enabled('RP0001'), True)
+ self.linter.disable('RP0001')
+ self.assertEquals(self.linter.is_report_enabled('RP0001'), False)
+ self.linter.enable('RP0001')
+ self.assertEquals(self.linter.is_report_enabled('RP0001'), True)
def test_set_option_1(self):
linter = self.linter
- linter.set_option('disable-msg', 'C0111,W0142')
+ linter.set_option('disable', 'C0111,W0142')
self.assert_(not linter.is_message_enabled('C0111'))
self.assert_(not linter.is_message_enabled('W0142'))
self.assert_(linter.is_message_enabled('W0113'))
def test_set_option_2(self):
linter = self.linter
- linter.set_option('disable-msg', ('C0111', 'W0142') )
+ linter.set_option('disable', ('C0111', 'W0142') )
self.assert_(not linter.is_message_enabled('C0111'))
self.assert_(not linter.is_message_enabled('W0142'))
self.assert_(linter.is_message_enabled('W0113'))
- def test_enable_checkers1(self):
- self.linter.enable_checkers(['design'], False)
- self.assertEquals(sorted([c.name for c in self.linter._checkers.values()
- if c.is_enabled()]),
- ['basic', 'classes', 'exceptions', 'format', 'imports',
- 'logging', 'master', 'metrics', 'miscellaneous', 'newstyle',
- 'similarities', 'string_format', 'typecheck', 'variables'])
+ def test_enable_checkers(self):
+ self.linter.disable('design')
+ self.failIf('design' in set([c.name for c in self.linter._get_checkers()]))
+ self.linter.enable('design')
+ self.failUnless('design' in set([c.name for c in self.linter._get_checkers()]))
- def test_enable_checkers2(self):
- self.linter.enable_checkers(['design'], True)
- self.assertEquals(sorted([c.name for c in self.linter._checkers.values()
- if c.is_enabled()]),
- ['design', 'master'])
-
-from pylint import config
class ConfigTC(TestCase):
diff --git a/utils.py b/utils.py
index cc7836c..5241d4a 100644
--- a/utils.py
+++ b/utils.py
@@ -20,13 +20,15 @@ main pylint class
import sys
from os import linesep
+from os.path import dirname, basename, splitext, exists, isdir, join, normpath
+from logilab.common.modutils import modpath_from_file, get_module_files, \
+ file_from_modpath
from logilab.common.textutils import normalize_text
from logilab.common.configuration import rest_format_section
from logilab.common.ureports import Section
-from logilab.common.graph import ordered_nodes
-from logilab.astng import Module
+from logilab.astng import nodes, Module
from pylint.checkers import EmptyReport
@@ -53,14 +55,6 @@ MSG_TYPES_STATUS = {
'F' : 1
}
-def sort_checkers(checkers):
- graph = {}
- cls_instance = {}
- for checker in checkers:
- graph[cube.__class__] = set(checker.needs_checkers)
- cls_instance[cube.__class__] = checker
- return [cls_instance.get(cls) for cls in ordered_nodes(graph)]
-
def sort_msgs(msgids):
"""sort message identifiers according to their category first"""
msg_order = 'EWRCIF'
@@ -331,15 +325,8 @@ class MessagesHandlerMixIn:
def list_messages(self):
"""output full messages list documentation in ReST format"""
- for checker in sort_checkers(self._checkers.values()):
- if checker.msgs:
- self.list_checkers_messages( checker)
- print
-
- def list_sorted_messages(self):
- """output full sorted messages list in ReST format"""
msgids = []
- for checker in self._checkers.values():
+ for checker in self.sort_checkers():
for msgid in checker.msgs.keys():
msgids.append(msgid)
msgids.sort()
@@ -356,32 +343,32 @@ class ReportsHandlerMixIn:
self._reports = {}
self._reports_state = {}
- def register_report(self, r_id, r_title, r_cb, checker):
+ def register_report(self, reportid, r_title, r_cb, checker):
"""register a report
- r_id is the unique identifier for the report
+ reportid is the unique identifier for the report
r_title the report's title
r_cb the method to call to make the report
checker is the checker defining the report
"""
- r_id = r_id.upper()
- self._reports.setdefault(checker, []).append( (r_id, r_title, r_cb) )
+ reportid = reportid.upper()
+ self._reports.setdefault(checker, []).append( (reportid, r_title, r_cb) )
- def enable_report(self, r_id):
+ def enable_report(self, reportid):
"""disable the report of the given id"""
- r_id = r_id.upper()
- self._reports_state[r_id] = True
+ reportid = reportid.upper()
+ self._reports_state[reportid] = True
- def disable_report(self, r_id):
+ def disable_report(self, reportid):
"""disable the report of the given id"""
- r_id = r_id.upper()
- self._reports_state[r_id] = False
+ reportid = reportid.upper()
+ self._reports_state[reportid] = False
- def is_report_enabled(self, r_id):
+ def is_report_enabled(self, reportid):
"""return true if the report associated to the given identifier is
enabled
"""
- return self._reports_state.get(r_id, True)
+ return self._reports_state.get(reportid, True)
def make_reports(self, stats, old_stats):
"""render registered reports"""
@@ -390,18 +377,18 @@ class ReportsHandlerMixIn:
self.reporter.set_output(open(filename, 'w'))
sect = Section('Report',
'%s statements analysed.'% (self.stats['statement']))
- checkers = sort_checkers(self._reports.keys())
+ checkers = self.sort_checkers(self._reports)
checkers.reverse()
for checker in checkers:
- for r_id, r_title, r_cb in self._reports[checker]:
- if not self.is_report_enabled(r_id):
+ for reportid, r_title, r_cb in self._reports[checker]:
+ if not self.is_report_enabled(reportid):
continue
report_sect = Section(r_title)
try:
r_cb(report_sect, stats, old_stats)
except EmptyReport:
continue
- report_sect.report_id = r_id
+ report_sect.report_id = reportid
sect.append(report_sect)
self.reporter.display_results(sect)
@@ -416,11 +403,6 @@ class ReportsHandlerMixIn:
self.stats[key] = value
return self.stats
-### ### - - - - import utils - - - - ### ###
-
-from os.path import dirname, basename, splitext, exists, isdir, join, normpath
-from logilab.common.modutils import modpath_from_file, get_module_files, \
- file_from_modpath
def expand_modules(files_or_modules, black_list):
"""take a list of files/modules/packages and return the list of tuple
@@ -462,3 +444,59 @@ def expand_modules(files_or_modules, black_list):
result.append( {'path': subfilepath, 'name': submodname,
'basepath': filepath, 'basename': modname} )
return result, errors
+
+
+class PyLintASTWalker(object):
+ def __init__(self):
+ # callbacks per node types
+ self.visit_events = {}
+ self.leave_events = {}
+
+ def add_checker(self, checker):
+ hasvdefault = False
+ vcids = set()
+ hasldefault = False
+ lcids = set()
+ for member in dir(checker):
+ if member.startswith('visit_'):
+ cid = member[6:]
+ if cid != 'default':
+ cbs = self.visit_events.setdefault(cid, [])
+ cbs.append(getattr(checker, member))
+ vcids.add(cid)
+ else:
+ hasvdefault = getattr(checker, member)
+ elif member.startswith('leave_'):
+ cid = member[6:]
+ if cid != 'default':
+ cbs = self.leave_events.setdefault(cid, [])
+ cbs.append(getattr(checker, member))
+ lcids.add(cid)
+ else:
+ hasldefault = getattr(checker, member)
+ if hasvdefault:
+ for cls in nodes.ALL_NODE_CLASSES:
+ cid = cls.__name__.lower()
+ if cid not in vcids:
+ cbs = self.visit_events.setdefault(cid, [])
+ cbs.append(hasvdefault)
+ if hasldefault:
+ for cls in nodes.ALL_NODE_CLASSES:
+ cid = cls.__name__.lower()
+ if cid not in lcids:
+ cbs = self.leave_events.setdefault(cid, [])
+ cbs.append(hasldefault)
+
+ def walk(self, astng):
+ """call visit events of astng checkers for the given node, recurse on
+ its children, then leave events.
+ """
+ cid = astng.__class__.__name__.lower()
+ # generate events for this node on each checker
+ for cb in self.visit_events.get(cid, ()):
+ cb(astng)
+ # recurse on children
+ for child in astng.get_children():
+ self.walk(child)
+ for cb in self.leave_events.get(cid, ()):
+ cb(astng)