summaryrefslogtreecommitdiff
path: root/lint.py
diff options
context:
space:
mode:
authorSylvain Th?nault <sylvain.thenault@logilab.fr>2012-11-12 14:24:55 +0100
committerSylvain Th?nault <sylvain.thenault@logilab.fr>2012-11-12 14:24:55 +0100
commit5cc780f3c63f9ccb1032549d39ac96e15bc2aa93 (patch)
treea1ed3d463bf0a64595cd8c1c80278b579600dcf0 /lint.py
parent6441eac02dc68b847a5b0af2c7e3626cbcffaa27 (diff)
parent6214805cb19dae4fee64fcda1435ac5a441fc24f (diff)
downloadpylint-5cc780f3c63f9ccb1032549d39ac96e15bc2aa93.tar.gz
backport stable
Diffstat (limited to 'lint.py')
-rw-r--r--lint.py80
1 files changed, 70 insertions, 10 deletions
diff --git a/lint.py b/lint.py
index 3a39e0f..21c5aa7 100644
--- a/lint.py
+++ b/lint.py
@@ -60,7 +60,7 @@ from pylint import config
from pylint.__pkginfo__ import version
-OPTION_RGX = re.compile(r'\s*#*\s*pylint:(.*)')
+OPTION_RGX = re.compile(r'\s*#.*\bpylint:(.*)')
REPORTER_OPT_MAP = {'text': TextReporter,
'parseable': ParseableTextReporter,
'msvs': VSTextReporter,
@@ -126,6 +126,21 @@ MSGS = {
'I0013': ('Ignoring entire file',
'file-ignored',
'Used to inform that the file will not be checked'),
+ 'I0014': ('Used deprecated directive "pylint:disable-all" or "pylint:disable=all"',
+ 'deprecated-disable-all',
+ 'You should preferably use "pylint:skip-file" as this directive '
+ 'has a less confusing name. Do this only if you are sure that all '
+ 'people running Pylint on your code have version >= 0.26'),
+ 'I0020': ('Suppressed %s (from line %d)',
+ 'suppressed-message',
+ 'A message was triggered on a line, but suppressed explicitly '
+ 'by a disable= comment in the file. This message is not '
+ 'generated for messages that are ignored due to configuration '
+ 'settings.'),
+ 'I0021': ('Useless suppression of %s',
+ 'useless-suppression',
+ 'Reported when a message is explicitly disabled for a line or '
+ 'a block of code, but never triggered.'),
'E0001': ('%s',
@@ -240,7 +255,8 @@ This is used by the global evaluation report (RP0004).'}),
'group': 'Messages control',
'help' : 'Enable the message, report, category or checker with the '
'given id(s). You can either give multiple identifier '
- 'separated by comma (,) or put this option multiple time.'}),
+ 'separated by comma (,) or put this option multiple time. '
+ 'See also the "--disable" option for examples. '}),
('disable',
{'type' : 'csv', 'metavar': '<msg ids>',
@@ -250,7 +266,14 @@ This is used by the global evaluation report (RP0004).'}),
'with the given id(s). You can either give multiple identifiers'
' separated by comma (,) or put this option multiple times '
'(only on the command line, not in the configuration file '
- 'where it should appear only once).'}),
+ 'where it should appear only once).'
+ 'You can also use "--disable=all" to disable everything first '
+ 'and then reenable specific checks. For example, if you want '
+ 'to run only the similarities checker, you can use '
+ '"--disable=all --enable=similarities". '
+ 'If you want to run only the classes checker, but have no '
+ 'Warning level messages displayed, use'
+ '"--disable=all --enable=classes --disable=W"'}),
)
option_groups = (
@@ -307,6 +330,17 @@ This is used by the global evaluation report (RP0004).'}),
from pylint import checkers
checkers.initialize(self)
+ def prepare_import_path(self, args):
+ """Prepare sys.path for running the linter checks."""
+ if len(args) == 1:
+ sys.path.insert(0, _get_python_path(args[0]))
+ else:
+ sys.path.insert(0, os.getcwd())
+
+ def cleanup_import_path(self):
+ """Revert any changes made to sys.path in prepare_import_path."""
+ sys.path.pop(0)
+
def load_plugin_modules(self, modnames):
"""take a list of module names which are pylint plugins and load
and register them
@@ -410,7 +444,9 @@ This is used by the global evaluation report (RP0004).'}),
match = OPTION_RGX.search(line)
if match is None:
continue
- if match.group(1).strip() == "disable-all":
+ if match.group(1).strip() == "disable-all" or match.group(1).strip() == 'skip-file':
+ if match.group(1).strip() == "disable-all":
+ self.add_message('I0014', line=start[0])
self.add_message('I0013', line=start[0])
self._ignore_file = True
return
@@ -426,11 +462,16 @@ This is used by the global evaluation report (RP0004).'}),
meth = self._options_methods[opt]
except KeyError:
meth = self._bw_options_methods[opt]
- warn('%s is deprecated, replace it by %s (%s, line %s)' % (
+ warn('%s is deprecated, replace it with %s (%s, line %s)' % (
opt, opt.split('-')[0], self.current_file, line),
DeprecationWarning)
for msgid in splitstrip(value):
try:
+ if (opt, msgid) == ('disable', 'all'):
+ self.add_message('I0014', line=start[0])
+ self.add_message('I0013', line=start[0])
+ self._ignore_file = True
+ return
meth(msgid, 'module', start[0])
except UnknownMessage:
self.add_message('E0012', args=msgid, line=start[0])
@@ -465,6 +506,7 @@ This is used by the global evaluation report (RP0004).'}),
firstchildlineno = last
for msgid, lines in msg_state.iteritems():
for lineno, state in lines.items():
+ original_lineno = lineno
if first <= lineno <= last:
if lineno > firstchildlineno:
state = True
@@ -475,6 +517,9 @@ This is used by the global evaluation report (RP0004).'}),
if not line in self._module_msgs_state.get(msgid, ()):
if line in lines: # state change in the same block
state = lines[line]
+ original_lineno = line
+ if not state:
+ self._suppression_mapping[(msgid, line)] = original_lineno
try:
self._module_msgs_state[msgid][line] = state
except KeyError:
@@ -579,6 +624,8 @@ This is used by the global evaluation report (RP0004).'}),
if modname:
self._module_msgs_state = {}
self._module_msg_cats_state = {}
+ self._raw_module_msgs_state = {}
+ self._ignored_msgs = {}
def get_astng(self, filepath, modname):
"""return a astng representation for a module"""
@@ -606,8 +653,11 @@ This is used by the global evaluation report (RP0004).'}),
if self._ignore_file:
return False
# walk ast to collect line numbers
+ for msg, lines in self._module_msgs_state.iteritems():
+ self._raw_module_msgs_state[msg] = lines.copy()
orig_state = self._module_msgs_state.copy()
self._module_msgs_state = {}
+ self._suppression_mapping = {}
self.collect_block_lines(astng, orig_state)
for checker in rawcheckers:
checker.process_module(astng)
@@ -630,6 +680,7 @@ This is used by the global evaluation report (RP0004).'}),
if persistent run, pickle results for later comparison
"""
+ self._add_suppression_messages()
if self.base_name is not None:
# load previous results if any
previous_stats = config.load_results(self.base_name)
@@ -650,6 +701,16 @@ This is used by the global evaluation report (RP0004).'}),
# specific reports ########################################################
+ def _add_suppression_messages(self):
+ for warning, lines in self._raw_module_msgs_state.iteritems():
+ for line, enable in lines.iteritems():
+ if not enable and (warning, line) not in self._ignored_msgs:
+ self.add_message('I0021', line, None, (warning,))
+
+ for (warning, from_), lines in self._ignored_msgs.iteritems():
+ for line in lines:
+ self.add_message('I0020', line, None, (warning, from_))
+
def report_evaluation(self, sect, stats, previous_stats):
"""make the global evaluation report"""
# check with at least check 1 statements (usually 0 when there is a
@@ -888,6 +949,8 @@ are done by default'''}),
level=1)
# read configuration
linter.disable('W0704')
+ linter.disable('I0020')
+ linter.disable('I0021')
linter.read_config_file()
# is there some additional plugins in the file configuration, in
config_parser = linter.cfgfile_parser
@@ -913,10 +976,7 @@ are done by default'''}),
sys.exit(32)
# insert current working directory to the python path to have a correct
# behaviour
- if len(args) == 1:
- sys.path.insert(0, _get_python_path(args[0]))
- else:
- sys.path.insert(0, os.getcwd())
+ linter.prepare_import_path(args)
if self.linter.config.profile:
print >> sys.stderr, '** profiled run'
import cProfile, pstats
@@ -927,7 +987,7 @@ are done by default'''}),
data.print_stats(30)
else:
linter.check(args)
- sys.path.pop(0)
+ linter.cleanup_import_path()
if exit:
sys.exit(self.linter.msg_status)