16
16
"""variables checkers for Python code
19
20
from copy import copy
21
from logilab.common.compat import enumerate
22
22
from logilab import astng
23
from logilab.astng import are_exclusive, builtin_lookup
23
from logilab.astng import are_exclusive, builtin_lookup, ASTNGBuildingException
25
25
from pylint.interfaces import IASTNGChecker
26
26
from pylint.checkers import BaseChecker
27
from pylint.checkers.utils import PYMETHODS, is_ancestor_name, is_builtin, \
28
is_defined_before, is_error, is_func_default, is_func_decorator, assign_parent
27
from pylint.checkers.utils import (PYMETHODS, is_ancestor_name, is_builtin,
28
is_defined_before, is_error, is_func_default, is_func_decorator,
29
assign_parent, check_messages)
31
31
def overridden_method(klass, name):
32
32
"""get overridden method if any"""
111
111
("dummy-variables-rgx",
112
112
{'default': ('_|dummy'),
113
113
'type' :'regexp', 'metavar' : '<regexp>',
114
'help' : 'A regular expression matching names used \
115
for dummy variables (i.e. not used).'}),
114
'help' : 'A regular expression matching the beginning of \
115
the name of dummy variables (i.e. not used).'}),
116
116
("additional-builtins",
117
117
{'default': (), 'type' : 'csv',
118
118
'metavar' : '<comma separated list>',
182
183
def visit_genexpr(self, node):
183
184
"""visit genexpr: update consumption analysis variable
185
self._to_consume.append((copy(node.locals), {}, 'genexpr'))
186
self._to_consume.append((copy(node.locals), {}, 'comprehension'))
187
188
def leave_genexpr(self, _):
188
189
"""leave genexpr: update consumption analysis variable
190
191
# do not check for not used locals here
191
192
self._to_consume.pop()
194
def visit_dictcomp(self, node):
195
"""visit dictcomp: update consumption analysis variable
197
self._to_consume.append((copy(node.locals), {}, 'comprehension'))
199
def leave_dictcomp(self, _):
200
"""leave dictcomp: update consumption analysis variable
202
# do not check for not used locals here
203
self._to_consume.pop()
205
def visit_setcomp(self, node):
206
"""visit setcomp: update consumption analysis variable
208
self._to_consume.append((copy(node.locals), {}, 'comprehension'))
210
def leave_setcomp(self, _):
211
"""leave setcomp: update consumption analysis variable
213
# do not check for not used locals here
214
self._to_consume.pop()
193
216
def visit_function(self, node):
194
217
"""visit function: update consumption analysis variable and check locals
219
self._to_consume.append((copy(node.locals), {}, 'function'))
220
self._vars.append({})
221
if not set(('W0621', 'W0622')) & self.active_msgs:
196
223
globs = node.root().globals
197
224
for name, stmt in node.items():
198
if globs.has_key(name) and not isinstance(stmt, astng.Global):
225
if name in globs and not isinstance(stmt, astng.Global):
199
226
line = globs[name][0].lineno
200
227
self.add_message('W0621', args=(name, line), node=stmt)
201
228
elif is_builtin(name):
202
229
# do not print Redefining builtin for additional builtins
203
230
self.add_message('W0622', args=name, node=stmt)
204
self._to_consume.append((copy(node.locals), {}, 'function'))
205
self._vars.append({})
207
232
def leave_function(self, node):
208
233
"""leave function: check function's locals are consumed"""
209
234
not_consumed = self._to_consume.pop()[0]
210
235
self._vars.pop(0)
236
if not set(('W0612', 'W0613')) & self.active_msgs:
211
238
# don't check arguments of function which are only raising an exception
212
239
if is_error(node):
350
381
# scope, ignore it. This prevents to access this scope instead of
351
382
# the globals one in function members when there are some common
352
383
# names. The only exception is when the starting scope is a
353
# genexpr and its direct outer scope is a class
384
# comprehension and its direct outer scope is a class
354
385
if scope_type == 'class' and i != start_index and not (
355
base_scope_type == 'genexpr' and i == start_index-1):
386
base_scope_type == 'comprehension' and i == start_index-1):
356
387
# XXX find a way to handle class scope in a smoother way
358
389
# the name has already been consumed, only check it's not a loop
359
390
# variable used outside the loop
360
if consumed.has_key(name):
361
392
self._loopvar_name(node, name)
363
394
# mark the name as consumed if it's defined in this scope
366
397
consumed[name] = to_consume[name]
400
# checks for use before assignment
401
defnode = assign_parent(to_consume[name][0])
402
if defnode is not None:
403
defstmt = defnode.statement()
404
defframe = defstmt.frame()
406
if not frame is defframe:
408
elif defframe.parent is None:
409
# we are at the module level, check the name is not
410
# defined in builtins
411
if name in defframe.scope_attrs or builtin_lookup(name)[1]:
414
# we are in a local scope, check the name is not
415
# defined in global or builtin scope
416
if defframe.root().lookup(name)[1]:
419
and stmt.fromlineno <= defstmt.fromlineno
420
and not is_defined_before(node)
421
and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))):
422
if defstmt is stmt and isinstance(node, (astng.DelName,
424
self.add_message('E0602', args=name, node=node)
425
elif self._to_consume[-1][-1] != 'lambda':
426
# E0601 may *not* occurs in lambda scope
427
self.add_message('E0601', args=name, node=node)
428
if not isinstance(node, astng.AssName): # Aug AssName
370
# checks for use before assignment
371
defnode = assign_parent(to_consume[name][0])
372
if defnode is not None:
373
defstmt = defnode.statement()
374
defframe = defstmt.frame()
376
if not frame is defframe:
378
elif defframe.parent is None:
379
# we are at the module level, check the name is not
380
# defined in builtins
381
if name in defframe.scope_attrs or builtin_lookup(name)[1]:
384
# we are in a local scope, check the name is not
385
# defined in global or builtin scope
386
if defframe.root().lookup(name)[1]:
389
and stmt.fromlineno <= defstmt.fromlineno
390
and not is_defined_before(node)
391
and not are_exclusive(stmt, defstmt, ('NameError', 'Exception', 'BaseException'))):
392
if defstmt is stmt and isinstance(node, (astng.DelName,
394
self.add_message('E0602', args=name, node=node)
395
elif self._to_consume[-1][-1] != 'lambda':
396
# E0601 may *not* occurs in lambda scope
397
self.add_message('E0601', args=name, node=node)
398
if not isinstance(node, astng.AssName): # Aug AssName
402
# check it's not a loop variable used outside the loop
403
self._loopvar_name(node, name)
432
# check it's not a loop variable used outside the loop
433
self._loopvar_name(node, name)
406
436
# we have not found the name, if it isn't a builtin, that's an
407
437
# undefined name !
420
451
self._check_module_attrs(node, module, parts[1:])
453
@check_messages('E0611')
422
454
def visit_from(self, node):
423
455
"""check modules attribute accesses"""
424
456
name_parts = node.modname.split('.')
426
458
module = node.root().import_module(name_parts[0])
427
except KeyboardInterrupt:
459
except ASTNGBuildingException:
461
except Exception, exc:
462
print 'Unhandled exception in VariablesChecker:', exc
431
464
module = self._check_module_attrs(node, module, name_parts[1:])
437
470
self._check_module_attrs(node, module, name.split('.'))
439
## def leave_getattr(self, node):
440
## """check modules attribute accesses
442
## this function is a "leave_" because when parsing 'a.b.c'
443
## we want to check the innermost expression first.
445
## if isinstance(node.expr, astng.Name):
447
## module = node.expr.infer().next()
448
## except astng.InferenceError:
450
## if not isinstance(module, astng.Module):
451
## # Not a module, don't check
453
## elif self._checking_mod_attr is not None:
454
## module = self._checking_mod_attr
457
## self._checking_mod_attr = self._check_module_attrs(node, module,
460
## def leave_default(self, node):
461
## """by default, reset the _checking_mod_attr attribute"""
462
## self._checking_mod_attr = None
464
472
def _check_module_attrs(self, node, module, module_names):
465
473
"""check that module_names (list of string) are accessible through the
502
class VariablesChecker3k(VariablesChecker):
503
'''Modified variables checker for 3k'''
504
# listcomp have now also their scope
506
def visit_listcomp(self, node):
507
"""visit dictcomp: update consumption analysis variable
509
self._to_consume.append((copy(node.locals), {}, 'comprehension'))
511
def leave_listcomp(self, _):
512
"""leave dictcomp: update consumption analysis variable
514
# do not check for not used locals here
515
self._to_consume.pop()
517
if sys.version_info >= (3, 0):
518
VariablesChecker = VariablesChecker3k
496
521
def register(linter):