Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(820)

Side by Side Diff: third_party/pylint/lint.py

Issue 876793002: pylint: upgrade to 1.4.1 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/pylint/interfaces.py ('k') | third_party/pylint/reporters/json.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE). 1 # Copyright (c) 2003-2014 LOGILAB S.A. (Paris, FRANCE).
2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 # 3 #
4 # This program is free software; you can redistribute it and/or modify it under 4 # This program is free software; you can redistribute it and/or modify it under
5 # the terms of the GNU General Public License as published by the Free Software 5 # the terms of the GNU General Public License as published by the Free Software
6 # Foundation; either version 2 of the License, or (at your option) any later 6 # Foundation; either version 2 of the License, or (at your option) any later
7 # version. 7 # version.
8 # 8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT 9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
12 # 12 #
13 # You should have received a copy of the GNU General Public License along with 13 # You should have received a copy of the GNU General Public License along with
14 # this program; if not, write to the Free Software Foundation, Inc., 14 # this program; if not, write to the Free Software Foundation, Inc.,
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 """ %prog [options] module_or_package 16 """ %prog [options] module_or_package
17 17
18 Check that a module satisfies a coding standard (and more !). 18 Check that a module satisfies a coding standard (and more !).
19 19
20 %prog --help 20 %prog --help
21 21
22 Display this help message and exit. 22 Display this help message and exit.
23 23
24 %prog --help-msg <msg-id>[,<msg-id>] 24 %prog --help-msg <msg-id>[,<msg-id>]
25 25
26 Display help messages about given message identifiers and exit. 26 Display help messages about given message identifiers and exit.
27 """ 27 """
28 from __future__ import print_function 28 from __future__ import print_function
29 29
30 # import this first to avoid builtin namespace pollution 30 import collections
31 from pylint.checkers import utils #pylint: disable=unused-import 31 import contextlib
32 32 import itertools
33 import sys 33 import operator
34 import os 34 import os
35 import tokenize
36 from collections import defaultdict
37 from contextlib import contextmanager
38 from operator import attrgetter
39 from warnings import warn
40 from itertools import chain
41 try: 35 try:
42 import multiprocessing 36 import multiprocessing
43 except ImportError: 37 except ImportError:
44 multiprocessing = None 38 multiprocessing = None
39 import sys
40 import tokenize
41 import warnings
45 42
43 import astroid
44 from astroid.__pkginfo__ import version as astroid_version
45 from astroid import modutils
46 from logilab.common import configuration
47 from logilab.common import optik_ext
48 from logilab.common import interface
49 from logilab.common import textutils
50 from logilab.common import ureports
51 from logilab.common.__pkginfo__ import version as common_version
46 import six 52 import six
47 from logilab.common.configuration import (
48 UnsupportedAction, OptionsManagerMixIn)
49 from logilab.common.optik_ext import check_csv
50 from logilab.common.interface import implements
51 from logilab.common.textutils import splitstrip, unquote
52 from logilab.common.ureports import Table, Text, Section
53 from logilab.common.__pkginfo__ import version as common_version
54 from astroid import MANAGER, AstroidBuildingException
55 from astroid.__pkginfo__ import version as astroid_version
56 from astroid.modutils import load_module_from_name, get_module_part
57 53
58 from pylint.utils import ( 54 from pylint import checkers
59 MSG_TYPES, OPTION_RGX, 55 from pylint import interfaces
60 PyLintASTWalker, UnknownMessage, MessagesHandlerMixIn, ReportsHandlerMixIn, 56 from pylint import reporters
61 MessagesStore, FileState, EmptyReport, 57 from pylint import utils
62 expand_modules, tokenize_module, Message)
63 from pylint.interfaces import IRawChecker, ITokenChecker, IAstroidChecker, CONFI DENCE_LEVELS
64 from pylint.checkers import (BaseTokenChecker,
65 table_lines_from_stats,
66 initialize as checkers_initialize)
67 from pylint.reporters import initialize as reporters_initialize, CollectingRepor ter
68 from pylint import config 58 from pylint import config
69 from pylint.__pkginfo__ import version 59 from pylint.__pkginfo__ import version
70 60
71 61
62 MANAGER = astroid.MANAGER
63
72 def _get_new_args(message): 64 def _get_new_args(message):
73 location = ( 65 location = (
74 message.abspath, 66 message.abspath,
75 message.path, 67 message.path,
76 message.module, 68 message.module,
77 message.obj, 69 message.obj,
78 message.line, 70 message.line,
79 message.column, 71 message.column,
80 ) 72 )
81 return ( 73 return (
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
188 def _deprecated_option(shortname, opt_type): 180 def _deprecated_option(shortname, opt_type):
189 def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argum ent 181 def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argum ent
190 sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (op tname,)) 182 sys.stderr.write('Warning: option %s is deprecated and ignored.\n' % (op tname,))
191 return {'short': shortname, 'help': 'DEPRECATED', 'hide': True, 183 return {'short': shortname, 'help': 'DEPRECATED', 'hide': True,
192 'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated } 184 'type': opt_type, 'action': 'callback', 'callback': _warn_deprecated }
193 185
194 186
195 if multiprocessing is not None: 187 if multiprocessing is not None:
196 class ChildLinter(multiprocessing.Process): # pylint: disable=no-member 188 class ChildLinter(multiprocessing.Process): # pylint: disable=no-member
197 def run(self): 189 def run(self):
198 tasks_queue, results_queue, config = self._args # pylint: disable=no -member 190 tasks_queue, results_queue, self._config = self._args # pylint: disa ble=no-member
199 191
192 self._config["jobs"] = 1 # Child does not parallelize any further.
193
194 # Run linter for received files/modules.
200 for file_or_module in iter(tasks_queue.get, 'STOP'): 195 for file_or_module in iter(tasks_queue.get, 'STOP'):
201 result = self._run_linter(config, file_or_module[0]) 196 result = self._run_linter(file_or_module[0])
202 try: 197 try:
203 results_queue.put(result) 198 results_queue.put(result)
204 except Exception as ex: 199 except Exception as ex:
205 print("internal error with sending report for module %s" % f ile_or_module, file=sys.stderr) 200 print("internal error with sending report for module %s" % f ile_or_module, file=sys.stderr)
206 print(ex, file=sys.stderr) 201 print(ex, file=sys.stderr)
207 results_queue.put({}) 202 results_queue.put({})
208 203
209 def _run_linter(self, config, file_or_module): 204 def _run_linter(self, file_or_module):
210 linter = PyLinter() 205 linter = PyLinter()
211 206
212 # Register standard checkers. 207 # Register standard checkers.
213 linter.load_default_plugins() 208 linter.load_default_plugins()
214 # Load command line plugins. 209 # Load command line plugins.
215 # TODO linter.load_plugin_modules(self._plugins) 210 # TODO linter.load_plugin_modules(self._plugins)
216 211
217 linter.disable('pointless-except') 212 linter.load_configuration(**self._config)
218 linter.disable('suppressed-message') 213 linter.set_reporter(reporters.CollectingReporter())
219 linter.disable('useless-suppression')
220
221 # TODO(cpopa): the sub-linters will not know all the options
222 # because they are not available here, as they are patches to
223 # PyLinter options. The following is just a hack to handle
224 # just a part of the options available in the Run class.
225
226 if 'disable_msg' in config:
227 # Disable everything again. We don't have access
228 # to the original linter though.
229 for msgid in config['disable_msg']:
230 linter.disable(msgid)
231 for key in set(config) - set(dict(linter.options)):
232 del config[key]
233
234 config['jobs'] = 1 # Child does not parallelize any further.
235 linter.load_configuration(**config)
236 linter.set_reporter(CollectingReporter())
237 214
238 # Run the checks. 215 # Run the checks.
239 linter.check(file_or_module) 216 linter.check(file_or_module)
240 217
241 msgs = [_get_new_args(m) for m in linter.reporter.messages] 218 msgs = [_get_new_args(m) for m in linter.reporter.messages]
242 return (file_or_module, linter.file_state.base_name, linter.current_ name, 219 return (file_or_module, linter.file_state.base_name, linter.current_ name,
243 msgs, linter.stats, linter.msg_status) 220 msgs, linter.stats, linter.msg_status)
244 221
245 222
246 class PyLinter(OptionsManagerMixIn, MessagesHandlerMixIn, ReportsHandlerMixIn, 223 class PyLinter(configuration.OptionsManagerMixIn,
247 BaseTokenChecker): 224 utils.MessagesHandlerMixIn,
225 utils.ReportsHandlerMixIn,
226 checkers.BaseTokenChecker):
248 """lint Python modules using external checkers. 227 """lint Python modules using external checkers.
249 228
250 This is the main checker controlling the other ones and the reports 229 This is the main checker controlling the other ones and the reports
251 generation. It is itself both a raw checker and an astroid checker in order 230 generation. It is itself both a raw checker and an astroid checker in order
252 to: 231 to:
253 * handle message activation / deactivation at the module level 232 * handle message activation / deactivation at the module level
254 * handle some basic but necessary stats'data (number of classes, methods...) 233 * handle some basic but necessary stats'data (number of classes, methods...)
255 234
256 IDE plugins developpers: you may have to call 235 IDE plugins developpers: you may have to call
257 `astroid.builder.MANAGER.astroid_cache.clear()` accross run if you want 236 `astroid.builder.MANAGER.astroid_cache.clear()` accross run if you want
258 to ensure the latest code version is actually checked. 237 to ensure the latest code version is actually checked.
259 """ 238 """
260 239
261 __implements__ = (ITokenChecker,) 240 __implements__ = (interfaces.ITokenChecker, )
262 241
263 name = 'master' 242 name = 'master'
264 priority = 0 243 priority = 0
265 level = 0 244 level = 0
266 msgs = MSGS 245 msgs = MSGS
267 246
268 @staticmethod 247 @staticmethod
269 def make_options(): 248 def make_options():
270 return (('ignore', 249 return (('ignore',
271 {'type' : 'csv', 'metavar' : '<file>[,<file>...]', 250 {'type' : 'csv', 'metavar' : '<file>[,<file>...]',
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
323 302
324 ('comment', 303 ('comment',
325 {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>', 304 {'default': 0, 'type' : 'yn', 'metavar' : '<y_or_n>',
326 'group': 'Reports', 'level': 1, 305 'group': 'Reports', 'level': 1,
327 'help' : 'Add a comment according to your evaluation note. ' 306 'help' : 'Add a comment according to your evaluation note. '
328 'This is used by the global evaluation report (RP0004 ).'}), 307 'This is used by the global evaluation report (RP0004 ).'}),
329 308
330 ('confidence', 309 ('confidence',
331 {'type' : 'multiple_choice', 'metavar': '<levels>', 310 {'type' : 'multiple_choice', 'metavar': '<levels>',
332 'default': '', 311 'default': '',
333 'choices': [c.name for c in CONFIDENCE_LEVELS], 312 'choices': [c.name for c in interfaces.CONFIDENCE_LEVELS],
334 'group': 'Messages control', 313 'group': 'Messages control',
335 'help' : 'Only show warnings with the listed confidence levels .' 314 'help' : 'Only show warnings with the listed confidence levels .'
336 ' Leave empty to show all. Valid levels: %s' % ( 315 ' Leave empty to show all. Valid levels: %s' % (
337 ', '.join(c.name for c in CONFIDENCE_LEVELS),)}), 316 ', '.join(c.name for c in interfaces.CONFIDENCE_L EVELS),)}),
338 317
339 ('enable', 318 ('enable',
340 {'type' : 'csv', 'metavar': '<msg ids>', 319 {'type' : 'csv', 'metavar': '<msg ids>',
341 'short': 'e', 320 'short': 'e',
342 'group': 'Messages control', 321 'group': 'Messages control',
343 'help' : 'Enable the message, report, category or checker with the ' 322 'help' : 'Enable the message, report, category or checker with the '
344 'given id(s). You can either give multiple identifier ' 323 'given id(s). You can either give multiple identifier '
345 'separated by comma (,) or put this option multiple t ime. ' 324 'separated by comma (,) or put this option multiple t ime. '
346 'See also the "--disable" option for examples. '}), 325 'See also the "--disable" option for examples. '}),
347 326
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 option_groups = ( 378 option_groups = (
400 ('Messages control', 'Options controling analysis messages'), 379 ('Messages control', 'Options controling analysis messages'),
401 ('Reports', 'Options related to output formating and reporting'), 380 ('Reports', 'Options related to output formating and reporting'),
402 ) 381 )
403 382
404 def __init__(self, options=(), reporter=None, option_groups=(), 383 def __init__(self, options=(), reporter=None, option_groups=(),
405 pylintrc=None): 384 pylintrc=None):
406 # some stuff has to be done before ancestors initialization... 385 # some stuff has to be done before ancestors initialization...
407 # 386 #
408 # messages store / checkers / reporter / astroid manager 387 # messages store / checkers / reporter / astroid manager
409 self.msgs_store = MessagesStore() 388 self.msgs_store = utils.MessagesStore()
410 self.reporter = None 389 self.reporter = None
411 self._reporter_name = None 390 self._reporter_name = None
412 self._reporters = {} 391 self._reporters = {}
413 self._checkers = defaultdict(list) 392 self._checkers = collections.defaultdict(list)
414 self._pragma_lineno = {} 393 self._pragma_lineno = {}
415 self._ignore_file = False 394 self._ignore_file = False
416 # visit variables 395 # visit variables
417 self.file_state = FileState() 396 self.file_state = utils.FileState()
418 self.current_name = None 397 self.current_name = None
419 self.current_file = None 398 self.current_file = None
420 self.stats = None 399 self.stats = None
421 # init options 400 # init options
401 self._external_opts = options
422 self.options = options + PyLinter.make_options() 402 self.options = options + PyLinter.make_options()
423 self.option_groups = option_groups + PyLinter.option_groups 403 self.option_groups = option_groups + PyLinter.option_groups
424 self._options_methods = { 404 self._options_methods = {
425 'enable': self.enable, 405 'enable': self.enable,
426 'disable': self.disable} 406 'disable': self.disable}
427 self._bw_options_methods = {'disable-msg': self.disable, 407 self._bw_options_methods = {'disable-msg': self.disable,
428 'enable-msg': self.enable} 408 'enable-msg': self.enable}
429 full_version = '%%prog %s, \nastroid %s, common %s\nPython %s' % ( 409 full_version = '%%prog %s, \nastroid %s, common %s\nPython %s' % (
430 version, astroid_version, common_version, sys.version) 410 version, astroid_version, common_version, sys.version)
431 OptionsManagerMixIn.__init__(self, usage=__doc__, 411 configuration.OptionsManagerMixIn.__init__(
432 version=full_version, 412 self, usage=__doc__,
433 config_file=pylintrc or config.PYLINTRC) 413 version=full_version,
434 MessagesHandlerMixIn.__init__(self) 414 config_file=pylintrc or config.PYLINTRC)
435 ReportsHandlerMixIn.__init__(self) 415 utils.MessagesHandlerMixIn.__init__(self)
436 BaseTokenChecker.__init__(self) 416 utils.ReportsHandlerMixIn.__init__(self)
417 checkers.BaseTokenChecker.__init__(self)
437 # provided reports 418 # provided reports
438 self.reports = (('RP0001', 'Messages by category', 419 self.reports = (('RP0001', 'Messages by category',
439 report_total_messages_stats), 420 report_total_messages_stats),
440 ('RP0002', '% errors / warnings by module', 421 ('RP0002', '% errors / warnings by module',
441 report_messages_by_module_stats), 422 report_messages_by_module_stats),
442 ('RP0003', 'Messages', 423 ('RP0003', 'Messages',
443 report_messages_stats), 424 report_messages_stats),
444 ('RP0004', 'Global evaluation', 425 ('RP0004', 'Global evaluation',
445 self.report_evaluation), 426 self.report_evaluation),
446 ) 427 )
447 self.register_checker(self) 428 self.register_checker(self)
448 self._dynamic_plugins = set() 429 self._dynamic_plugins = set()
449 self.load_provider_defaults() 430 self.load_provider_defaults()
450 if reporter: 431 if reporter:
451 self.set_reporter(reporter) 432 self.set_reporter(reporter)
452 433
453 def load_default_plugins(self): 434 def load_default_plugins(self):
454 checkers_initialize(self) 435 checkers.initialize(self)
455 reporters_initialize(self) 436 reporters.initialize(self)
456 # Make sure to load the default reporter, because 437 # Make sure to load the default reporter, because
457 # the option has been set before the plugins had been loaded. 438 # the option has been set before the plugins had been loaded.
458 if not self.reporter: 439 if not self.reporter:
459 self._load_reporter() 440 self._load_reporter()
460 441
461 def load_plugin_modules(self, modnames): 442 def load_plugin_modules(self, modnames):
462 """take a list of module names which are pylint plugins and load 443 """take a list of module names which are pylint plugins and load
463 and register them 444 and register them
464 """ 445 """
465 for modname in modnames: 446 for modname in modnames:
466 if modname in self._dynamic_plugins: 447 if modname in self._dynamic_plugins:
467 continue 448 continue
468 self._dynamic_plugins.add(modname) 449 self._dynamic_plugins.add(modname)
469 module = load_module_from_name(modname) 450 module = modutils.load_module_from_name(modname)
470 module.register(self) 451 module.register(self)
471 452
472 def _load_reporter(self): 453 def _load_reporter(self):
473 name = self._reporter_name.lower() 454 name = self._reporter_name.lower()
474 if name in self._reporters: 455 if name in self._reporters:
475 self.set_reporter(self._reporters[name]()) 456 self.set_reporter(self._reporters[name]())
476 else: 457 else:
477 qname = self._reporter_name 458 qname = self._reporter_name
478 module = load_module_from_name(get_module_part(qname)) 459 module = modutils.load_module_from_name(
460 modutils.get_module_part(qname))
479 class_name = qname.split('.')[-1] 461 class_name = qname.split('.')[-1]
480 reporter_class = getattr(module, class_name) 462 reporter_class = getattr(module, class_name)
481 self.set_reporter(reporter_class()) 463 self.set_reporter(reporter_class())
482 464
483 def set_reporter(self, reporter): 465 def set_reporter(self, reporter):
484 """set the reporter used to display messages and reports""" 466 """set the reporter used to display messages and reports"""
485 self.reporter = reporter 467 self.reporter = reporter
486 reporter.linter = self 468 reporter.linter = self
487 469
488 def set_option(self, optname, value, action=None, optdict=None): 470 def set_option(self, optname, value, action=None, optdict=None):
489 """overridden from configuration.OptionsProviderMixin to handle some 471 """overridden from configuration.OptionsProviderMixin to handle some
490 special options 472 special options
491 """ 473 """
492 if optname in self._options_methods or \ 474 if optname in self._options_methods or \
493 optname in self._bw_options_methods: 475 optname in self._bw_options_methods:
494 if value: 476 if value:
495 try: 477 try:
496 meth = self._options_methods[optname] 478 meth = self._options_methods[optname]
497 except KeyError: 479 except KeyError:
498 meth = self._bw_options_methods[optname] 480 meth = self._bw_options_methods[optname]
499 warn('%s is deprecated, replace it by %s' % ( 481 warnings.warn('%s is deprecated, replace it by %s' % (
500 optname, optname.split('-')[0]), DeprecationWarning) 482 optname, optname.split('-')[0]),
501 value = check_csv(None, optname, value) 483 DeprecationWarning)
484 value = optik_ext.check_csv(None, optname, value)
502 if isinstance(value, (list, tuple)): 485 if isinstance(value, (list, tuple)):
503 for _id in value: 486 for _id in value:
504 meth(_id, ignore_unknown=True) 487 meth(_id, ignore_unknown=True)
505 else: 488 else:
506 meth(value) 489 meth(value)
490 return # no need to call set_option, disable/enable methods do i t
507 elif optname == 'output-format': 491 elif optname == 'output-format':
508 self._reporter_name = value 492 self._reporter_name = value
509 # If the reporters are already available, load 493 # If the reporters are already available, load
510 # the reporter class. 494 # the reporter class.
511 if self._reporters: 495 if self._reporters:
512 self._load_reporter() 496 self._load_reporter()
497
513 try: 498 try:
514 BaseTokenChecker.set_option(self, optname, value, action, optdict) 499 checkers.BaseTokenChecker.set_option(self, optname,
515 except UnsupportedAction: 500 value, action, optdict)
501 except configuration.UnsupportedAction:
516 print('option %s can\'t be read from config file' % \ 502 print('option %s can\'t be read from config file' % \
517 optname, file=sys.stderr) 503 optname, file=sys.stderr)
518 504
519 def register_reporter(self, reporter_class): 505 def register_reporter(self, reporter_class):
520 self._reporters[reporter_class.name] = reporter_class 506 self._reporters[reporter_class.name] = reporter_class
521 507
522 def report_order(self): 508 def report_order(self):
523 reports = sorted(self._reports, key=lambda x: getattr(x, 'name', '')) 509 reports = sorted(self._reports, key=lambda x: getattr(x, 'name', ''))
524 try: 510 try:
525 # Remove the current reporter and add it 511 # Remove the current reporter and add it
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
557 if msgcat == 'E': 543 if msgcat == 'E':
558 for msgid in msgids: 544 for msgid in msgids:
559 self.enable(msgid) 545 self.enable(msgid)
560 else: 546 else:
561 for msgid in msgids: 547 for msgid in msgids:
562 self.disable(msgid) 548 self.disable(msgid)
563 549
564 def disable_reporters(self): 550 def disable_reporters(self):
565 """disable all reporters""" 551 """disable all reporters"""
566 for reporters in six.itervalues(self._reports): 552 for reporters in six.itervalues(self._reports):
567 for report_id, _title, _cb in reporters: 553 for report_id, _, _ in reporters:
568 self.disable_report(report_id) 554 self.disable_report(report_id)
569 555
570 def error_mode(self): 556 def error_mode(self):
571 """error mode: enable only errors; no reports, no persistent""" 557 """error mode: enable only errors; no reports, no persistent"""
572 self.disable_noerror_messages() 558 self.disable_noerror_messages()
573 self.disable('miscellaneous') 559 self.disable('miscellaneous')
574 self.set_option('reports', False) 560 self.set_option('reports', False)
575 self.set_option('persistent', False) 561 self.set_option('persistent', False)
576 562
577 # block level option handling ############################################# 563 # block level option handling #############################################
578 # 564 #
579 # see func_block_disable_msg.py test case for expected behaviour 565 # see func_block_disable_msg.py test case for expected behaviour
580 566
581 def process_tokens(self, tokens): 567 def process_tokens(self, tokens):
582 """process tokens from the current module to search for module/block 568 """process tokens from the current module to search for module/block
583 level options 569 level options
584 """ 570 """
585 control_pragmas = {'disable', 'enable'} 571 control_pragmas = {'disable', 'enable'}
586 for (tok_type, content, start, _, _) in tokens: 572 for (tok_type, content, start, _, _) in tokens:
587 if tok_type != tokenize.COMMENT: 573 if tok_type != tokenize.COMMENT:
588 continue 574 continue
589 match = OPTION_RGX.search(content) 575 match = utils.OPTION_RGX.search(content)
590 if match is None: 576 if match is None:
591 continue 577 continue
592 if match.group(1).strip() == "disable-all" or \ 578 if match.group(1).strip() == "disable-all" or \
593 match.group(1).strip() == 'skip-file': 579 match.group(1).strip() == 'skip-file':
594 if match.group(1).strip() == "disable-all": 580 if match.group(1).strip() == "disable-all":
595 self.add_message('deprecated-pragma', line=start[0], 581 self.add_message('deprecated-pragma', line=start[0],
596 args=('disable-all', 'skip-file')) 582 args=('disable-all', 'skip-file'))
597 self.add_message('file-ignored', line=start[0]) 583 self.add_message('file-ignored', line=start[0])
598 self._ignore_file = True 584 self._ignore_file = True
599 return 585 return
600 try: 586 try:
601 opt, value = match.group(1).split('=', 1) 587 opt, value = match.group(1).split('=', 1)
602 except ValueError: 588 except ValueError:
603 self.add_message('bad-inline-option', args=match.group(1).strip( ), 589 self.add_message('bad-inline-option', args=match.group(1).strip( ),
604 line=start[0]) 590 line=start[0])
605 continue 591 continue
606 opt = opt.strip() 592 opt = opt.strip()
607 if opt in self._options_methods or opt in self._bw_options_methods: 593 if opt in self._options_methods or opt in self._bw_options_methods:
608 try: 594 try:
609 meth = self._options_methods[opt] 595 meth = self._options_methods[opt]
610 except KeyError: 596 except KeyError:
611 meth = self._bw_options_methods[opt] 597 meth = self._bw_options_methods[opt]
612 # found a "(dis|en)able-msg" pragma deprecated suppresssion 598 # found a "(dis|en)able-msg" pragma deprecated suppresssion
613 self.add_message('deprecated-pragma', line=start[0], args=(o pt, opt.replace('-msg', ''))) 599 self.add_message('deprecated-pragma', line=start[0], args=(o pt, opt.replace('-msg', '')))
614 for msgid in splitstrip(value): 600 for msgid in textutils.splitstrip(value):
615 # Add the line where a control pragma was encountered. 601 # Add the line where a control pragma was encountered.
616 if opt in control_pragmas: 602 if opt in control_pragmas:
617 self._pragma_lineno[msgid] = start[0] 603 self._pragma_lineno[msgid] = start[0]
618 604
619 try: 605 try:
620 if (opt, msgid) == ('disable', 'all'): 606 if (opt, msgid) == ('disable', 'all'):
621 self.add_message('deprecated-pragma', line=start[0], args=('disable=all', 'skip-file')) 607 self.add_message('deprecated-pragma', line=start[0], args=('disable=all', 'skip-file'))
622 self.add_message('file-ignored', line=start[0]) 608 self.add_message('file-ignored', line=start[0])
623 self._ignore_file = True 609 self._ignore_file = True
624 return 610 return
625 meth(msgid, 'module', start[0]) 611 meth(msgid, 'module', start[0])
626 except UnknownMessage: 612 except utils.UnknownMessage:
627 self.add_message('bad-option-value', args=msgid, line=st art[0]) 613 self.add_message('bad-option-value', args=msgid, line=st art[0])
628 else: 614 else:
629 self.add_message('unrecognized-inline-option', args=opt, line=st art[0]) 615 self.add_message('unrecognized-inline-option', args=opt, line=st art[0])
630 616
631 617
632 # code checking methods ################################################### 618 # code checking methods ###################################################
633 619
634 def get_checkers(self): 620 def get_checkers(self):
635 """return all available checkers as a list""" 621 """return all available checkers as a list"""
636 return [self] + [c for checkers in six.itervalues(self._checkers) 622 return [self] + [c for checkers in six.itervalues(self._checkers)
637 for c in checkers if c is not self] 623 for c in checkers if c is not self]
638 624
639 def prepare_checkers(self): 625 def prepare_checkers(self):
640 """return checkers needed for activated messages and reports""" 626 """return checkers needed for activated messages and reports"""
641 if not self.config.reports: 627 if not self.config.reports:
642 self.disable_reporters() 628 self.disable_reporters()
643 # get needed checkers 629 # get needed checkers
644 neededcheckers = [self] 630 neededcheckers = [self]
645 for checker in self.get_checkers()[1:]: 631 for checker in self.get_checkers()[1:]:
646 # fatal errors should not trigger enable / disabling a checker 632 # fatal errors should not trigger enable / disabling a checker
647 messages = set(msg for msg in checker.msgs 633 messages = set(msg for msg in checker.msgs
648 if msg[0] != 'F' and self.is_message_enabled(msg)) 634 if msg[0] != 'F' and self.is_message_enabled(msg))
649 if (messages or 635 if (messages or
650 any(self.report_is_enabled(r[0]) for r in checker.reports)): 636 any(self.report_is_enabled(r[0]) for r in checker.reports)):
651 neededcheckers.append(checker) 637 neededcheckers.append(checker)
652 # Sort checkers by priority 638 # Sort checkers by priority
653 neededcheckers = sorted(neededcheckers, key=attrgetter('priority'), 639 neededcheckers = sorted(neededcheckers,
640 key=operator.attrgetter('priority'),
654 reverse=True) 641 reverse=True)
655 return neededcheckers 642 return neededcheckers
656 643
657 def should_analyze_file(self, modname, path): # pylint: disable=unused-argum ent, no-self-use 644 def should_analyze_file(self, modname, path): # pylint: disable=unused-argum ent, no-self-use
658 """Returns whether or not a module should be checked. 645 """Returns whether or not a module should be checked.
659 646
660 This implementation returns True for all python source file, indicating 647 This implementation returns True for all python source file, indicating
661 that all files should be linted. 648 that all files should be linted.
662 649
663 Subclasses may override this method to indicate that modules satisfying 650 Subclasses may override this method to indicate that modules satisfying
(...skipping 16 matching lines...) Expand all
680 if not msg.may_be_emitted(): 667 if not msg.may_be_emitted():
681 self._msgs_state[msg.msgid] = False 668 self._msgs_state[msg.msgid] = False
682 669
683 if not isinstance(files_or_modules, (list, tuple)): 670 if not isinstance(files_or_modules, (list, tuple)):
684 files_or_modules = (files_or_modules,) 671 files_or_modules = (files_or_modules,)
685 672
686 if self.config.jobs == 1: 673 if self.config.jobs == 1:
687 self._do_check(files_or_modules) 674 self._do_check(files_or_modules)
688 else: 675 else:
689 # Hack that permits running pylint, on Windows, with -m switch 676 # Hack that permits running pylint, on Windows, with -m switch
690 # and with --jobs, as in 'py -2 -m pylint .. --jobs'. 677 # and with --jobs, as in 'python -2 -m pylint .. --jobs'.
691 # For more details why this is needed, 678 # For more details why this is needed,
692 # see Python issue http://bugs.python.org/issue10845. 679 # see Python issue http://bugs.python.org/issue10845.
693 680
694 mock_main = six.PY2 and __name__ != '__main__' # -m switch 681 mock_main = __name__ != '__main__' # -m switch
695 if mock_main: 682 if mock_main:
696 sys.modules['__main__'] = sys.modules[__name__] 683 sys.modules['__main__'] = sys.modules[__name__]
697 try: 684 try:
698 self._parallel_check(files_or_modules) 685 self._parallel_check(files_or_modules)
699 finally: 686 finally:
700 if mock_main: 687 if mock_main:
701 sys.modules.pop('__main__') 688 sys.modules.pop('__main__')
702 689
703 def _parallel_task(self, files_or_modules): 690 def _parallel_task(self, files_or_modules):
704 # Prepare configuration for child linters. 691 # Prepare configuration for child linters.
705 config = vars(self.config) 692 filter_options = {'symbols', 'include-ids', 'long-help'}
693 filter_options.update([opt_name for opt_name, _ in self._external_opts])
694 config = {}
695 for opt_providers in six.itervalues(self._all_options):
696 for optname, optdict, val in opt_providers.options_and_values():
697 if optname not in filter_options:
698 config[optname] = configuration.format_option_value(optdict, val)
699
706 childs = [] 700 childs = []
707 manager = multiprocessing.Manager() # pylint: disable=no-member 701 manager = multiprocessing.Manager() # pylint: disable=no-member
708 tasks_queue = manager.Queue() # pylint: disable=no-member 702 tasks_queue = manager.Queue() # pylint: disable=no-member
709 results_queue = manager.Queue() # pylint: disable=no-member 703 results_queue = manager.Queue() # pylint: disable=no-member
710 704
711 for _ in range(self.config.jobs): 705 for _ in range(self.config.jobs):
712 cl = ChildLinter(args=(tasks_queue, results_queue, config)) 706 cl = ChildLinter(args=(tasks_queue, results_queue, config))
713 cl.start() # pylint: disable=no-member 707 cl.start() # pylint: disable=no-member
714 childs.append(cl) 708 childs.append(cl)
715 709
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
752 module, 746 module,
753 messages, 747 messages,
754 stats, 748 stats,
755 msg_status 749 msg_status
756 ) = result 750 ) = result
757 751
758 if file_or_module == files_or_modules[-1]: 752 if file_or_module == files_or_modules[-1]:
759 last_module = module 753 last_module = module
760 754
761 for msg in messages: 755 for msg in messages:
762 msg = Message(*msg) 756 msg = utils.Message(*msg)
763 self.set_current_module(module) 757 self.set_current_module(module)
764 self.reporter.handle_message(msg) 758 self.reporter.handle_message(msg)
765 759
766 all_stats.append(stats) 760 all_stats.append(stats)
767 self.msg_status |= msg_status 761 self.msg_status |= msg_status
768 762
769 self.stats = _merge_stats(chain(all_stats, [self.stats])) 763 self.stats = _merge_stats(itertools.chain(all_stats, [self.stats]))
770 self.current_name = last_module 764 self.current_name = last_module
771 765
772 # Insert stats data to local checkers. 766 # Insert stats data to local checkers.
773 for checker in self.get_checkers(): 767 for checker in self.get_checkers():
774 if checker is not self: 768 if checker is not self:
775 checker.stats = self.stats 769 checker.stats = self.stats
776 770
777 def _do_check(self, files_or_modules): 771 def _do_check(self, files_or_modules):
778 walker = PyLintASTWalker(self) 772 walker = utils.PyLintASTWalker(self)
779 checkers = self.prepare_checkers() 773 checkers = self.prepare_checkers()
780 tokencheckers = [c for c in checkers if implements(c, ITokenChecker) 774 tokencheckers = [c for c in checkers
775 if interface.implements(c, interfaces.ITokenChecker)
781 and c is not self] 776 and c is not self]
782 rawcheckers = [c for c in checkers if implements(c, IRawChecker)] 777 rawcheckers = [c for c in checkers
778 if interface.implements(c, interfaces.IRawChecker)]
783 # notify global begin 779 # notify global begin
784 for checker in checkers: 780 for checker in checkers:
785 checker.open() 781 checker.open()
786 if implements(checker, IAstroidChecker): 782 if interface.implements(checker, interfaces.IAstroidChecker):
787 walker.add_checker(checker) 783 walker.add_checker(checker)
788 # build ast and check modules or packages 784 # build ast and check modules or packages
789 for descr in self.expand_files(files_or_modules): 785 for descr in self.expand_files(files_or_modules):
790 modname, filepath = descr['name'], descr['path'] 786 modname, filepath = descr['name'], descr['path']
791 if not descr['isarg'] and not self.should_analyze_file(modname, file path): 787 if not descr['isarg'] and not self.should_analyze_file(modname, file path):
792 continue 788 continue
793 if self.config.files_output: 789 if self.config.files_output:
794 reportfile = 'pylint_%s.%s' % (modname, self.reporter.extension) 790 reportfile = 'pylint_%s.%s' % (modname, self.reporter.extension)
795 self.reporter.set_output(open(reportfile, 'w')) 791 self.reporter.set_output(open(reportfile, 'w'))
796 self.set_current_module(modname, filepath) 792 self.set_current_module(modname, filepath)
797 # get the module representation 793 # get the module representation
798 astroid = self.get_ast(filepath, modname) 794 ast_node = self.get_ast(filepath, modname)
799 if astroid is None: 795 if ast_node is None:
800 continue 796 continue
801 # XXX to be correct we need to keep module_msgs_state for every 797 # XXX to be correct we need to keep module_msgs_state for every
802 # analyzed module (the problem stands with localized messages which 798 # analyzed module (the problem stands with localized messages which
803 # are only detected in the .close step) 799 # are only detected in the .close step)
804 self.file_state = FileState(descr['basename']) 800 self.file_state = utils.FileState(descr['basename'])
805 self._ignore_file = False 801 self._ignore_file = False
806 # fix the current file (if the source file was not available or 802 # fix the current file (if the source file was not available or
807 # if it's actually a c extension) 803 # if it's actually a c extension)
808 self.current_file = astroid.file # pylint: disable=maybe-no-member 804 self.current_file = ast_node.file # pylint: disable=maybe-no-member
809 self.check_astroid_module(astroid, walker, rawcheckers, tokenchecker s) 805 self.check_astroid_module(ast_node, walker, rawcheckers, tokenchecke rs)
810 # warn about spurious inline messages handling 806 # warn about spurious inline messages handling
811 for msgid, line, args in self.file_state.iter_spurious_suppression_m essages(self.msgs_store): 807 for msgid, line, args in self.file_state.iter_spurious_suppression_m essages(self.msgs_store):
812 self.add_message(msgid, line, None, args) 808 self.add_message(msgid, line, None, args)
813 # notify global end 809 # notify global end
814 self.stats['statement'] = walker.nbstatements 810 self.stats['statement'] = walker.nbstatements
815 checkers.reverse() 811 checkers.reverse()
816 for checker in checkers: 812 for checker in checkers:
817 checker.close() 813 checker.close()
818 814
819 def expand_files(self, modules): 815 def expand_files(self, modules):
820 """get modules and errors from a list of modules and handle errors 816 """get modules and errors from a list of modules and handle errors
821 """ 817 """
822 result, errors = expand_modules(modules, self.config.black_list) 818 result, errors = utils.expand_modules(modules, self.config.black_list)
823 for error in errors: 819 for error in errors:
824 message = modname = error["mod"] 820 message = modname = error["mod"]
825 key = error["key"] 821 key = error["key"]
826 self.set_current_module(modname) 822 self.set_current_module(modname)
827 if key == "fatal": 823 if key == "fatal":
828 message = str(error["ex"]).replace(os.getcwd() + os.sep, '') 824 message = str(error["ex"]).replace(os.getcwd() + os.sep, '')
829 self.add_message(key, args=message) 825 self.add_message(key, args=message)
830 return result 826 return result
831 827
832 def set_current_module(self, modname, filepath=None): 828 def set_current_module(self, modname, filepath=None):
833 """set the name of the currently analyzed module and 829 """set the name of the currently analyzed module and
834 init statistics for it 830 init statistics for it
835 """ 831 """
836 if not modname and filepath is None: 832 if not modname and filepath is None:
837 return 833 return
838 self.reporter.on_set_current_module(modname, filepath) 834 self.reporter.on_set_current_module(modname, filepath)
839 self.current_name = modname 835 self.current_name = modname
840 self.current_file = filepath or modname 836 self.current_file = filepath or modname
841 self.stats['by_module'][modname] = {} 837 self.stats['by_module'][modname] = {}
842 self.stats['by_module'][modname]['statement'] = 0 838 self.stats['by_module'][modname]['statement'] = 0
843 for msg_cat in six.itervalues(MSG_TYPES): 839 for msg_cat in six.itervalues(utils.MSG_TYPES):
844 self.stats['by_module'][modname][msg_cat] = 0 840 self.stats['by_module'][modname][msg_cat] = 0
845 841
846 def get_ast(self, filepath, modname): 842 def get_ast(self, filepath, modname):
847 """return a ast(roid) representation for a module""" 843 """return a ast(roid) representation for a module"""
848 try: 844 try:
849 return MANAGER.ast_from_file(filepath, modname, source=True) 845 return MANAGER.ast_from_file(filepath, modname, source=True)
850 except SyntaxError as ex: 846 except SyntaxError as ex:
851 self.add_message('syntax-error', line=ex.lineno, args=ex.msg) 847 self.add_message('syntax-error', line=ex.lineno, args=ex.msg)
852 except AstroidBuildingException as ex: 848 except astroid.AstroidBuildingException as ex:
853 self.add_message('parse-error', args=ex) 849 self.add_message('parse-error', args=ex)
854 except Exception as ex: # pylint: disable=broad-except 850 except Exception as ex: # pylint: disable=broad-except
855 import traceback 851 import traceback
856 traceback.print_exc() 852 traceback.print_exc()
857 self.add_message('astroid-error', args=(ex.__class__, ex)) 853 self.add_message('astroid-error', args=(ex.__class__, ex))
858 854
859 def check_astroid_module(self, astroid, walker, rawcheckers, tokencheckers): 855 def check_astroid_module(self, ast_node, walker,
860 """check a module from its astroid representation, real work""" 856 rawcheckers, tokencheckers):
857 """Check a module from its astroid representation."""
861 try: 858 try:
862 return self._check_astroid_module(astroid, walker, 859 tokens = utils.tokenize_module(ast_node)
863 rawcheckers, tokencheckers)
864 finally:
865 # Close file_stream, if opened, to avoid to open many files.
866 if astroid.file_stream:
867 astroid.file_stream.close()
868 # TODO(cpopa): This is an implementation detail, but it will
869 # be moved in astroid at some point.
870 # We invalidate the cached property, to let the others
871 # modules which relies on this one to get a new file stream.
872 del astroid.file_stream
873
874 def _check_astroid_module(self, astroid, walker, rawcheckers, tokencheckers) :
875 # call raw checkers if possible
876 try:
877 tokens = tokenize_module(astroid)
878 except tokenize.TokenError as ex: 860 except tokenize.TokenError as ex:
879 self.add_message('syntax-error', line=ex.args[1][0], args=ex.args[0] ) 861 self.add_message('syntax-error', line=ex.args[1][0], args=ex.args[0] )
880 return 862 return
881 863
882 if not astroid.pure_python: 864 if not ast_node.pure_python:
883 self.add_message('raw-checker-failed', args=astroid.name) 865 self.add_message('raw-checker-failed', args=ast_node.name)
884 else: 866 else:
885 #assert astroid.file.endswith('.py') 867 #assert astroid.file.endswith('.py')
886 # invoke ITokenChecker interface on self to fetch module/block 868 # invoke ITokenChecker interface on self to fetch module/block
887 # level options 869 # level options
888 self.process_tokens(tokens) 870 self.process_tokens(tokens)
889 if self._ignore_file: 871 if self._ignore_file:
890 return False 872 return False
891 # walk ast to collect line numbers 873 # walk ast to collect line numbers
892 self.file_state.collect_block_lines(self.msgs_store, astroid) 874 self.file_state.collect_block_lines(self.msgs_store, ast_node)
893 # run raw and tokens checkers 875 # run raw and tokens checkers
894 for checker in rawcheckers: 876 for checker in rawcheckers:
895 checker.process_module(astroid) 877 checker.process_module(ast_node)
896 for checker in tokencheckers: 878 for checker in tokencheckers:
897 checker.process_tokens(tokens) 879 checker.process_tokens(tokens)
898 # generate events to astroid checkers 880 # generate events to astroid checkers
899 walker.walk(astroid) 881 walker.walk(ast_node)
900 return True 882 return True
901 883
902 # IAstroidChecker interface ################################################ # 884 # IAstroidChecker interface ################################################ #
903 885
904 def open(self): 886 def open(self):
905 """initialize counters""" 887 """initialize counters"""
906 self.stats = {'by_module' : {}, 888 self.stats = {'by_module' : {},
907 'by_msg' : {}, 889 'by_msg' : {},
908 } 890 }
909 MANAGER.always_load_extensions = self.config.unsafe_load_any_extension 891 MANAGER.always_load_extensions = self.config.unsafe_load_any_extension
910 MANAGER.extension_package_whitelist.update(self.config.extension_pkg_whi telist) 892 MANAGER.extension_package_whitelist.update(
911 for msg_cat in six.itervalues(MSG_TYPES): 893 self.config.extension_pkg_whitelist)
894 for msg_cat in six.itervalues(utils.MSG_TYPES):
912 self.stats[msg_cat] = 0 895 self.stats[msg_cat] = 0
913 896
914 def generate_reports(self): 897 def generate_reports(self):
915 """close the whole package /module, it's time to make reports ! 898 """close the whole package /module, it's time to make reports !
916 899
917 if persistent run, pickle results for later comparison 900 if persistent run, pickle results for later comparison
918 """ 901 """
919 if self.file_state.base_name is not None: 902 if self.file_state.base_name is not None:
920 # load previous results if any 903 # load previous results if any
921 previous_stats = config.load_results(self.file_state.base_name) 904 previous_stats = config.load_results(self.file_state.base_name)
922 # XXX code below needs refactoring to be more reporter agnostic 905 # XXX code below needs refactoring to be more reporter agnostic
923 self.reporter.on_close(self.stats, previous_stats) 906 self.reporter.on_close(self.stats, previous_stats)
924 if self.config.reports: 907 if self.config.reports:
925 sect = self.make_reports(self.stats, previous_stats) 908 sect = self.make_reports(self.stats, previous_stats)
926 if self.config.files_output: 909 if self.config.files_output:
927 filename = 'pylint_global.' + self.reporter.extension 910 filename = 'pylint_global.' + self.reporter.extension
928 self.reporter.set_output(open(filename, 'w')) 911 self.reporter.set_output(open(filename, 'w'))
929 else: 912 else:
930 sect = Section() 913 sect = ureports.Section()
931 if self.config.reports or self.config.output_format == 'html': 914 if self.config.reports or self.config.output_format == 'html':
932 self.reporter.display_results(sect) 915 self.reporter.display_results(sect)
933 # save results if persistent run 916 # save results if persistent run
934 if self.config.persistent: 917 if self.config.persistent:
935 config.save_results(self.stats, self.file_state.base_name) 918 config.save_results(self.stats, self.file_state.base_name)
936 else: 919 else:
937 if self.config.output_format == 'html': 920 if self.config.output_format == 'html':
938 # No output will be emitted for the html 921 # No output will be emitted for the html
939 # reporter if the file doesn't exist, so emit 922 # reporter if the file doesn't exist, so emit
940 # the results here. 923 # the results here.
941 self.reporter.display_results(Section()) 924 self.reporter.display_results(ureports.Section())
942 self.reporter.on_close(self.stats, {}) 925 self.reporter.on_close(self.stats, {})
943 926
944 # specific reports ######################################################## 927 # specific reports ########################################################
945 928
946 def report_evaluation(self, sect, stats, previous_stats): 929 def report_evaluation(self, sect, stats, previous_stats):
947 """make the global evaluation report""" 930 """make the global evaluation report"""
948 # check with at least check 1 statements (usually 0 when there is a 931 # check with at least check 1 statements (usually 0 when there is a
949 # syntax error preventing pylint from further processing) 932 # syntax error preventing pylint from further processing)
950 if stats['statement'] == 0: 933 if stats['statement'] == 0:
951 raise EmptyReport() 934 raise utils.EmptyReport()
952 # get a global note for the code 935 # get a global note for the code
953 evaluation = self.config.evaluation 936 evaluation = self.config.evaluation
954 try: 937 try:
955 note = eval(evaluation, {}, self.stats) # pylint: disable=eval-used 938 note = eval(evaluation, {}, self.stats) # pylint: disable=eval-used
956 except Exception as ex: # pylint: disable=broad-except 939 except Exception as ex: # pylint: disable=broad-except
957 msg = 'An exception occurred while rating: %s' % ex 940 msg = 'An exception occurred while rating: %s' % ex
958 else: 941 else:
959 stats['global_note'] = note 942 stats['global_note'] = note
960 msg = 'Your code has been rated at %.2f/10' % note 943 msg = 'Your code has been rated at %.2f/10' % note
961 pnote = previous_stats.get('global_note') 944 pnote = previous_stats.get('global_note')
962 if pnote is not None: 945 if pnote is not None:
963 msg += ' (previous run: %.2f/10, %+.2f)' % (pnote, note - pnote) 946 msg += ' (previous run: %.2f/10, %+.2f)' % (pnote, note - pnote)
964 if self.config.comment: 947 if self.config.comment:
965 msg = '%s\n%s' % (msg, config.get_note_message(note)) 948 msg = '%s\n%s' % (msg, config.get_note_message(note))
966 sect.append(Text(msg)) 949 sect.append(ureports.Text(msg))
967 950
968 # some reporting functions #################################################### 951 # some reporting functions ####################################################
969 952
970 def report_total_messages_stats(sect, stats, previous_stats): 953 def report_total_messages_stats(sect, stats, previous_stats):
971 """make total errors / warnings report""" 954 """make total errors / warnings report"""
972 lines = ['type', 'number', 'previous', 'difference'] 955 lines = ['type', 'number', 'previous', 'difference']
973 lines += table_lines_from_stats(stats, previous_stats, 956 lines += checkers.table_lines_from_stats(stats, previous_stats,
974 ('convention', 'refactor', 957 ('convention', 'refactor',
975 'warning', 'error')) 958 'warning', 'error'))
976 sect.append(Table(children=lines, cols=4, rheaders=1)) 959 sect.append(ureports.Table(children=lines, cols=4, rheaders=1))
977 960
978 def report_messages_stats(sect, stats, _): 961 def report_messages_stats(sect, stats, _):
979 """make messages type report""" 962 """make messages type report"""
980 if not stats['by_msg']: 963 if not stats['by_msg']:
981 # don't print this report when we didn't detected any errors 964 # don't print this report when we didn't detected any errors
982 raise EmptyReport() 965 raise utils.EmptyReport()
983 in_order = sorted([(value, msg_id) 966 in_order = sorted([(value, msg_id)
984 for msg_id, value in six.iteritems(stats['by_msg']) 967 for msg_id, value in six.iteritems(stats['by_msg'])
985 if not msg_id.startswith('I')]) 968 if not msg_id.startswith('I')])
986 in_order.reverse() 969 in_order.reverse()
987 lines = ('message id', 'occurrences') 970 lines = ('message id', 'occurrences')
988 for value, msg_id in in_order: 971 for value, msg_id in in_order:
989 lines += (msg_id, str(value)) 972 lines += (msg_id, str(value))
990 sect.append(Table(children=lines, cols=2, rheaders=1)) 973 sect.append(ureports.Table(children=lines, cols=2, rheaders=1))
991 974
992 def report_messages_by_module_stats(sect, stats, _): 975 def report_messages_by_module_stats(sect, stats, _):
993 """make errors / warnings by modules report""" 976 """make errors / warnings by modules report"""
994 if len(stats['by_module']) == 1: 977 if len(stats['by_module']) == 1:
995 # don't print this report when we are analysing a single module 978 # don't print this report when we are analysing a single module
996 raise EmptyReport() 979 raise utils.EmptyReport()
997 by_mod = defaultdict(dict) 980 by_mod = collections.defaultdict(dict)
998 for m_type in ('fatal', 'error', 'warning', 'refactor', 'convention'): 981 for m_type in ('fatal', 'error', 'warning', 'refactor', 'convention'):
999 total = stats[m_type] 982 total = stats[m_type]
1000 for module in six.iterkeys(stats['by_module']): 983 for module in six.iterkeys(stats['by_module']):
1001 mod_total = stats['by_module'][module][m_type] 984 mod_total = stats['by_module'][module][m_type]
1002 if total == 0: 985 if total == 0:
1003 percent = 0 986 percent = 0
1004 else: 987 else:
1005 percent = float((mod_total)*100) / total 988 percent = float((mod_total)*100) / total
1006 by_mod[module][m_type] = percent 989 by_mod[module][m_type] = percent
1007 sorted_result = [] 990 sorted_result = []
1008 for module, mod_info in six.iteritems(by_mod): 991 for module, mod_info in six.iteritems(by_mod):
1009 sorted_result.append((mod_info['error'], 992 sorted_result.append((mod_info['error'],
1010 mod_info['warning'], 993 mod_info['warning'],
1011 mod_info['refactor'], 994 mod_info['refactor'],
1012 mod_info['convention'], 995 mod_info['convention'],
1013 module)) 996 module))
1014 sorted_result.sort() 997 sorted_result.sort()
1015 sorted_result.reverse() 998 sorted_result.reverse()
1016 lines = ['module', 'error', 'warning', 'refactor', 'convention'] 999 lines = ['module', 'error', 'warning', 'refactor', 'convention']
1017 for line in sorted_result: 1000 for line in sorted_result:
1018 # Don't report clean modules. 1001 # Don't report clean modules.
1019 if all(entry == 0 for entry in line[:-1]): 1002 if all(entry == 0 for entry in line[:-1]):
1020 continue 1003 continue
1021 lines.append(line[-1]) 1004 lines.append(line[-1])
1022 for val in line[:-1]: 1005 for val in line[:-1]:
1023 lines.append('%.2f' % val) 1006 lines.append('%.2f' % val)
1024 if len(lines) == 5: 1007 if len(lines) == 5:
1025 raise EmptyReport() 1008 raise utils.EmptyReport()
1026 sect.append(Table(children=lines, cols=5, rheaders=1)) 1009 sect.append(ureports.Table(children=lines, cols=5, rheaders=1))
1027 1010
1028 1011
1029 # utilities ################################################################### 1012 # utilities ###################################################################
1030 1013
1031 1014
1032 class ArgumentPreprocessingError(Exception): 1015 class ArgumentPreprocessingError(Exception):
1033 """Raised if an error occurs during argument preprocessing.""" 1016 """Raised if an error occurs during argument preprocessing."""
1034 1017
1035 1018
1036 def preprocess_options(args, search_for): 1019 def preprocess_options(args, search_for):
(...skipping 24 matching lines...) Expand all
1061 val = args[i] 1044 val = args[i]
1062 del args[i] 1045 del args[i]
1063 elif not takearg and val is not None: 1046 elif not takearg and val is not None:
1064 msg = "Option %s doesn't expects a value" % option 1047 msg = "Option %s doesn't expects a value" % option
1065 raise ArgumentPreprocessingError(msg) 1048 raise ArgumentPreprocessingError(msg)
1066 cb(option, val) 1049 cb(option, val)
1067 else: 1050 else:
1068 i += 1 1051 i += 1
1069 1052
1070 1053
1071 @contextmanager 1054 @contextlib.contextmanager
1072 def fix_import_path(args): 1055 def fix_import_path(args):
1073 """Prepare sys.path for running the linter checks. 1056 """Prepare sys.path for running the linter checks.
1074 1057
1075 Within this context, each of the given arguments is importable. 1058 Within this context, each of the given arguments is importable.
1076 Paths are added to sys.path in corresponding order to the arguments. 1059 Paths are added to sys.path in corresponding order to the arguments.
1077 We avoid adding duplicate directories to sys.path. 1060 We avoid adding duplicate directories to sys.path.
1078 `sys.path` is reset to its original value upon exitign this context. 1061 `sys.path` is reset to its original value upon exitign this context.
1079 """ 1062 """
1080 orig = list(sys.path) 1063 orig = list(sys.path)
1081 changes = [] 1064 changes = []
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
1222 level=1) 1205 level=1)
1223 # read configuration 1206 # read configuration
1224 linter.disable('pointless-except') 1207 linter.disable('pointless-except')
1225 linter.disable('suppressed-message') 1208 linter.disable('suppressed-message')
1226 linter.disable('useless-suppression') 1209 linter.disable('useless-suppression')
1227 linter.read_config_file() 1210 linter.read_config_file()
1228 config_parser = linter.cfgfile_parser 1211 config_parser = linter.cfgfile_parser
1229 # run init hook, if present, before loading plugins 1212 # run init hook, if present, before loading plugins
1230 if config_parser.has_option('MASTER', 'init-hook'): 1213 if config_parser.has_option('MASTER', 'init-hook'):
1231 cb_init_hook('init-hook', 1214 cb_init_hook('init-hook',
1232 unquote(config_parser.get('MASTER', 'init-hook'))) 1215 textutils.unquote(config_parser.get('MASTER',
1216 'init-hook')))
1233 # is there some additional plugins in the file configuration, in 1217 # is there some additional plugins in the file configuration, in
1234 if config_parser.has_option('MASTER', 'load-plugins'): 1218 if config_parser.has_option('MASTER', 'load-plugins'):
1235 plugins = splitstrip(config_parser.get('MASTER', 'load-plugins')) 1219 plugins = textutils.splitstrip(
1220 config_parser.get('MASTER', 'load-plugins'))
1236 linter.load_plugin_modules(plugins) 1221 linter.load_plugin_modules(plugins)
1237 # now we can load file config and command line, plugins (which can 1222 # now we can load file config and command line, plugins (which can
1238 # provide options) have been registered 1223 # provide options) have been registered
1239 linter.load_config_file() 1224 linter.load_config_file()
1240 if reporter: 1225 if reporter:
1241 # if a custom reporter is provided as argument, it may be overridden 1226 # if a custom reporter is provided as argument, it may be overridden
1242 # by file parameters, so re-set it here, but before command line 1227 # by file parameters, so re-set it here, but before command line
1243 # parsing so it's still overrideable by command line option 1228 # parsing so it's still overrideable by command line option
1244 linter.set_reporter(reporter) 1229 linter.set_reporter(reporter)
1245 try: 1230 try:
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
1282 linter.generate_reports() 1267 linter.generate_reports()
1283 if exit: 1268 if exit:
1284 sys.exit(self.linter.msg_status) 1269 sys.exit(self.linter.msg_status)
1285 1270
1286 def cb_set_rcfile(self, name, value): 1271 def cb_set_rcfile(self, name, value):
1287 """callback for option preprocessing (i.e. before option parsing)""" 1272 """callback for option preprocessing (i.e. before option parsing)"""
1288 self._rcfile = value 1273 self._rcfile = value
1289 1274
1290 def cb_add_plugins(self, name, value): 1275 def cb_add_plugins(self, name, value):
1291 """callback for option preprocessing (i.e. before option parsing)""" 1276 """callback for option preprocessing (i.e. before option parsing)"""
1292 self._plugins.extend(splitstrip(value)) 1277 self._plugins.extend(textutils.splitstrip(value))
1293 1278
1294 def cb_error_mode(self, *args, **kwargs): 1279 def cb_error_mode(self, *args, **kwargs):
1295 """error mode: 1280 """error mode:
1296 * disable all but error messages 1281 * disable all but error messages
1297 * disable the 'miscellaneous' checker which can be safely deactivated in 1282 * disable the 'miscellaneous' checker which can be safely deactivated in
1298 debug 1283 debug
1299 * disable reports 1284 * disable reports
1300 * do not save execution information 1285 * do not save execution information
1301 """ 1286 """
1302 self.linter.error_mode() 1287 self.linter.error_mode()
1303 1288
1304 def cb_generate_config(self, *args, **kwargs): 1289 def cb_generate_config(self, *args, **kwargs):
1305 """optik callback for sample config file generation""" 1290 """optik callback for sample config file generation"""
1306 self.linter.generate_config(skipsections=('COMMANDS',)) 1291 self.linter.generate_config(skipsections=('COMMANDS',))
1307 sys.exit(0) 1292 sys.exit(0)
1308 1293
1309 def cb_generate_manpage(self, *args, **kwargs): 1294 def cb_generate_manpage(self, *args, **kwargs):
1310 """optik callback for sample config file generation""" 1295 """optik callback for sample config file generation"""
1311 from pylint import __pkginfo__ 1296 from pylint import __pkginfo__
1312 self.linter.generate_manpage(__pkginfo__) 1297 self.linter.generate_manpage(__pkginfo__)
1313 sys.exit(0) 1298 sys.exit(0)
1314 1299
1315 def cb_help_message(self, option, optname, value, parser): 1300 def cb_help_message(self, option, optname, value, parser):
1316 """optik callback for printing some help about a particular message""" 1301 """optik callback for printing some help about a particular message"""
1317 self.linter.msgs_store.help_message(splitstrip(value)) 1302 self.linter.msgs_store.help_message(textutils.splitstrip(value))
1318 sys.exit(0) 1303 sys.exit(0)
1319 1304
1320 def cb_full_documentation(self, option, optname, value, parser): 1305 def cb_full_documentation(self, option, optname, value, parser):
1321 """optik callback for printing full documentation""" 1306 """optik callback for printing full documentation"""
1322 self.linter.print_full_documentation() 1307 self.linter.print_full_documentation()
1323 sys.exit(0) 1308 sys.exit(0)
1324 1309
1325 def cb_list_messages(self, option, optname, value, parser): # FIXME 1310 def cb_list_messages(self, option, optname, value, parser): # FIXME
1326 """optik callback for printing available messages""" 1311 """optik callback for printing available messages"""
1327 self.linter.msgs_store.list_messages() 1312 self.linter.msgs_store.list_messages()
1328 sys.exit(0) 1313 sys.exit(0)
1329 1314
1330 def cb_python3_porting_mode(self, *args, **kwargs): 1315 def cb_python3_porting_mode(self, *args, **kwargs):
1331 """Activate only the python3 porting checker.""" 1316 """Activate only the python3 porting checker."""
1332 self.linter.disable('all') 1317 self.linter.disable('all')
1333 self.linter.enable('python3') 1318 self.linter.enable('python3')
1334 1319
1335 1320
1336 def cb_list_confidence_levels(option, optname, value, parser): 1321 def cb_list_confidence_levels(option, optname, value, parser):
1337 for level in CONFIDENCE_LEVELS: 1322 for level in interfaces.CONFIDENCE_LEVELS:
1338 print('%-18s: %s' % level) 1323 print('%-18s: %s' % level)
1339 sys.exit(0) 1324 sys.exit(0)
1340 1325
1341 def cb_init_hook(optname, value): 1326 def cb_init_hook(optname, value):
1342 """exec arbitrary code to set sys.path for instance""" 1327 """exec arbitrary code to set sys.path for instance"""
1343 exec(value) # pylint: disable=exec-used 1328 exec(value) # pylint: disable=exec-used
1344 1329
1345 1330
1346 if __name__ == '__main__': 1331 if __name__ == '__main__':
1347 Run(sys.argv[1:]) 1332 Run(sys.argv[1:])
OLDNEW
« no previous file with comments | « third_party/pylint/interfaces.py ('k') | third_party/pylint/reporters/json.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698