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

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

Issue 753543006: pylint: upgrade to 1.4.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years 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/testutils.py ('k') | no next file » | 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 """some various utilities and helper classes, most of them used in the 16 """some various utilities and helper classes, most of them used in the
17 main pylint class 17 main pylint class
18 """ 18 """
19 from __future__ import print_function
19 20
21 import collections
22 import os
20 import re 23 import re
21 import sys 24 import sys
22 import tokenize 25 import tokenize
23 import os 26 import warnings
24 from warnings import warn
25 from os.path import dirname, basename, splitext, exists, isdir, join, normpath 27 from os.path import dirname, basename, splitext, exists, isdir, join, normpath
26 28
29 import six
30 from six.moves import zip # pylint: disable=redefined-builtin
31
27 from logilab.common.interface import implements 32 from logilab.common.interface import implements
28 from logilab.common.textutils import normalize_text 33 from logilab.common.textutils import normalize_text
29 from logilab.common.configuration import rest_format_section 34 from logilab.common.configuration import rest_format_section
30 from logilab.common.ureports import Section 35 from logilab.common.ureports import Section
31 36
32 from astroid import nodes, Module 37 from astroid import nodes, Module
33 from astroid.modutils import modpath_from_file, get_module_files, \ 38 from astroid.modutils import modpath_from_file, get_module_files, \
34 file_from_modpath, load_module_from_file 39 file_from_modpath, load_module_from_file
35 40
36 from pylint.interfaces import IRawChecker, ITokenChecker 41 from pylint.interfaces import IRawChecker, ITokenChecker, UNDEFINED
37 42
38 43
39 class UnknownMessage(Exception): 44 class UnknownMessage(Exception):
40 """raised when a unregistered message id is encountered""" 45 """raised when a unregistered message id is encountered"""
41 46
42 class EmptyReport(Exception): 47 class EmptyReport(Exception):
43 """raised when a report is empty and so should not be displayed""" 48 """raised when a report is empty and so should not be displayed"""
44 49
45 50
46 MSG_TYPES = { 51 MSG_TYPES = {
47 'I' : 'info', 52 'I' : 'info',
48 'C' : 'convention', 53 'C' : 'convention',
49 'R' : 'refactor', 54 'R' : 'refactor',
50 'W' : 'warning', 55 'W' : 'warning',
51 'E' : 'error', 56 'E' : 'error',
52 'F' : 'fatal' 57 'F' : 'fatal'
53 } 58 }
54 MSG_TYPES_LONG = dict([(v, k) for k, v in MSG_TYPES.iteritems()]) 59 MSG_TYPES_LONG = {v: k for k, v in six.iteritems(MSG_TYPES)}
55 60
56 MSG_TYPES_STATUS = { 61 MSG_TYPES_STATUS = {
57 'I' : 0, 62 'I' : 0,
58 'C' : 16, 63 'C' : 16,
59 'R' : 8, 64 'R' : 8,
60 'W' : 4, 65 'W' : 4,
61 'E' : 2, 66 'E' : 2,
62 'F' : 1 67 'F' : 1
63 } 68 }
64 69
65 _MSG_ORDER = 'EWRCIF' 70 _MSG_ORDER = 'EWRCIF'
66 MSG_STATE_SCOPE_CONFIG = 0 71 MSG_STATE_SCOPE_CONFIG = 0
67 MSG_STATE_SCOPE_MODULE = 1 72 MSG_STATE_SCOPE_MODULE = 1
73 MSG_STATE_CONFIDENCE = 2
68 74
69 OPTION_RGX = re.compile(r'\s*#.*\bpylint:(.*)') 75 OPTION_RGX = re.compile(r'\s*#.*\bpylint:(.*)')
70 76
71 # The line/node distinction does not apply to fatal errors and reports. 77 # The line/node distinction does not apply to fatal errors and reports.
72 _SCOPE_EXEMPT = 'FR' 78 _SCOPE_EXEMPT = 'FR'
73 79
74 class WarningScope(object): 80 class WarningScope(object):
75 LINE = 'line-based-msg' 81 LINE = 'line-based-msg'
76 NODE = 'node-based-msg' 82 NODE = 'node-based-msg'
77 83
84 _MsgBase = collections.namedtuple(
85 '_MsgBase',
86 ['msg_id', 'symbol', 'msg', 'C', 'category', 'confidence',
87 'abspath', 'path', 'module', 'obj', 'line', 'column'])
88
89
90 class Message(_MsgBase):
91 """This class represent a message to be issued by the reporters"""
92 def __new__(cls, msg_id, symbol, location, msg, confidence):
93 return _MsgBase.__new__(
94 cls, msg_id, symbol, msg, msg_id[0], MSG_TYPES[msg_id[0]],
95 confidence, *location)
96
97 def format(self, template):
98 """Format the message according to the given template.
99
100 The template format is the one of the format method :
101 cf. http://docs.python.org/2/library/string.html#formatstrings
102 """
103 # For some reason, _asdict on derived namedtuples does not work with
104 # Python 3.4. Needs some investigation.
105 return template.format(**dict(zip(self._fields, self)))
106
78 107
79 def get_module_and_frameid(node): 108 def get_module_and_frameid(node):
80 """return the module name and the frame id in the module""" 109 """return the module name and the frame id in the module"""
81 frame = node.frame() 110 frame = node.frame()
82 module, obj = '', [] 111 module, obj = '', []
83 while frame: 112 while frame:
84 if isinstance(frame, Module): 113 if isinstance(frame, Module):
85 module = frame.name 114 module = frame.name
86 else: 115 else:
87 obj.append(getattr(frame, 'name', '<lambda>')) 116 obj.append(getattr(frame, 'name', '<lambda>'))
88 try: 117 try:
89 frame = frame.parent.frame() 118 frame = frame.parent.frame()
90 except AttributeError: 119 except AttributeError:
91 frame = None 120 frame = None
92 obj.reverse() 121 obj.reverse()
93 return module, '.'.join(obj) 122 return module, '.'.join(obj)
94 123
95 def category_id(id): 124 def category_id(cid):
96 id = id.upper() 125 cid = cid.upper()
97 if id in MSG_TYPES: 126 if cid in MSG_TYPES:
98 return id 127 return cid
99 return MSG_TYPES_LONG.get(id) 128 return MSG_TYPES_LONG.get(cid)
100 129
101 130
102 def tokenize_module(module): 131 def tokenize_module(module):
103 stream = module.file_stream 132 stream = module.file_stream
104 stream.seek(0) 133 stream.seek(0)
105 readline = stream.readline 134 readline = stream.readline
106 if sys.version_info < (3, 0): 135 if sys.version_info < (3, 0):
107 if module.file_encoding is not None: 136 if module.file_encoding is not None:
108 readline = lambda: stream.readline().decode(module.file_encoding, 137 readline = lambda: stream.readline().decode(module.file_encoding,
109 'replace') 138 'replace')
110 return list(tokenize.generate_tokens(readline)) 139 return list(tokenize.generate_tokens(readline))
111 return list(tokenize.tokenize(readline)) 140 return list(tokenize.tokenize(readline))
112 141
113 def build_message_def(checker, msgid, msg_tuple): 142 def build_message_def(checker, msgid, msg_tuple):
114 if implements(checker, (IRawChecker, ITokenChecker)): 143 if implements(checker, (IRawChecker, ITokenChecker)):
115 default_scope = WarningScope.LINE 144 default_scope = WarningScope.LINE
116 else: 145 else:
117 default_scope = WarningScope.NODE 146 default_scope = WarningScope.NODE
118 options = {} 147 options = {}
119 if len(msg_tuple) > 3: 148 if len(msg_tuple) > 3:
120 (msg, symbol, descr, options) = msg_tuple 149 (msg, symbol, descr, options) = msg_tuple
121 elif len(msg_tuple) > 2: 150 elif len(msg_tuple) > 2:
122 (msg, symbol, descr) = msg_tuple[:3] 151 (msg, symbol, descr) = msg_tuple[:3]
123 else: 152 else:
124 # messages should have a symbol, but for backward compatibility 153 # messages should have a symbol, but for backward compatibility
125 # they may not. 154 # they may not.
126 (msg, descr) = msg_tuple 155 (msg, descr) = msg_tuple
127 warn("[pylint 0.26] description of message %s doesn't include " 156 warnings.warn("[pylint 0.26] description of message %s doesn't include "
128 "a symbolic name" % msgid, DeprecationWarning) 157 "a symbolic name" % msgid, DeprecationWarning)
129 symbol = None 158 symbol = None
130 options.setdefault('scope', default_scope) 159 options.setdefault('scope', default_scope)
131 return MessageDefinition(checker, msgid, msg, descr, symbol, **options) 160 return MessageDefinition(checker, msgid, msg, descr, symbol, **options)
132 161
133 162
134 class MessageDefinition(object): 163 class MessageDefinition(object):
135 def __init__(self, checker, msgid, msg, descr, symbol, scope, 164 def __init__(self, checker, msgid, msg, descr, symbol, scope,
136 minversion=None, maxversion=None, old_names=None): 165 minversion=None, maxversion=None, old_names=None):
137 self.checker = checker 166 self.checker = checker
138 assert len(msgid) == 5, 'Invalid message id %s' % msgid 167 assert len(msgid) == 5, 'Invalid message id %s' % msgid
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 if scope == 'module': 260 if scope == 'module':
232 self.file_state.set_msg_status(msg, line, False) 261 self.file_state.set_msg_status(msg, line, False)
233 if msg.symbol != 'locally-disabled': 262 if msg.symbol != 'locally-disabled':
234 self.add_message('locally-disabled', line=line, 263 self.add_message('locally-disabled', line=line,
235 args=(msg.symbol, msg.msgid)) 264 args=(msg.symbol, msg.msgid))
236 265
237 else: 266 else:
238 msgs = self._msgs_state 267 msgs = self._msgs_state
239 msgs[msg.msgid] = False 268 msgs[msg.msgid] = False
240 # sync configuration object 269 # sync configuration object
241 self.config.disable_msg = [mid for mid, val in msgs.iteritems() 270 self.config.disable_msg = [mid for mid, val in six.iteritems(msgs)
242 if not val] 271 if not val]
243 272
244 def enable(self, msgid, scope='package', line=None, ignore_unknown=False): 273 def enable(self, msgid, scope='package', line=None, ignore_unknown=False):
245 """reenable message of the given id""" 274 """reenable message of the given id"""
246 assert scope in ('package', 'module') 275 assert scope in ('package', 'module')
247 catid = category_id(msgid) 276 catid = category_id(msgid)
248 # msgid is a category? 277 # msgid is a category?
249 if catid is not None: 278 if catid is not None:
250 for msgid in self.msgs_store._msgs_by_category.get(catid): 279 for msgid in self.msgs_store._msgs_by_category.get(catid):
251 self.enable(msgid, scope, line) 280 self.enable(msgid, scope, line)
(...skipping 17 matching lines...) Expand all
269 return 298 return
270 raise 299 raise
271 300
272 if scope == 'module': 301 if scope == 'module':
273 self.file_state.set_msg_status(msg, line, True) 302 self.file_state.set_msg_status(msg, line, True)
274 self.add_message('locally-enabled', line=line, args=(msg.symbol, msg .msgid)) 303 self.add_message('locally-enabled', line=line, args=(msg.symbol, msg .msgid))
275 else: 304 else:
276 msgs = self._msgs_state 305 msgs = self._msgs_state
277 msgs[msg.msgid] = True 306 msgs[msg.msgid] = True
278 # sync configuration object 307 # sync configuration object
279 self.config.enable = [mid for mid, val in msgs.iteritems() if val] 308 self.config.enable = [mid for mid, val in six.iteritems(msgs) if val ]
280 309
281 def is_message_enabled(self, msg_descr, line=None): 310 def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED):
311 """Returns the scope at which a message was enabled/disabled."""
312 if self.config.confidence and confidence.name not in self.config.confide nce:
313 return MSG_STATE_CONFIDENCE
314 try:
315 if line in self.file_state._module_msgs_state[msgid]:
316 return MSG_STATE_SCOPE_MODULE
317 except (KeyError, TypeError):
318 return MSG_STATE_SCOPE_CONFIG
319
320 def is_message_enabled(self, msg_descr, line=None, confidence=None):
282 """return true if the message associated to the given message id is 321 """return true if the message associated to the given message id is
283 enabled 322 enabled
284 323
285 msgid may be either a numeric or symbolic message id. 324 msgid may be either a numeric or symbolic message id.
286 """ 325 """
326 if self.config.confidence and confidence:
327 if confidence.name not in self.config.confidence:
328 return False
287 try: 329 try:
288 msgid = self.msgs_store.check_message_id(msg_descr).msgid 330 msgid = self.msgs_store.check_message_id(msg_descr).msgid
289 except UnknownMessage: 331 except UnknownMessage:
290 # The linter checks for messages that are not registered 332 # The linter checks for messages that are not registered
291 # due to version mismatch, just treat them as message IDs 333 # due to version mismatch, just treat them as message IDs
292 # for now. 334 # for now.
293 msgid = msg_descr 335 msgid = msg_descr
294 if line is None: 336 if line is None:
295 return self._msgs_state.get(msgid, True) 337 return self._msgs_state.get(msgid, True)
296 try: 338 try:
297 return self.file_state._module_msgs_state[msgid][line] 339 return self.file_state._module_msgs_state[msgid][line]
298 except KeyError: 340 except KeyError:
299 return self._msgs_state.get(msgid, True) 341 return self._msgs_state.get(msgid, True)
300 342
301 def add_message(self, msg_descr, line=None, node=None, args=None): 343 def add_message(self, msg_descr, line=None, node=None, args=None, confidence =UNDEFINED):
302 """Adds a message given by ID or name. 344 """Adds a message given by ID or name.
303 345
304 If provided, the message string is expanded using args 346 If provided, the message string is expanded using args
305 347
306 AST checkers should must the node argument (but may optionally 348 AST checkers should must the node argument (but may optionally
307 provide line if the line number is different), raw and token checkers 349 provide line if the line number is different), raw and token checkers
308 must provide the line argument. 350 must provide the line argument.
309 """ 351 """
310 msg_info = self.msgs_store.check_message_id(msg_descr) 352 msg_info = self.msgs_store.check_message_id(msg_descr)
311 msgid = msg_info.msgid 353 msgid = msg_info.msgid
312 # backward compatibility, message may not have a symbol 354 # backward compatibility, message may not have a symbol
313 symbol = msg_info.symbol or msgid 355 symbol = msg_info.symbol or msgid
314 # Fatal messages and reports are special, the node/scope distinction 356 # Fatal messages and reports are special, the node/scope distinction
315 # does not apply to them. 357 # does not apply to them.
316 if msgid[0] not in _SCOPE_EXEMPT: 358 if msgid[0] not in _SCOPE_EXEMPT:
317 if msg_info.scope == WarningScope.LINE: 359 if msg_info.scope == WarningScope.LINE:
318 assert node is None and line is not None, ( 360 assert node is None and line is not None, (
319 'Message %s must only provide line, got line=%s, node=%s' % (msgid, line, node)) 361 'Message %s must only provide line, got line=%s, node=%s' % (msgid, line, node))
320 elif msg_info.scope == WarningScope.NODE: 362 elif msg_info.scope == WarningScope.NODE:
321 # Node-based warnings may provide an override line. 363 # Node-based warnings may provide an override line.
322 assert node is not None, 'Message %s must provide Node, got None ' 364 assert node is not None, 'Message %s must provide Node, got None '
323 365
324 if line is None and node is not None: 366 if line is None and node is not None:
325 line = node.fromlineno 367 line = node.fromlineno
326 if hasattr(node, 'col_offset'): 368 if hasattr(node, 'col_offset'):
327 col_offset = node.col_offset # XXX measured in bytes for utf-8, divi de by two for chars? 369 col_offset = node.col_offset # XXX measured in bytes for utf-8, divi de by two for chars?
328 else: 370 else:
329 col_offset = None 371 col_offset = None
330 # should this message be displayed 372 # should this message be displayed
331 if not self.is_message_enabled(msgid, line): 373 if not self.is_message_enabled(msgid, line, confidence):
332 self.file_state.handle_ignored_message(msgid, line, node, args) 374 self.file_state.handle_ignored_message(
375 self.get_message_state_scope(msgid, line, confidence),
376 msgid, line, node, args, confidence)
333 return 377 return
334 # update stats 378 # update stats
335 msg_cat = MSG_TYPES[msgid[0]] 379 msg_cat = MSG_TYPES[msgid[0]]
336 self.msg_status |= MSG_TYPES_STATUS[msgid[0]] 380 self.msg_status |= MSG_TYPES_STATUS[msgid[0]]
337 self.stats[msg_cat] += 1 381 self.stats[msg_cat] += 1
338 self.stats['by_module'][self.current_name][msg_cat] += 1 382 self.stats['by_module'][self.current_name][msg_cat] += 1
339 try: 383 try:
340 self.stats['by_msg'][symbol] += 1 384 self.stats['by_msg'][symbol] += 1
341 except KeyError: 385 except KeyError:
342 self.stats['by_msg'][symbol] = 1 386 self.stats['by_msg'][symbol] = 1
343 # expand message ? 387 # expand message ?
344 msg = msg_info.msg 388 msg = msg_info.msg
345 if args: 389 if args:
346 msg %= args 390 msg %= args
347 # get module and object 391 # get module and object
348 if node is None: 392 if node is None:
349 module, obj = self.current_name, '' 393 module, obj = self.current_name, ''
350 path = self.current_file 394 abspath = self.current_file
351 else: 395 else:
352 module, obj = get_module_and_frameid(node) 396 module, obj = get_module_and_frameid(node)
353 path = node.root().file 397 abspath = node.root().file
398 path = abspath.replace(self.reporter.path_strip_prefix, '')
354 # add the message 399 # add the message
355 self.reporter.add_message(msgid, (path, module, obj, line or 1, col_offs et or 0), msg) 400 self.reporter.handle_message(
401 Message(msgid, symbol,
402 (abspath, path, module, obj, line or 1, col_offset or 0), ms g, confidence))
356 403
357 def print_full_documentation(self): 404 def print_full_documentation(self):
358 """output a full documentation in ReST format""" 405 """output a full documentation in ReST format"""
406 print("Pylint global options and switches")
407 print("----------------------------------")
408 print("")
409 print("Pylint provides global options and switches.")
410 print("")
411
359 by_checker = {} 412 by_checker = {}
360 for checker in self.get_checkers(): 413 for checker in self.get_checkers():
361 if checker.name == 'master': 414 if checker.name == 'master':
362 prefix = 'Main '
363 print "Options"
364 print '-------\n'
365 if checker.options: 415 if checker.options:
366 for section, options in checker.options_by_section(): 416 for section, options in checker.options_by_section():
367 if section is None: 417 if section is None:
368 title = 'General options' 418 title = 'General options'
369 else: 419 else:
370 title = '%s options' % section.capitalize() 420 title = '%s options' % section.capitalize()
371 print title 421 print(title)
372 print '~' * len(title) 422 print('~' * len(title))
373 rest_format_section(sys.stdout, None, options) 423 rest_format_section(sys.stdout, None, options)
374 print 424 print("")
375 else: 425 else:
376 try: 426 try:
377 by_checker[checker.name][0] += checker.options_and_values() 427 by_checker[checker.name][0] += checker.options_and_values()
378 by_checker[checker.name][1].update(checker.msgs) 428 by_checker[checker.name][1].update(checker.msgs)
379 by_checker[checker.name][2] += checker.reports 429 by_checker[checker.name][2] += checker.reports
380 except KeyError: 430 except KeyError:
381 by_checker[checker.name] = [list(checker.options_and_values( )), 431 by_checker[checker.name] = [list(checker.options_and_values( )),
382 dict(checker.msgs), 432 dict(checker.msgs),
383 list(checker.reports)] 433 list(checker.reports)]
384 for checker, (options, msgs, reports) in by_checker.iteritems(): 434
385 prefix = '' 435 print("Pylint checkers' options and switches")
386 title = '%s checker' % checker 436 print("-------------------------------------")
387 print title 437 print("")
388 print '-' * len(title) 438 print("Pylint checkers can provide three set of features:")
389 print 439 print("")
440 print("* options that control their execution,")
441 print("* messages that they can raise,")
442 print("* reports that they can generate.")
443 print("")
444 print("Below is a list of all checkers and their features.")
445 print("")
446
447 for checker, (options, msgs, reports) in six.iteritems(by_checker):
448 title = '%s checker' % (checker.replace("_", " ").title())
449 print(title)
450 print('~' * len(title))
451 print("")
452 print("Verbatim name of the checker is ``%s``." % checker)
453 print("")
390 if options: 454 if options:
391 title = 'Options' 455 title = 'Options'
392 print title 456 print(title)
393 print '~' * len(title) 457 print('^' * len(title))
394 rest_format_section(sys.stdout, None, options) 458 rest_format_section(sys.stdout, None, options)
395 print 459 print("")
396 if msgs: 460 if msgs:
397 title = ('%smessages' % prefix).capitalize() 461 title = 'Messages'
398 print title 462 print(title)
399 print '~' * len(title) 463 print('~' * len(title))
400 for msgid, msg in sorted(msgs.iteritems(), 464 for msgid, msg in sorted(six.iteritems(msgs),
401 key=lambda (k, v): (_MSG_ORDER.index(k[ 0]), k)): 465 key=lambda kv: (_MSG_ORDER.index(kv[0][ 0]), kv[1])):
402 msg = build_message_def(checker, msgid, msg) 466 msg = build_message_def(checker, msgid, msg)
403 print msg.format_help(checkerref=False) 467 print(msg.format_help(checkerref=False))
404 print 468 print("")
405 if reports: 469 if reports:
406 title = ('%sreports' % prefix).capitalize() 470 title = 'Reports'
407 print title 471 print(title)
408 print '~' * len(title) 472 print('~' * len(title))
409 for report in reports: 473 for report in reports:
410 print ':%s: %s' % report[:2] 474 print(':%s: %s' % report[:2])
411 print 475 print("")
412 print 476 print("")
413 477
414 478
415 class FileState(object): 479 class FileState(object):
416 """Hold internal state specific to the currently analyzed file""" 480 """Hold internal state specific to the currently analyzed file"""
417 481
418 def __init__(self, modname=None): 482 def __init__(self, modname=None):
419 self.base_name = modname 483 self.base_name = modname
420 self._module_msgs_state = {} 484 self._module_msgs_state = {}
421 self._raw_module_msgs_state = {} 485 self._raw_module_msgs_state = {}
422 self._ignored_msgs = {} 486 self._ignored_msgs = collections.defaultdict(set)
423 self._suppression_mapping = {} 487 self._suppression_mapping = {}
424 488
425 def collect_block_lines(self, msgs_store, module_node): 489 def collect_block_lines(self, msgs_store, module_node):
426 """Walk the AST to collect block level options line numbers.""" 490 """Walk the AST to collect block level options line numbers."""
427 for msg, lines in self._module_msgs_state.iteritems(): 491 for msg, lines in six.iteritems(self._module_msgs_state):
428 self._raw_module_msgs_state[msg] = lines.copy() 492 self._raw_module_msgs_state[msg] = lines.copy()
429 orig_state = self._module_msgs_state.copy() 493 orig_state = self._module_msgs_state.copy()
430 self._module_msgs_state = {} 494 self._module_msgs_state = {}
431 self._suppression_mapping = {} 495 self._suppression_mapping = {}
432 self._collect_block_lines(msgs_store, module_node, orig_state) 496 self._collect_block_lines(msgs_store, module_node, orig_state)
433 497
434 def _collect_block_lines(self, msgs_store, node, msg_state): 498 def _collect_block_lines(self, msgs_store, node, msg_state):
435 """Recursivly walk (depth first) AST to collect block level options line 499 """Recursivly walk (depth first) AST to collect block level options line
436 numbers. 500 numbers.
437 """ 501 """
(...skipping 13 matching lines...) Expand all
451 # 6. print self.bla 515 # 6. print self.bla
452 # 516 #
453 # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6 517 # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6
454 # 518 #
455 # this is necessary to disable locally messages applying to class / 519 # this is necessary to disable locally messages applying to class /
456 # function using their fromlineno 520 # function using their fromlineno
457 if isinstance(node, (nodes.Module, nodes.Class, nodes.Function)) and nod e.body: 521 if isinstance(node, (nodes.Module, nodes.Class, nodes.Function)) and nod e.body:
458 firstchildlineno = node.body[0].fromlineno 522 firstchildlineno = node.body[0].fromlineno
459 else: 523 else:
460 firstchildlineno = last 524 firstchildlineno = last
461 for msgid, lines in msg_state.iteritems(): 525 for msgid, lines in six.iteritems(msg_state):
462 for lineno, state in lines.items(): 526 for lineno, state in list(lines.items()):
463 original_lineno = lineno 527 original_lineno = lineno
464 if first <= lineno <= last: 528 if first <= lineno <= last:
465 # Set state for all lines for this block, if the 529 # Set state for all lines for this block, if the
466 # warning is applied to nodes. 530 # warning is applied to nodes.
467 if msgs_store.check_message_id(msgid).scope == WarningScope .NODE: 531 if msgs_store.check_message_id(msgid).scope == WarningScope .NODE:
468 if lineno > firstchildlineno: 532 if lineno > firstchildlineno:
469 state = True 533 state = True
470 first_, last_ = node.block_range(lineno) 534 first_, last_ = node.block_range(lineno)
471 else: 535 else:
472 first_ = lineno 536 first_ = lineno
473 last_ = last 537 last_ = last
474 for line in xrange(first_, last_+1): 538 for line in range(first_, last_+1):
475 # do not override existing entries 539 # do not override existing entries
476 if not line in self._module_msgs_state.get(msgid, ()): 540 if not line in self._module_msgs_state.get(msgid, ()):
477 if line in lines: # state change in the same block 541 if line in lines: # state change in the same block
478 state = lines[line] 542 state = lines[line]
479 original_lineno = line 543 original_lineno = line
480 if not state: 544 if not state:
481 self._suppression_mapping[(msgid, line)] = origi nal_lineno 545 self._suppression_mapping[(msgid, line)] = origi nal_lineno
482 try: 546 try:
483 self._module_msgs_state[msgid][line] = state 547 self._module_msgs_state[msgid][line] = state
484 except KeyError: 548 except KeyError:
485 self._module_msgs_state[msgid] = {line: state} 549 self._module_msgs_state[msgid] = {line: state}
486 del lines[lineno] 550 del lines[lineno]
487 551
488 def set_msg_status(self, msg, line, status): 552 def set_msg_status(self, msg, line, status):
489 """Set status (enabled/disable) for a given message at a given line""" 553 """Set status (enabled/disable) for a given message at a given line"""
490 assert line > 0 554 assert line > 0
491 try: 555 try:
492 self._module_msgs_state[msg.msgid][line] = status 556 self._module_msgs_state[msg.msgid][line] = status
493 except KeyError: 557 except KeyError:
494 self._module_msgs_state[msg.msgid] = {line: status} 558 self._module_msgs_state[msg.msgid] = {line: status}
495 559
496 def handle_ignored_message(self, msgid, line, node, args): 560 def handle_ignored_message(self, state_scope, msgid, line,
561 node, args, confidence): # pylint: disable=unused -argument
497 """Report an ignored message. 562 """Report an ignored message.
498 563
499 state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG, 564 state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG,
500 depending on whether the message was disabled locally in the module, 565 depending on whether the message was disabled locally in the module,
501 or globally. The other arguments are the same as for add_message. 566 or globally. The other arguments are the same as for add_message.
502 """ 567 """
503 state_scope = self._message_state_scope(msgid, line)
504 if state_scope == MSG_STATE_SCOPE_MODULE: 568 if state_scope == MSG_STATE_SCOPE_MODULE:
505 try: 569 try:
506 orig_line = self._suppression_mapping[(msgid, line)] 570 orig_line = self._suppression_mapping[(msgid, line)]
507 self._ignored_msgs.setdefault((msgid, orig_line), set()).add(lin e) 571 self._ignored_msgs[(msgid, orig_line)].add(line)
508 except KeyError: 572 except KeyError:
509 pass 573 pass
510 574
511 def _message_state_scope(self, msgid, line=None):
512 """Returns the scope at which a message was enabled/disabled."""
513 try:
514 if line in self._module_msgs_state[msgid]:
515 return MSG_STATE_SCOPE_MODULE
516 except KeyError:
517 return MSG_STATE_SCOPE_CONFIG
518
519 def iter_spurious_suppression_messages(self, msgs_store): 575 def iter_spurious_suppression_messages(self, msgs_store):
520 for warning, lines in self._raw_module_msgs_state.iteritems(): 576 for warning, lines in six.iteritems(self._raw_module_msgs_state):
521 for line, enable in lines.iteritems(): 577 for line, enable in six.iteritems(lines):
522 if not enable and (warning, line) not in self._ignored_msgs: 578 if not enable and (warning, line) not in self._ignored_msgs:
523 yield 'useless-suppression', line, \ 579 yield 'useless-suppression', line, \
524 (msgs_store.get_msg_display_string(warning),) 580 (msgs_store.get_msg_display_string(warning),)
525 # don't use iteritems here, _ignored_msgs may be modified by add_message 581 # don't use iteritems here, _ignored_msgs may be modified by add_message
526 for (warning, from_), lines in self._ignored_msgs.items(): 582 for (warning, from_), lines in list(self._ignored_msgs.items()):
527 for line in lines: 583 for line in lines:
528 yield 'suppressed-message', line, \ 584 yield 'suppressed-message', line, \
529 (msgs_store.get_msg_display_string(warning), from_) 585 (msgs_store.get_msg_display_string(warning), from_)
530 586
531 587
532 class MessagesStore(object): 588 class MessagesStore(object):
533 """The messages store knows information about every possible message but has 589 """The messages store knows information about every possible message but has
534 no particular state during analysis. 590 no particular state during analysis.
535 """ 591 """
536 592
537 def __init__(self): 593 def __init__(self):
538 # Primary registry for all active messages (i.e. all messages 594 # Primary registry for all active messages (i.e. all messages
539 # that can be emitted by pylint for the underlying Python 595 # that can be emitted by pylint for the underlying Python
540 # version). It contains the 1:1 mapping from symbolic names 596 # version). It contains the 1:1 mapping from symbolic names
541 # to message definition objects. 597 # to message definition objects.
542 self._messages = {} 598 self._messages = {}
543 # Maps alternative names (numeric IDs, deprecated names) to 599 # Maps alternative names (numeric IDs, deprecated names) to
544 # message definitions. May contain several names for each definition 600 # message definitions. May contain several names for each definition
545 # object. 601 # object.
546 self._alternative_names = {} 602 self._alternative_names = {}
547 self._msgs_by_category = {} 603 self._msgs_by_category = collections.defaultdict(list)
548 604
549 @property 605 @property
550 def messages(self): 606 def messages(self):
551 """The list of all active messages.""" 607 """The list of all active messages."""
552 return self._messages.itervalues() 608 return six.itervalues(self._messages)
553 609
554 def add_renamed_message(self, old_id, old_symbol, new_symbol): 610 def add_renamed_message(self, old_id, old_symbol, new_symbol):
555 """Register the old ID and symbol for a warning that was renamed. 611 """Register the old ID and symbol for a warning that was renamed.
556 612
557 This allows users to keep using the old ID/symbol in suppressions. 613 This allows users to keep using the old ID/symbol in suppressions.
558 """ 614 """
559 msg = self.check_message_id(new_symbol) 615 msg = self.check_message_id(new_symbol)
560 msg.old_names.append((old_id, old_symbol)) 616 msg.old_names.append((old_id, old_symbol))
561 self._alternative_names[old_id] = msg 617 self._alternative_names[old_id] = msg
562 self._alternative_names[old_symbol] = msg 618 self._alternative_names[old_symbol] = msg
563 619
564 def register_messages(self, checker): 620 def register_messages(self, checker):
565 """register a dictionary of messages 621 """register a dictionary of messages
566 622
567 Keys are message ids, values are a 2-uple with the message type and the 623 Keys are message ids, values are a 2-uple with the message type and the
568 message itself 624 message itself
569 625
570 message ids should be a string of len 4, where the two first characters 626 message ids should be a string of len 4, where the two first characters
571 are the checker id and the two last the message id in this checker 627 are the checker id and the two last the message id in this checker
572 """ 628 """
573 chkid = None 629 chkid = None
574 for msgid, msg_tuple in checker.msgs.iteritems(): 630 for msgid, msg_tuple in six.iteritems(checker.msgs):
575 msg = build_message_def(checker, msgid, msg_tuple) 631 msg = build_message_def(checker, msgid, msg_tuple)
576 assert msg.symbol not in self._messages, \ 632 assert msg.symbol not in self._messages, \
577 'Message symbol %r is already defined' % msg.symbol 633 'Message symbol %r is already defined' % msg.symbol
578 # avoid duplicate / malformed ids 634 # avoid duplicate / malformed ids
579 assert msg.msgid not in self._alternative_names, \ 635 assert msg.msgid not in self._alternative_names, \
580 'Message id %r is already defined' % msgid 636 'Message id %r is already defined' % msgid
581 assert chkid is None or chkid == msg.msgid[1:3], \ 637 assert chkid is None or chkid == msg.msgid[1:3], \
582 'Inconsistent checker part in message id %r' % msgid 638 'Inconsistent checker part in message id %r' % msgid
583 chkid = msg.msgid[1:3] 639 chkid = msg.msgid[1:3]
584 self._messages[msg.symbol] = msg 640 self._messages[msg.symbol] = msg
585 self._alternative_names[msg.msgid] = msg 641 self._alternative_names[msg.msgid] = msg
586 for old_id, old_symbol in msg.old_names: 642 for old_id, old_symbol in msg.old_names:
587 self._alternative_names[old_id] = msg 643 self._alternative_names[old_id] = msg
588 self._alternative_names[old_symbol] = msg 644 self._alternative_names[old_symbol] = msg
589 self._msgs_by_category.setdefault(msg.msgid[0], []).append(msg.msgid ) 645 self._msgs_by_category[msg.msgid[0]].append(msg.msgid)
590 646
591 def check_message_id(self, msgid): 647 def check_message_id(self, msgid):
592 """returns the Message object for this message. 648 """returns the Message object for this message.
593 649
594 msgid may be either a numeric or symbolic id. 650 msgid may be either a numeric or symbolic id.
595 651
596 Raises UnknownMessage if the message id is not defined. 652 Raises UnknownMessage if the message id is not defined.
597 """ 653 """
598 if msgid[1:].isdigit(): 654 if msgid[1:].isdigit():
599 msgid = msgid.upper() 655 msgid = msgid.upper()
600 for source in (self._alternative_names, self._messages): 656 for source in (self._alternative_names, self._messages):
601 try: 657 try:
602 return source[msgid] 658 return source[msgid]
603 except KeyError: 659 except KeyError:
604 pass 660 pass
605 raise UnknownMessage('No such message id %s' % msgid) 661 raise UnknownMessage('No such message id %s' % msgid)
606 662
607 def get_msg_display_string(self, msgid): 663 def get_msg_display_string(self, msgid):
608 """Generates a user-consumable representation of a message. 664 """Generates a user-consumable representation of a message.
609 665
610 Can be just the message ID or the ID and the symbol. 666 Can be just the message ID or the ID and the symbol.
611 """ 667 """
612 return repr(self.check_message_id(msgid).symbol) 668 return repr(self.check_message_id(msgid).symbol)
613 669
614 def help_message(self, msgids): 670 def help_message(self, msgids):
615 """display help messages for the given message identifiers""" 671 """display help messages for the given message identifiers"""
616 for msgid in msgids: 672 for msgid in msgids:
617 try: 673 try:
618 print self.check_message_id(msgid).format_help(checkerref=True) 674 print(self.check_message_id(msgid).format_help(checkerref=True))
619 print 675 print("")
620 except UnknownMessage, ex: 676 except UnknownMessage as ex:
621 print ex 677 print(ex)
622 print 678 print("")
623 continue 679 continue
624 680
625 def list_messages(self): 681 def list_messages(self):
626 """output full messages list documentation in ReST format""" 682 """output full messages list documentation in ReST format"""
627 msgs = sorted(self._messages.itervalues(), key=lambda msg: msg.msgid) 683 msgs = sorted(six.itervalues(self._messages), key=lambda msg: msg.msgid)
628 for msg in msgs: 684 for msg in msgs:
629 if not msg.may_be_emitted(): 685 if not msg.may_be_emitted():
630 continue 686 continue
631 print msg.format_help(checkerref=False) 687 print(msg.format_help(checkerref=False))
632 print 688 print("")
633 689
634 690
635 class ReportsHandlerMixIn(object): 691 class ReportsHandlerMixIn(object):
636 """a mix-in class containing all the reports and stats manipulation 692 """a mix-in class containing all the reports and stats manipulation
637 related methods for the main lint class 693 related methods for the main lint class
638 """ 694 """
639 def __init__(self): 695 def __init__(self):
640 self._reports = {} 696 self._reports = collections.defaultdict(list)
641 self._reports_state = {} 697 self._reports_state = {}
642 698
699 def report_order(self):
700 """ Return a list of reports, sorted in the order
701 in which they must be called.
702 """
703 return list(self._reports)
704
643 def register_report(self, reportid, r_title, r_cb, checker): 705 def register_report(self, reportid, r_title, r_cb, checker):
644 """register a report 706 """register a report
645 707
646 reportid is the unique identifier for the report 708 reportid is the unique identifier for the report
647 r_title the report's title 709 r_title the report's title
648 r_cb the method to call to make the report 710 r_cb the method to call to make the report
649 checker is the checker defining the report 711 checker is the checker defining the report
650 """ 712 """
651 reportid = reportid.upper() 713 reportid = reportid.upper()
652 self._reports.setdefault(checker, []).append((reportid, r_title, r_cb)) 714 self._reports[checker].append((reportid, r_title, r_cb))
653 715
654 def enable_report(self, reportid): 716 def enable_report(self, reportid):
655 """disable the report of the given id""" 717 """disable the report of the given id"""
656 reportid = reportid.upper() 718 reportid = reportid.upper()
657 self._reports_state[reportid] = True 719 self._reports_state[reportid] = True
658 720
659 def disable_report(self, reportid): 721 def disable_report(self, reportid):
660 """disable the report of the given id""" 722 """disable the report of the given id"""
661 reportid = reportid.upper() 723 reportid = reportid.upper()
662 self._reports_state[reportid] = False 724 self._reports_state[reportid] = False
663 725
664 def report_is_enabled(self, reportid): 726 def report_is_enabled(self, reportid):
665 """return true if the report associated to the given identifier is 727 """return true if the report associated to the given identifier is
666 enabled 728 enabled
667 """ 729 """
668 return self._reports_state.get(reportid, True) 730 return self._reports_state.get(reportid, True)
669 731
670 def make_reports(self, stats, old_stats): 732 def make_reports(self, stats, old_stats):
671 """render registered reports""" 733 """render registered reports"""
672 sect = Section('Report', 734 sect = Section('Report',
673 '%s statements analysed.'% (self.stats['statement'])) 735 '%s statements analysed.'% (self.stats['statement']))
674 for checker in self._reports: 736 for checker in self.report_order():
675 for reportid, r_title, r_cb in self._reports[checker]: 737 for reportid, r_title, r_cb in self._reports[checker]:
676 if not self.report_is_enabled(reportid): 738 if not self.report_is_enabled(reportid):
677 continue 739 continue
678 report_sect = Section(r_title) 740 report_sect = Section(r_title)
679 try: 741 try:
680 r_cb(report_sect, stats, old_stats) 742 r_cb(report_sect, stats, old_stats)
681 except EmptyReport: 743 except EmptyReport:
682 continue 744 continue
683 report_sect.report_id = reportid 745 report_sect.report_id = reportid
684 sect.append(report_sect) 746 sect.append(report_sect)
685 return sect 747 return sect
686 748
687 def add_stats(self, **kwargs): 749 def add_stats(self, **kwargs):
688 """add some stats entries to the statistic dictionary 750 """add some stats entries to the statistic dictionary
689 raise an AssertionError if there is a key conflict 751 raise an AssertionError if there is a key conflict
690 """ 752 """
691 for key, value in kwargs.iteritems(): 753 for key, value in six.iteritems(kwargs):
692 if key[-1] == '_': 754 if key[-1] == '_':
693 key = key[:-1] 755 key = key[:-1]
694 assert key not in self.stats 756 assert key not in self.stats
695 self.stats[key] = value 757 self.stats[key] = value
696 return self.stats 758 return self.stats
697 759
698 760
699 def expand_modules(files_or_modules, black_list): 761 def expand_modules(files_or_modules, black_list):
700 """take a list of files/modules/packages and return the list of tuple 762 """take a list of files/modules/packages and return the list of tuple
701 (file, module name) which have to be actually checked 763 (file, module name) which have to be actually checked
(...skipping 12 matching lines...) Expand all
714 else: 776 else:
715 filepath = something 777 filepath = something
716 else: 778 else:
717 # suppose it's a module or package 779 # suppose it's a module or package
718 modname = something 780 modname = something
719 try: 781 try:
720 filepath = file_from_modpath(modname.split('.')) 782 filepath = file_from_modpath(modname.split('.'))
721 if filepath is None: 783 if filepath is None:
722 errors.append({'key' : 'ignored-builtin-module', 'mod': modn ame}) 784 errors.append({'key' : 'ignored-builtin-module', 'mod': modn ame})
723 continue 785 continue
724 except (ImportError, SyntaxError), ex: 786 except (ImportError, SyntaxError) as ex:
725 # FIXME p3k : the SyntaxError is a Python bug and should be 787 # FIXME p3k : the SyntaxError is a Python bug and should be
726 # removed as soon as possible http://bugs.python.org/issue10588 788 # removed as soon as possible http://bugs.python.org/issue10588
727 errors.append({'key': 'fatal', 'mod': modname, 'ex': ex}) 789 errors.append({'key': 'fatal', 'mod': modname, 'ex': ex})
728 continue 790 continue
729 filepath = normpath(filepath) 791 filepath = normpath(filepath)
730 result.append({'path': filepath, 'name': modname, 'isarg': True, 792 result.append({'path': filepath, 'name': modname, 'isarg': True,
731 'basepath': filepath, 'basename': modname}) 793 'basepath': filepath, 'basename': modname})
732 if not (modname.endswith('.__init__') or modname == '__init__') \ 794 if not (modname.endswith('.__init__') or modname == '__init__') \
733 and '__init__.py' in filepath: 795 and '__init__.py' in filepath:
734 for subfilepath in get_module_files(dirname(filepath), black_list): 796 for subfilepath in get_module_files(dirname(filepath), black_list):
735 if filepath == subfilepath: 797 if filepath == subfilepath:
736 continue 798 continue
737 submodname = '.'.join(modpath_from_file(subfilepath)) 799 submodname = '.'.join(modpath_from_file(subfilepath))
738 result.append({'path': subfilepath, 'name': submodname, 800 result.append({'path': subfilepath, 'name': submodname,
739 'isarg': False, 801 'isarg': False,
740 'basepath': filepath, 'basename': modname}) 802 'basepath': filepath, 'basename': modname})
741 return result, errors 803 return result, errors
742 804
743 805
744 class PyLintASTWalker(object): 806 class PyLintASTWalker(object):
745 807
746 def __init__(self, linter): 808 def __init__(self, linter):
747 # callbacks per node types 809 # callbacks per node types
748 self.nbstatements = 1 810 self.nbstatements = 1
749 self.visit_events = {} 811 self.visit_events = collections.defaultdict(list)
750 self.leave_events = {} 812 self.leave_events = collections.defaultdict(list)
751 self.linter = linter 813 self.linter = linter
752 814
753 def _is_method_enabled(self, method): 815 def _is_method_enabled(self, method):
754 if not hasattr(method, 'checks_msgs'): 816 if not hasattr(method, 'checks_msgs'):
755 return True 817 return True
756 for msg_desc in method.checks_msgs: 818 for msg_desc in method.checks_msgs:
757 if self.linter.is_message_enabled(msg_desc): 819 if self.linter.is_message_enabled(msg_desc):
758 return True 820 return True
759 return False 821 return False
760 822
761 def add_checker(self, checker): 823 def add_checker(self, checker):
762 """walk to the checker's dir and collect visit and leave methods""" 824 """walk to the checker's dir and collect visit and leave methods"""
763 # XXX : should be possible to merge needed_checkers and add_checker 825 # XXX : should be possible to merge needed_checkers and add_checker
764 vcids = set() 826 vcids = set()
765 lcids = set() 827 lcids = set()
766 visits = self.visit_events 828 visits = self.visit_events
767 leaves = self.leave_events 829 leaves = self.leave_events
768 for member in dir(checker): 830 for member in dir(checker):
769 cid = member[6:] 831 cid = member[6:]
770 if cid == 'default': 832 if cid == 'default':
771 continue 833 continue
772 if member.startswith('visit_'): 834 if member.startswith('visit_'):
773 v_meth = getattr(checker, member) 835 v_meth = getattr(checker, member)
774 # don't use visit_methods with no activated message: 836 # don't use visit_methods with no activated message:
775 if self._is_method_enabled(v_meth): 837 if self._is_method_enabled(v_meth):
776 visits.setdefault(cid, []).append(v_meth) 838 visits[cid].append(v_meth)
777 vcids.add(cid) 839 vcids.add(cid)
778 elif member.startswith('leave_'): 840 elif member.startswith('leave_'):
779 l_meth = getattr(checker, member) 841 l_meth = getattr(checker, member)
780 # don't use leave_methods with no activated message: 842 # don't use leave_methods with no activated message:
781 if self._is_method_enabled(l_meth): 843 if self._is_method_enabled(l_meth):
782 leaves.setdefault(cid, []).append(l_meth) 844 leaves[cid].append(l_meth)
783 lcids.add(cid) 845 lcids.add(cid)
784 visit_default = getattr(checker, 'visit_default', None) 846 visit_default = getattr(checker, 'visit_default', None)
785 if visit_default: 847 if visit_default:
786 for cls in nodes.ALL_NODE_CLASSES: 848 for cls in nodes.ALL_NODE_CLASSES:
787 cid = cls.__name__.lower() 849 cid = cls.__name__.lower()
788 if cid not in vcids: 850 if cid not in vcids:
789 visits.setdefault(cid, []).append(visit_default) 851 visits[cid].append(visit_default)
790 # for now we have no "leave_default" method in Pylint 852 # for now we have no "leave_default" method in Pylint
791 853
792 def walk(self, astroid): 854 def walk(self, astroid):
793 """call visit events of astroid checkers for the given node, recurse on 855 """call visit events of astroid checkers for the given node, recurse on
794 its children, then leave events. 856 its children, then leave events.
795 """ 857 """
796 cid = astroid.__class__.__name__.lower() 858 cid = astroid.__class__.__name__.lower()
797 if astroid.is_statement: 859 if astroid.is_statement:
798 self.nbstatements += 1 860 self.nbstatements += 1
799 # generate events for this node on each checker 861 # generate events for this node on each checker
(...skipping 17 matching lines...) Expand all
817 base, extension = splitext(filename) 879 base, extension = splitext(filename)
818 if base in imported or base == '__pycache__': 880 if base in imported or base == '__pycache__':
819 continue 881 continue
820 if extension in PY_EXTS and base != '__init__' or ( 882 if extension in PY_EXTS and base != '__init__' or (
821 not extension and isdir(join(directory, base))): 883 not extension and isdir(join(directory, base))):
822 try: 884 try:
823 module = load_module_from_file(join(directory, filename)) 885 module = load_module_from_file(join(directory, filename))
824 except ValueError: 886 except ValueError:
825 # empty module name (usually emacs auto-save files) 887 # empty module name (usually emacs auto-save files)
826 continue 888 continue
827 except ImportError, exc: 889 except ImportError as exc:
828 print >> sys.stderr, "Problem importing module %s: %s" % (filena me, exc) 890 print("Problem importing module %s: %s" % (filename, exc),
891 file=sys.stderr)
829 else: 892 else:
830 if hasattr(module, 'register'): 893 if hasattr(module, 'register'):
831 module.register(linter) 894 module.register(linter)
832 imported[base] = 1 895 imported[base] = 1
833 896
834 def get_global_option(checker, option, default=None): 897 def get_global_option(checker, option, default=None):
835 """ Retrieve an option defined by the given *checker* or 898 """ Retrieve an option defined by the given *checker* or
836 by all known option providers. 899 by all known option providers.
837 900
838 It will look in the list of all options providers 901 It will look in the list of all options providers
839 until the given *option* will be found. 902 until the given *option* will be found.
840 If the option wasn't found, the *default* value will be returned. 903 If the option wasn't found, the *default* value will be returned.
841 """ 904 """
842 # First, try in the given checker's config. 905 # First, try in the given checker's config.
843 # After that, look in the options providers. 906 # After that, look in the options providers.
844 907
845 try: 908 try:
846 return getattr(checker.config, option.replace("-", "_")) 909 return getattr(checker.config, option.replace("-", "_"))
847 except AttributeError: 910 except AttributeError:
848 pass 911 pass
849 for provider in checker.linter.options_providers: 912 for provider in checker.linter.options_providers:
850 for options in provider.options: 913 for options in provider.options:
851 if options[0] == option: 914 if options[0] == option:
852 return getattr(provider.config, option.replace("-", "_")) 915 return getattr(provider.config, option.replace("-", "_"))
853 return default 916 return default
OLDNEW
« no previous file with comments | « third_party/pylint/testutils.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698