Index: third_party/pylint/checkers/imports.py |
diff --git a/third_party/pylint/checkers/imports.py b/third_party/pylint/checkers/imports.py |
index 5964a266049103b2f713b6e149191a03f43b0e5f..7e6a4f880c9cc9b4bcb4ae5135df31315b426775 100644 |
--- a/third_party/pylint/checkers/imports.py |
+++ b/third_party/pylint/checkers/imports.py |
@@ -1,4 +1,4 @@ |
-# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE). |
+# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE). |
# http://www.logilab.fr/ -- mailto:contact@logilab.fr |
# |
# This program is free software; you can redistribute it and/or modify it under |
@@ -12,51 +12,55 @@ |
# |
# You should have received a copy of the GNU General Public License along with |
# this program; if not, write to the Free Software Foundation, Inc., |
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
"""imports checkers for Python code""" |
-import sys |
- |
from logilab.common.graph import get_cycles, DotBackend |
+from logilab.common.modutils import is_standard_module |
from logilab.common.ureports import VerbatimText, Paragraph |
-import astroid |
-from astroid import are_exclusive |
-from astroid.modutils import get_module_part, is_standard_module |
+from logilab import astng |
+from logilab.astng import are_exclusive |
-from pylint.interfaces import IAstroidChecker |
-from pylint.utils import EmptyReport |
-from pylint.checkers import BaseChecker |
-from pylint.checkers.utils import check_messages |
+from pylint.interfaces import IASTNGChecker |
+from pylint.checkers import BaseChecker, EmptyReport |
def get_first_import(node, context, name, base, level): |
"""return the node where [base.]<name> is imported or None if not found |
""" |
- fullname = '%s.%s' % (base, name) if base else name |
- |
first = None |
found = False |
- for first in context.body: |
- if first is node: |
- continue |
- if first.scope() is node.scope() and first.fromlineno > node.fromlineno: |
- continue |
- if isinstance(first, astroid.Import): |
- if any(fullname == iname[0] for iname in first.names): |
+ for first in context.values(): |
+ if isinstance(first, astng.Import): |
+ if name in [iname[0] for iname in first.names]: |
found = True |
break |
- elif isinstance(first, astroid.From): |
- if level == first.level and any( |
- fullname == '%s.%s' % (first.modname, iname[0]) |
- for iname in first.names): |
+ elif isinstance(first, astng.From): |
+ if base == first.modname and level == first.level and \ |
+ name in [iname[0] for iname in first.names]: |
found = True |
break |
- if found and not are_exclusive(first, node): |
+ if found and first is not node and not are_exclusive(first, node): |
return first |
# utilities to represents import dependencies as tree and dot graph ########### |
+def filter_dependencies_info(dep_info, package_dir, mode='external'): |
+ """filter external or internal dependencies from dep_info (return a |
+ new dictionary containing the filtered modules only) |
+ """ |
+ if mode == 'external': |
+ filter_func = lambda x: not is_standard_module(x, (package_dir,)) |
+ else: |
+ assert mode == 'internal' |
+ filter_func = lambda x: is_standard_module(x, (package_dir,)) |
+ result = {} |
+ for importee, importers in dep_info.items(): |
+ if filter_func(importee): |
+ result[importee] = importers |
+ return result |
+ |
def make_tree_defs(mod_files_list): |
"""get a list of 2-uple (module, list_of_files_which_import_this_module), |
it will return a dictionary to represent this as a tree |
@@ -82,7 +86,7 @@ def repr_tree_defs(data, indent_str=None): |
lines.append('%s %s' % (mod, files)) |
sub_indent_str = ' ' |
else: |
- lines.append(r'%s\-%s %s' % (indent_str, mod, files)) |
+ lines.append('%s\-%s %s' % (indent_str, mod, files)) |
if i == len(nodes)-1: |
sub_indent_str = '%s ' % indent_str |
else: |
@@ -96,16 +100,16 @@ def dependencies_graph(filename, dep_info): |
"""write dependencies as a dot (graphviz) file |
""" |
done = {} |
- printer = DotBackend(filename[:-4], rankdir='LR') |
+ printer = DotBackend(filename[:-4], rankdir = "LR") |
printer.emit('URL="." node[shape="box"]') |
- for modname, dependencies in sorted(dep_info.iteritems()): |
+ for modname, dependencies in dep_info.items(): |
done[modname] = 1 |
printer.emit_node(modname) |
for modname in dependencies: |
if modname not in done: |
done[modname] = 1 |
printer.emit_node(modname) |
- for depmodname, dependencies in sorted(dep_info.iteritems()): |
+ for depmodname, dependencies in dep_info.items(): |
for modname in dependencies: |
printer.emit_edge(modname, depmodname) |
printer.generate(filename) |
@@ -124,36 +128,26 @@ def make_graph(filename, dep_info, sect, gtype): |
MSGS = { |
'F0401': ('Unable to import %s', |
- 'import-error', |
'Used when pylint has been unable to import a module.'), |
'R0401': ('Cyclic import (%s)', |
- 'cyclic-import', |
'Used when a cyclic import between two or more modules is \ |
detected.'), |
'W0401': ('Wildcard import %s', |
- 'wildcard-import', |
'Used when `from module import *` is detected.'), |
'W0402': ('Uses of a deprecated module %r', |
- 'deprecated-module', |
'Used a module marked as deprecated is imported.'), |
'W0403': ('Relative import %r, should be %r', |
- 'relative-import', |
- 'Used when an import relative to the package directory is ' |
- 'detected.', |
- {'maxversion': (3, 0)}), |
+ 'Used when an import relative to the package directory is \ |
+ detected.'), |
'W0404': ('Reimport %r (imported line %s)', |
- 'reimported', |
'Used when a module is reimported multiple times.'), |
'W0406': ('Module import itself', |
- 'import-self', |
'Used when a module is importing itself.'), |
'W0410': ('__future__ import is not the first non docstring statement', |
- 'misplaced-future', |
'Python 2.5 and greater require __future__ import to be the \ |
- first non docstring statement in the module.', |
- {'maxversion': (3, 0)}), |
+ first non docstring statement in the module.'), |
} |
class ImportsChecker(BaseChecker): |
@@ -164,45 +158,43 @@ class ImportsChecker(BaseChecker): |
* uses of deprecated modules |
""" |
- __implements__ = IAstroidChecker |
+ __implements__ = IASTNGChecker |
name = 'imports' |
msgs = MSGS |
priority = -2 |
- if sys.version_info < (3,): |
- deprecated_modules = ('regsub', 'TERMIOS', 'Bastion', 'rexec') |
- else: |
- deprecated_modules = ('stringprep', 'optparse') |
options = (('deprecated-modules', |
- {'default' : deprecated_modules, |
+ {'default' : ('regsub', 'string', 'TERMIOS', |
+ 'Bastion', 'rexec'), |
'type' : 'csv', |
'metavar' : '<modules>', |
'help' : 'Deprecated modules which should not be used, \ |
separated by a comma'} |
- ), |
+ ), |
('import-graph', |
{'default' : '', |
'type' : 'string', |
'metavar' : '<file.dot>', |
'help' : 'Create a graph of every (i.e. internal and \ |
external) dependencies in the given file (report RP0402 must not be disabled)'} |
- ), |
+ ), |
('ext-import-graph', |
{'default' : '', |
'type' : 'string', |
'metavar' : '<file.dot>', |
'help' : 'Create a graph of external dependencies in the \ |
given file (report RP0402 must not be disabled)'} |
- ), |
+ ), |
('int-import-graph', |
{'default' : '', |
'type' : 'string', |
'metavar' : '<file.dot>', |
'help' : 'Create a graph of internal dependencies in the \ |
given file (report RP0402 must not be disabled)'} |
- ), |
- ) |
+ ), |
+ |
+ ) |
def __init__(self, linter=None): |
BaseChecker.__init__(self, linter) |
@@ -213,7 +205,7 @@ given file (report RP0402 must not be disabled)'} |
self.report_external_dependencies), |
('RP0402', 'Modules dependencies graph', |
self.report_dependencies_graph), |
- ) |
+ ) |
def open(self): |
"""called before visiting project (i.e set of modules)""" |
@@ -225,9 +217,9 @@ given file (report RP0402 must not be disabled)'} |
def close(self): |
"""called before visiting project (i.e set of modules)""" |
# don't try to compute cycles if the associated message is disabled |
- if self.linter.is_message_enabled('cyclic-import'): |
+ if self.linter.is_message_enabled('R0401'): |
for cycle in get_cycles(self.import_graph): |
- self.add_message('cyclic-import', args=' -> '.join(cycle)) |
+ self.add_message('R0401', args=' -> '.join(cycle)) |
def visit_import(self, node): |
"""triggered when an import statement is seen""" |
@@ -241,9 +233,7 @@ given file (report RP0402 must not be disabled)'} |
self._check_deprecated_module(node, name) |
self._check_reimport(node, name) |
- # TODO This appears to be the list of all messages of the checker... |
- # @check_messages('W0410', 'W0401', 'W0403', 'W0402', 'W0404', 'W0406', 'F0401') |
- @check_messages(*(MSGS.keys())) |
+ |
def visit_from(self, node): |
"""triggered when a from statement is seen""" |
basename = node.modname |
@@ -252,13 +242,10 @@ given file (report RP0402 must not be disabled)'} |
prev = node.previous_sibling() |
if prev: |
# consecutive future statements are possible |
- if not (isinstance(prev, astroid.From) |
- and prev.modname == '__future__'): |
- self.add_message('misplaced-future', node=node) |
+ if not (isinstance(prev, astng.From) |
+ and prev.modname == '__future__'): |
+ self.add_message('W0410', node=node) |
return |
- for name, _ in node.names: |
- if name == '*': |
- self.add_message('wildcard-import', args=basename, node=node) |
modnode = node.root() |
importedmodnode = self.get_imported_module(modnode, node, basename) |
if importedmodnode is None: |
@@ -266,26 +253,28 @@ given file (report RP0402 must not be disabled)'} |
self._check_relative_import(modnode, node, importedmodnode, basename) |
self._check_deprecated_module(node, basename) |
for name, _ in node.names: |
- if name != '*': |
- self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name)) |
- self._check_reimport(node, name, basename, node.level) |
+ if name == '*': |
+ self.add_message('W0401', args=basename, node=node) |
+ continue |
+ self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name)) |
+ self._check_reimport(node, name, basename, node.level) |
def get_imported_module(self, modnode, importnode, modname): |
try: |
return importnode.do_import_module(modname) |
- except astroid.InferenceError, ex: |
+ except astng.InferenceError, ex: |
if str(ex) != modname: |
args = '%r (%s)' % (modname, ex) |
else: |
args = repr(modname) |
- self.add_message("import-error", args=args, node=importnode) |
+ self.add_message("F0401", args=args, node=importnode) |
def _check_relative_import(self, modnode, importnode, importedmodnode, |
importedasname): |
"""check relative import. node is either an Import or From node, modname |
the imported module name. |
""" |
- if not self.linter.is_message_enabled('relative-import'): |
+ if 'W0403' not in self.active_msgs: |
return |
if importedmodnode.file is None: |
return False # built-in module |
@@ -295,55 +284,52 @@ given file (report RP0402 must not be disabled)'} |
return False |
if importedmodnode.name != importedasname: |
# this must be a relative import... |
- self.add_message('relative-import', args=(importedasname, importedmodnode.name), |
+ self.add_message('W0403', args=(importedasname, importedmodnode.name), |
node=importnode) |
def _add_imported_module(self, node, importedmodname): |
"""notify an imported module, used to analyze dependencies""" |
- try: |
- importedmodname = get_module_part(importedmodname) |
- except ImportError: |
- pass |
context_name = node.root().name |
if context_name == importedmodname: |
# module importing itself ! |
- self.add_message('import-self', node=node) |
+ self.add_message('W0406', node=node) |
elif not is_standard_module(importedmodname): |
# handle dependencies |
importedmodnames = self.stats['dependencies'].setdefault( |
importedmodname, set()) |
if not context_name in importedmodnames: |
importedmodnames.add(context_name) |
- # update import graph |
- mgraph = self.import_graph.setdefault(context_name, set()) |
- if not importedmodname in mgraph: |
- mgraph.add(importedmodname) |
+ if is_standard_module( importedmodname, (self.package_dir(),) ): |
+ # update import graph |
+ mgraph = self.import_graph.setdefault(context_name, set()) |
+ if not importedmodname in mgraph: |
+ mgraph.add(importedmodname) |
def _check_deprecated_module(self, node, mod_path): |
"""check if the module is deprecated""" |
for mod_name in self.config.deprecated_modules: |
if mod_path == mod_name or mod_path.startswith(mod_name + '.'): |
- self.add_message('deprecated-module', node=node, args=mod_path) |
+ self.add_message('W0402', node=node, args=mod_path) |
- def _check_reimport(self, node, name, basename=None, level=None): |
+ def _check_reimport(self, node, name, basename=None, level=0): |
"""check if the import is necessary (i.e. not already done)""" |
- if not self.linter.is_message_enabled('reimported'): |
+ if 'W0404' not in self.active_msgs: |
return |
frame = node.frame() |
root = node.root() |
contexts = [(frame, level)] |
if root is not frame: |
- contexts.append((root, None)) |
+ contexts.append((root, 0)) |
for context, level in contexts: |
first = get_first_import(node, context, name, basename, level) |
if first is not None: |
- self.add_message('reimported', node=node, |
+ self.add_message('W0404', node=node, |
args=(name, first.fromlineno)) |
def report_external_dependencies(self, sect, _, dummy): |
"""return a verbatim layout for displaying dependencies""" |
- dep_info = make_tree_defs(self._external_dependencies_info().iteritems()) |
+ dep_info = make_tree_defs(self._external_dependencies_info().items()) |
if not dep_info: |
raise EmptyReport() |
tree_str = repr_tree_defs(dep_info) |
@@ -373,11 +359,8 @@ given file (report RP0402 must not be disabled)'} |
cache them |
""" |
if self.__ext_dep_info is None: |
- package = self.linter.current_name |
- self.__ext_dep_info = result = {} |
- for importee, importers in self.stats['dependencies'].iteritems(): |
- if not importee.startswith(package): |
- result[importee] = importers |
+ self.__ext_dep_info = filter_dependencies_info( |
+ self.stats['dependencies'], self.package_dir(), 'external') |
return self.__ext_dep_info |
def _internal_dependencies_info(self): |
@@ -385,11 +368,8 @@ given file (report RP0402 must not be disabled)'} |
cache them |
""" |
if self.__int_dep_info is None: |
- package = self.linter.current_name |
- self.__int_dep_info = result = {} |
- for importee, importers in self.stats['dependencies'].iteritems(): |
- if importee.startswith(package): |
- result[importee] = importers |
+ self.__int_dep_info = filter_dependencies_info( |
+ self.stats['dependencies'], self.package_dir(), 'internal') |
return self.__int_dep_info |