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

Side by Side Diff: third_party/pylint/checkers/imports.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/checkers/format.py ('k') | third_party/pylint/checkers/logging.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-2013 LOGILAB S.A. (Paris, FRANCE). 1 # Copyright (c) 2003-2013 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 """imports checkers for Python code""" 16 """imports checkers for Python code"""
17 17
18 import sys 18 import sys
19 from collections import defaultdict
20
21 import six
22 from six.moves import map # pylint: disable=redefined-builtin
19 23
20 from logilab.common.graph import get_cycles, DotBackend 24 from logilab.common.graph import get_cycles, DotBackend
21 from logilab.common.ureports import VerbatimText, Paragraph 25 from logilab.common.ureports import VerbatimText, Paragraph
22 26
23 import astroid 27 import astroid
24 from astroid import are_exclusive 28 from astroid import are_exclusive
25 from astroid.modutils import get_module_part, is_standard_module 29 from astroid.modutils import get_module_part, is_standard_module
26 30
27 from pylint.interfaces import IAstroidChecker 31 from pylint.interfaces import IAstroidChecker
28 from pylint.utils import EmptyReport 32 from pylint.utils import EmptyReport
29 from pylint.checkers import BaseChecker 33 from pylint.checkers import BaseChecker
30 from pylint.checkers.utils import check_messages 34 from pylint.checkers.utils import check_messages, is_import_error
31 35
36 def _except_import_error(node):
37 """
38 Check if the try-except node has an ImportError handler.
39 Return True if an ImportError handler was infered, False otherwise.
40 """
41 if not isinstance(node, astroid.TryExcept):
42 return
43 return any(map(is_import_error, node.handlers))
32 44
33 def get_first_import(node, context, name, base, level): 45 def get_first_import(node, context, name, base, level):
34 """return the node where [base.]<name> is imported or None if not found 46 """return the node where [base.]<name> is imported or None if not found
35 """ 47 """
36 fullname = '%s.%s' % (base, name) if base else name 48 fullname = '%s.%s' % (base, name) if base else name
37 49
38 first = None 50 first = None
39 found = False 51 found = False
40 for first in context.body: 52 for first in context.body:
41 if first is node: 53 if first is node:
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 lines.append(repr_tree_defs(sub, sub_indent_str)) 103 lines.append(repr_tree_defs(sub, sub_indent_str))
92 return '\n'.join(lines) 104 return '\n'.join(lines)
93 105
94 106
95 def dependencies_graph(filename, dep_info): 107 def dependencies_graph(filename, dep_info):
96 """write dependencies as a dot (graphviz) file 108 """write dependencies as a dot (graphviz) file
97 """ 109 """
98 done = {} 110 done = {}
99 printer = DotBackend(filename[:-4], rankdir='LR') 111 printer = DotBackend(filename[:-4], rankdir='LR')
100 printer.emit('URL="." node[shape="box"]') 112 printer.emit('URL="." node[shape="box"]')
101 for modname, dependencies in sorted(dep_info.iteritems()): 113 for modname, dependencies in sorted(six.iteritems(dep_info)):
102 done[modname] = 1 114 done[modname] = 1
103 printer.emit_node(modname) 115 printer.emit_node(modname)
104 for modname in dependencies: 116 for modname in dependencies:
105 if modname not in done: 117 if modname not in done:
106 done[modname] = 1 118 done[modname] = 1
107 printer.emit_node(modname) 119 printer.emit_node(modname)
108 for depmodname, dependencies in sorted(dep_info.iteritems()): 120 for depmodname, dependencies in sorted(six.iteritems(dep_info)):
109 for modname in dependencies: 121 for modname in dependencies:
110 printer.emit_edge(modname, depmodname) 122 printer.emit_edge(modname, depmodname)
111 printer.generate(filename) 123 printer.generate(filename)
112 124
113 125
114 def make_graph(filename, dep_info, sect, gtype): 126 def make_graph(filename, dep_info, sect, gtype):
115 """generate a dependencies graph and add some information about it in the 127 """generate a dependencies graph and add some information about it in the
116 report's section 128 report's section
117 """ 129 """
118 dependencies_graph(filename, dep_info) 130 dependencies_graph(filename, dep_info)
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 self.report_external_dependencies), 225 self.report_external_dependencies),
214 ('RP0402', 'Modules dependencies graph', 226 ('RP0402', 'Modules dependencies graph',
215 self.report_dependencies_graph), 227 self.report_dependencies_graph),
216 ) 228 )
217 229
218 def open(self): 230 def open(self):
219 """called before visiting project (i.e set of modules)""" 231 """called before visiting project (i.e set of modules)"""
220 self.linter.add_stats(dependencies={}) 232 self.linter.add_stats(dependencies={})
221 self.linter.add_stats(cycles=[]) 233 self.linter.add_stats(cycles=[])
222 self.stats = self.linter.stats 234 self.stats = self.linter.stats
223 self.import_graph = {} 235 self.import_graph = defaultdict(set)
224 236
225 def close(self): 237 def close(self):
226 """called before visiting project (i.e set of modules)""" 238 """called before visiting project (i.e set of modules)"""
227 # don't try to compute cycles if the associated message is disabled 239 # don't try to compute cycles if the associated message is disabled
228 if self.linter.is_message_enabled('cyclic-import'): 240 if self.linter.is_message_enabled('cyclic-import'):
229 for cycle in get_cycles(self.import_graph): 241 vertices = list(self.import_graph)
242 for cycle in get_cycles(self.import_graph, vertices=vertices):
230 self.add_message('cyclic-import', args=' -> '.join(cycle)) 243 self.add_message('cyclic-import', args=' -> '.join(cycle))
231 244
232 def visit_import(self, node): 245 def visit_import(self, node):
233 """triggered when an import statement is seen""" 246 """triggered when an import statement is seen"""
234 modnode = node.root() 247 modnode = node.root()
235 for name, _ in node.names: 248 for name, _ in node.names:
236 importedmodnode = self.get_imported_module(modnode, node, name) 249 importedmodnode = self.get_imported_module(node, name)
237 if importedmodnode is None: 250 if importedmodnode is None:
238 continue 251 continue
239 self._check_relative_import(modnode, node, importedmodnode, name) 252 self._check_relative_import(modnode, node, importedmodnode, name)
240 self._add_imported_module(node, importedmodnode.name) 253 self._add_imported_module(node, importedmodnode.name)
241 self._check_deprecated_module(node, name) 254 self._check_deprecated_module(node, name)
242 self._check_reimport(node, name) 255 self._check_reimport(node, name)
243 256
244 # TODO This appears to be the list of all messages of the checker... 257 # TODO This appears to be the list of all messages of the checker...
245 # @check_messages('W0410', 'W0401', 'W0403', 'W0402', 'W0404', 'W0406', 'F04 01') 258 # @check_messages('W0410', 'W0401', 'W0403', 'W0402', 'W0404', 'W0406', 'F04 01')
246 @check_messages(*(MSGS.keys())) 259 @check_messages(*(MSGS.keys()))
247 def visit_from(self, node): 260 def visit_from(self, node):
248 """triggered when a from statement is seen""" 261 """triggered when a from statement is seen"""
249 basename = node.modname 262 basename = node.modname
250 if basename == '__future__': 263 if basename == '__future__':
251 # check if this is the first non-docstring statement in the module 264 # check if this is the first non-docstring statement in the module
252 prev = node.previous_sibling() 265 prev = node.previous_sibling()
253 if prev: 266 if prev:
254 # consecutive future statements are possible 267 # consecutive future statements are possible
255 if not (isinstance(prev, astroid.From) 268 if not (isinstance(prev, astroid.From)
256 and prev.modname == '__future__'): 269 and prev.modname == '__future__'):
257 self.add_message('misplaced-future', node=node) 270 self.add_message('misplaced-future', node=node)
258 return 271 return
259 for name, _ in node.names: 272 for name, _ in node.names:
260 if name == '*': 273 if name == '*':
261 self.add_message('wildcard-import', args=basename, node=node) 274 self.add_message('wildcard-import', args=basename, node=node)
262 modnode = node.root() 275 modnode = node.root()
263 importedmodnode = self.get_imported_module(modnode, node, basename) 276 importedmodnode = self.get_imported_module(node, basename)
264 if importedmodnode is None: 277 if importedmodnode is None:
265 return 278 return
266 self._check_relative_import(modnode, node, importedmodnode, basename) 279 self._check_relative_import(modnode, node, importedmodnode, basename)
267 self._check_deprecated_module(node, basename) 280 self._check_deprecated_module(node, basename)
268 for name, _ in node.names: 281 for name, _ in node.names:
269 if name != '*': 282 if name != '*':
270 self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name)) 283 self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name))
271 self._check_reimport(node, name, basename, node.level) 284 self._check_reimport(node, name, basename, node.level)
272 285
273 def get_imported_module(self, modnode, importnode, modname): 286 def get_imported_module(self, importnode, modname):
274 try: 287 try:
275 return importnode.do_import_module(modname) 288 return importnode.do_import_module(modname)
276 except astroid.InferenceError, ex: 289 except astroid.InferenceError as ex:
277 if str(ex) != modname: 290 if str(ex) != modname:
278 args = '%r (%s)' % (modname, ex) 291 args = '%r (%s)' % (modname, ex)
279 else: 292 else:
280 args = repr(modname) 293 args = repr(modname)
281 self.add_message("import-error", args=args, node=importnode) 294 if not _except_import_error(importnode.parent):
295 self.add_message("import-error", args=args, node=importnode)
282 296
283 def _check_relative_import(self, modnode, importnode, importedmodnode, 297 def _check_relative_import(self, modnode, importnode, importedmodnode,
284 importedasname): 298 importedasname):
285 """check relative import. node is either an Import or From node, modname 299 """check relative import. node is either an Import or From node, modname
286 the imported module name. 300 the imported module name.
287 """ 301 """
288 if not self.linter.is_message_enabled('relative-import'): 302 if not self.linter.is_message_enabled('relative-import'):
289 return 303 return
290 if importedmodnode.file is None: 304 if importedmodnode.file is None:
291 return False # built-in module 305 return False # built-in module
292 if modnode is importedmodnode: 306 if modnode is importedmodnode:
293 return False # module importing itself 307 return False # module importing itself
294 if modnode.absolute_import_activated() or getattr(importnode, 'level', N one): 308 if modnode.absolute_import_activated() or getattr(importnode, 'level', N one):
295 return False 309 return False
296 if importedmodnode.name != importedasname: 310 if importedmodnode.name != importedasname:
297 # this must be a relative import... 311 # this must be a relative import...
298 self.add_message('relative-import', args=(importedasname, importedmo dnode.name), 312 self.add_message('relative-import',
313 args=(importedasname, importedmodnode.name),
299 node=importnode) 314 node=importnode)
300 315
301 def _add_imported_module(self, node, importedmodname): 316 def _add_imported_module(self, node, importedmodname):
302 """notify an imported module, used to analyze dependencies""" 317 """notify an imported module, used to analyze dependencies"""
303 try: 318 try:
304 importedmodname = get_module_part(importedmodname) 319 importedmodname = get_module_part(importedmodname)
305 except ImportError: 320 except ImportError:
306 pass 321 pass
307 context_name = node.root().name 322 context_name = node.root().name
308 if context_name == importedmodname: 323 if context_name == importedmodname:
309 # module importing itself ! 324 # module importing itself !
310 self.add_message('import-self', node=node) 325 self.add_message('import-self', node=node)
311 elif not is_standard_module(importedmodname): 326 elif not is_standard_module(importedmodname):
312 # handle dependencies 327 # handle dependencies
313 importedmodnames = self.stats['dependencies'].setdefault( 328 importedmodnames = self.stats['dependencies'].setdefault(
314 importedmodname, set()) 329 importedmodname, set())
315 if not context_name in importedmodnames: 330 if not context_name in importedmodnames:
316 importedmodnames.add(context_name) 331 importedmodnames.add(context_name)
317 # update import graph 332 # update import graph
318 mgraph = self.import_graph.setdefault(context_name, set()) 333 mgraph = self.import_graph[context_name]
319 if not importedmodname in mgraph: 334 if importedmodname not in mgraph:
320 mgraph.add(importedmodname) 335 mgraph.add(importedmodname)
321 336
322 def _check_deprecated_module(self, node, mod_path): 337 def _check_deprecated_module(self, node, mod_path):
323 """check if the module is deprecated""" 338 """check if the module is deprecated"""
324 for mod_name in self.config.deprecated_modules: 339 for mod_name in self.config.deprecated_modules:
325 if mod_path == mod_name or mod_path.startswith(mod_name + '.'): 340 if mod_path == mod_name or mod_path.startswith(mod_name + '.'):
326 self.add_message('deprecated-module', node=node, args=mod_path) 341 self.add_message('deprecated-module', node=node, args=mod_path)
327 342
328 def _check_reimport(self, node, name, basename=None, level=None): 343 def _check_reimport(self, node, name, basename=None, level=None):
329 """check if the import is necessary (i.e. not already done)""" 344 """check if the import is necessary (i.e. not already done)"""
330 if not self.linter.is_message_enabled('reimported'): 345 if not self.linter.is_message_enabled('reimported'):
331 return 346 return
332 frame = node.frame() 347 frame = node.frame()
333 root = node.root() 348 root = node.root()
334 contexts = [(frame, level)] 349 contexts = [(frame, level)]
335 if root is not frame: 350 if root is not frame:
336 contexts.append((root, None)) 351 contexts.append((root, None))
337 for context, level in contexts: 352 for context, level in contexts:
338 first = get_first_import(node, context, name, basename, level) 353 first = get_first_import(node, context, name, basename, level)
339 if first is not None: 354 if first is not None:
340 self.add_message('reimported', node=node, 355 self.add_message('reimported', node=node,
341 args=(name, first.fromlineno)) 356 args=(name, first.fromlineno))
342 357
343 358
344 def report_external_dependencies(self, sect, _, dummy): 359 def report_external_dependencies(self, sect, _, dummy):
345 """return a verbatim layout for displaying dependencies""" 360 """return a verbatim layout for displaying dependencies"""
346 dep_info = make_tree_defs(self._external_dependencies_info().iteritems() ) 361 dep_info = make_tree_defs(six.iteritems(self._external_dependencies_info ()))
347 if not dep_info: 362 if not dep_info:
348 raise EmptyReport() 363 raise EmptyReport()
349 tree_str = repr_tree_defs(dep_info) 364 tree_str = repr_tree_defs(dep_info)
350 sect.append(VerbatimText(tree_str)) 365 sect.append(VerbatimText(tree_str))
351 366
352 def report_dependencies_graph(self, sect, _, dummy): 367 def report_dependencies_graph(self, sect, _, dummy):
353 """write dependencies as a dot (graphviz) file""" 368 """write dependencies as a dot (graphviz) file"""
354 dep_info = self.stats['dependencies'] 369 dep_info = self.stats['dependencies']
355 if not dep_info or not (self.config.import_graph 370 if not dep_info or not (self.config.import_graph
356 or self.config.ext_import_graph 371 or self.config.ext_import_graph
(...skipping 11 matching lines...) Expand all
368 make_graph(filename, self._internal_dependencies_info(), 383 make_graph(filename, self._internal_dependencies_info(),
369 sect, 'internal ') 384 sect, 'internal ')
370 385
371 def _external_dependencies_info(self): 386 def _external_dependencies_info(self):
372 """return cached external dependencies information or build and 387 """return cached external dependencies information or build and
373 cache them 388 cache them
374 """ 389 """
375 if self.__ext_dep_info is None: 390 if self.__ext_dep_info is None:
376 package = self.linter.current_name 391 package = self.linter.current_name
377 self.__ext_dep_info = result = {} 392 self.__ext_dep_info = result = {}
378 for importee, importers in self.stats['dependencies'].iteritems(): 393 for importee, importers in six.iteritems(self.stats['dependencies']) :
379 if not importee.startswith(package): 394 if not importee.startswith(package):
380 result[importee] = importers 395 result[importee] = importers
381 return self.__ext_dep_info 396 return self.__ext_dep_info
382 397
383 def _internal_dependencies_info(self): 398 def _internal_dependencies_info(self):
384 """return cached internal dependencies information or build and 399 """return cached internal dependencies information or build and
385 cache them 400 cache them
386 """ 401 """
387 if self.__int_dep_info is None: 402 if self.__int_dep_info is None:
388 package = self.linter.current_name 403 package = self.linter.current_name
389 self.__int_dep_info = result = {} 404 self.__int_dep_info = result = {}
390 for importee, importers in self.stats['dependencies'].iteritems(): 405 for importee, importers in six.iteritems(self.stats['dependencies']) :
391 if importee.startswith(package): 406 if importee.startswith(package):
392 result[importee] = importers 407 result[importee] = importers
393 return self.__int_dep_info 408 return self.__int_dep_info
394 409
395 410
396 def register(linter): 411 def register(linter):
397 """required method to auto register this checker """ 412 """required method to auto register this checker """
398 linter.register_checker(ImportsChecker(linter)) 413 linter.register_checker(ImportsChecker(linter))
OLDNEW
« no previous file with comments | « third_party/pylint/checkers/format.py ('k') | third_party/pylint/checkers/logging.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698