diff options
author | Sylvain Th?nault <sylvain.thenault@logilab.fr> | 2012-11-12 14:24:55 +0100 |
---|---|---|
committer | Sylvain Th?nault <sylvain.thenault@logilab.fr> | 2012-11-12 14:24:55 +0100 |
commit | 5cc780f3c63f9ccb1032549d39ac96e15bc2aa93 (patch) | |
tree | a1ed3d463bf0a64595cd8c1c80278b579600dcf0 /lint.py | |
parent | 6441eac02dc68b847a5b0af2c7e3626cbcffaa27 (diff) | |
parent | 6214805cb19dae4fee64fcda1435ac5a441fc24f (diff) | |
download | pylint-5cc780f3c63f9ccb1032549d39ac96e15bc2aa93.tar.gz |
backport stable
Diffstat (limited to 'lint.py')
-rw-r--r-- | lint.py | 80 |
1 files changed, 70 insertions, 10 deletions
@@ -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) |