126
123
# the import checker itself ###################################################
129
'F0401': ('Unable to import %r (%s)' ,
126
'F0401': ('Unable to import %r' ,
130
127
'Used when pylint has been unable to import a module.'),
131
128
'R0401': ('Cyclic import (%s)',
132
129
'Used when a cyclic import between two or more modules is \
135
132
'W0401': ('Wildcard import %s',
136
133
'Used when `from module import *` is detected.'),
137
134
'W0402': ('Uses of a deprecated module %r',
138
135
'Used a module marked as deprecated is imported.'),
139
'W0403': ('Relative import %r',
136
'W0403': ('Relative import %r, should be %r',
140
137
'Used when an import relative to the package directory is \
142
139
'W0404': ('Reimport %r (imported line %s)',
143
140
'Used when a module is reimported multiple times.'),
144
141
'W0406': ('Module import itself',
145
142
'Used when a module is importing itself.'),
147
144
'W0410': ('__future__ import is not the first non docstring statement',
148
145
'Python 2.5 and greater require __future__ import to be the \
149
146
first non docstring statement in the module.'),
152
149
class ImportsChecker(BaseChecker):
154
* external modules dependencies
155
* relative / wildcard imports
151
* external modules dependencies
152
* relative / wildcard imports
157
154
* uses of deprecated modules
160
157
__implements__ = IASTNGChecker
166
163
options = (('deprecated-modules',
167
164
{'default' : ('regsub','string', 'TERMIOS',
168
165
'Bastion', 'rexec'),
205
202
('R0402', 'Modules dependencies graph',
206
203
self.report_dependencies_graph),
210
207
"""called before visiting project (i.e set of modules)"""
211
208
self.linter.add_stats(dependencies={})
212
209
self.linter.add_stats(cycles=[])
213
210
self.stats = self.linter.stats
214
211
self.import_graph = {}
217
214
"""called before visiting project (i.e set of modules)"""
218
215
# don't try to compute cycles if the associated message is disabled
219
216
if self.linter.is_message_enabled('R0401'):
220
217
for cycle in get_cycles(self.import_graph):
221
218
self.add_message('R0401', args=' -> '.join(cycle))
223
220
def visit_import(self, node):
224
221
"""triggered when an import statement is seen"""
222
modnode = node.root()
225
223
for name, _ in node.names:
226
if self._module_not_exists(node, name):
224
importedmodnode = self.get_imported_module(modnode, node, name)
225
if importedmodnode is None:
228
self._check_deprecated(node, name)
229
relative = self._check_relative(node, name)
230
self._imported_module(node, name, relative)
227
self._check_relative_import(modnode, node, importedmodnode, name)
228
self._add_imported_module(node, importedmodnode.name)
229
self._check_deprecated_module(node, name)
232
230
self._check_reimport(node, name)
235
233
def visit_from(self, node):
236
234
"""triggered when a from statement is seen"""
237
235
basename = node.modname
238
if self._module_not_exists(node, basename):
240
236
if basename == '__future__':
241
237
# check if this is the first non-docstring statement in the module
242
238
prev = node.previous_sibling()
245
241
if not (isinstance(prev, astng.From)
246
242
and prev.modname == '__future__'):
247
243
self.add_message('W0410', node=node)
248
self._check_deprecated(node, basename)
250
if level > 0: # explicit relative import (leading dots)
253
relative = self._check_relative(node, basename)
245
modnode = node.root()
246
importedmodnode = self.get_imported_module(modnode, node, basename)
247
if importedmodnode is None:
249
self._check_relative_import(modnode, node, importedmodnode, basename)
250
self._check_deprecated_module(node, basename)
254
251
for name, _ in node.names:
256
253
self.add_message('W0401', args=basename, node=node)
259
self._check_reimport(node, name, basename, level)
260
# analyze dependencies
261
fullname = '.' * level + '%s.%s' % (basename, name)
262
fullname = get_module_part(fullname,context_file=node.root().file)
263
self._imported_module(node, fullname, relative)
265
def _module_not_exists(self, node, modname):
266
"""check if module exists and possibly add message"""
267
errors = expand_modules([modname], [])[1]
268
if not errors or is_relative(modname, node.root().file):
255
self._add_imported_module(node, '%s.%s' % (importedmodnode.name, name))
256
self._check_reimport(node, name, basename, node.level)
258
def get_imported_module(self, modnode, importnode, modname):
260
return importnode.do_import_module(modname)
261
except astng.InferenceError, ex:
262
if str(ex).startswith('module importing itself'): # XXX
265
self.add_message("F0401", args=modname, node=importnode)
268
def _check_relative_import(self, modnode, importnode, importedmodnode,
270
"""check relative import. node is either an Import or From node, modname
271
the imported module name.
273
if importedmodnode.file is None:
274
return False # built-in module
275
if modnode is importedmodnode:
276
return False # module importing itself
277
if modnode.absolute_import_activated() or getattr(importnode, 'level', None):
271
if error["key"] == "F0001":
272
args = (error["mod"], error["ex"])
273
self.add_message("F0401", args=args, node=node)
275
assert error["key"] == "F0003"
279
if importedmodnode.name != importedasname:
280
# this must be a relative import...
281
self.add_message('W0403', args=(importedasname, importedmodnode.name),
278
def _imported_module(self, node, mod_path, relative):
279
"""notify an imported module, used to analyze dependencies
284
def _add_imported_module(self, node, importedmodname):
285
"""notify an imported module, used to analyze dependencies"""
281
286
context_name = node.root().name
283
context_parts = context_name.split('.')
284
if mod_path.startswith('.'):
285
while mod_path.startswith('.'):
286
mod_path = mod_path[1:]
287
del context_parts[-1] # one level upwards
288
context_parts.append(mod_path)
290
context_parts[-1] = mod_path
291
mod_path = '.'.join(context_parts)
292
if context_name == mod_path:
287
if context_name == importedmodname:
293
288
# module importing itself !
294
289
self.add_message('W0406', node=node)
295
elif not is_standard_module(mod_path):
290
elif not is_standard_module(importedmodname):
296
291
# handle dependencies
297
mod_paths = self.stats['dependencies'].setdefault(mod_path, [])
298
if not context_name in mod_paths:
299
mod_paths.append(context_name)
300
if is_standard_module( mod_path, (self.package_dir(),) ):
292
importedmodnames = self.stats['dependencies'].setdefault(
293
importedmodname, set())
294
if not context_name in importedmodnames:
295
importedmodnames.add(context_name)
296
if is_standard_module( importedmodname, (self.package_dir(),) ):
301
297
# update import graph
302
mgraph = self.import_graph.setdefault(context_name, [])
303
if not mod_path in mgraph:
304
mgraph.append(mod_path)
298
mgraph = self.import_graph.setdefault(context_name, set())
299
if not importedmodname in mgraph:
300
mgraph.add(importedmodname)
306
def _check_relative(self, node, mod_path):
307
"""check relative import module"""
308
context_file = node.root().file
309
relative = is_relative(mod_path, context_file)
311
self.add_message('W0403', args=mod_path, node=node)
314
def _check_deprecated(self, node, mod_path):
302
def _check_deprecated_module(self, node, mod_path):
315
303
"""check if the module is deprecated"""
316
305
for mod_name in self.config.deprecated_modules:
317
306
if mod_path.startswith(mod_name) and \
318
307
(len(mod_path) == len(mod_name)
319
308
or mod_path[len(mod_name)] == '.'):
320
309
self.add_message('W0402', node=node, args=mod_path)
322
311
def _check_reimport(self, node, name, basename=None, level=0):
323
"""check if the import is necessary (i.e. not already done)
312
"""check if the import is necessary (i.e. not already done)"""
325
314
frame = node.frame()
326
315
first = get_first_import(frame, name, basename, level)
327
316
if isinstance(first, (astng.Import, astng.From)) and first is not node \