UtilitiesPython2/py2flakes/checker.py

branch
5_1_x
changeset 1537
acae49c8d343
parent 1510
e75ecf2bd9dd
child 1545
2738022ed226
equal deleted inserted replaced
1536:7d766369823d 1537:acae49c8d343
1 # -*- coding: utf-8 -*- 1 # -*- coding: utf-8 -*-
2 2
3 # Copyright (c) 2011 - 2012 Detlev Offenbach <detlev@die-offenbachs.de> 3 # Copyright (c) 2011 - 2012 Detlev Offenbach <detlev@die-offenbachs.de>
4 # 4 #
5 # Original (c) 2005-2008 Divmod, Inc. 5 # Original (c) 2005-2010 Divmod, Inc.
6 # 6 #
7 # This module is based on pyflakes for Python2 but was heavily hacked to 7 # This module is based on pyflakes for Python2 but was heavily hacked to
8 # work within eric5 8 # work within eric5
9 9
10 import __builtin__ 10 import __builtin__
11 import os.path 11 import os.path
12 import compiler 12 import _ast
13 from compiler import ast
14 13
15 from py2flakes import messages 14 from py2flakes import messages
15
16 # utility function to iterate over an AST node's children, adapted
17 # from Python 2.6's standard ast module
18 try:
19 import ast
20 iter_child_nodes = ast.iter_child_nodes
21 except (ImportError, AttributeError):
22 def iter_child_nodes(node, astcls=_ast.AST):
23 """
24 Yield all direct child nodes of *node*, that is, all fields that are nodes
25 and all items of fields that are lists of nodes.
26 """
27 for name in node._fields:
28 field = getattr(node, name, None)
29 if isinstance(field, astcls):
30 yield field
31 elif isinstance(field, list):
32 for item in field:
33 yield item
34
16 35
17 class Binding(object): 36 class Binding(object):
18 """ 37 """
19 Represents the binding of a value to a name. 38 Represents the binding of a value to a name.
20 39
88 def names(self): 107 def names(self):
89 """ 108 """
90 Return a list of the names referenced by this binding. 109 Return a list of the names referenced by this binding.
91 """ 110 """
92 names = [] 111 names = []
93 if isinstance(self.source, ast.List): 112 if isinstance(self.source, _ast.List):
94 for node in self.source.nodes: 113 for node in self.source.elts:
95 if isinstance(node, ast.Const): 114 if isinstance(node, _ast.Str):
96 names.append(node.value) 115 names.append(node.s)
97 return names 116 return names
98 117
99 class Scope(dict): 118 class Scope(dict):
100 """ 119 """
101 Class defining the scope base class. 120 Class defining the scope base class.
102 """ 121 """
103 importStarred = False # set to True when import * is found 122 importStarred = False # set to True when import * is found
104 123
105 def __repr__(self): 124 def __repr__(self):
106 return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self)) 125 return '<%s at 0x%x %s>' % (
126 self.__class__.__name__, id(self), dict.__repr__(self))
107 127
108 def __init__(self): 128 def __init__(self):
109 super(Scope, self).__init__() 129 super(Scope, self).__init__()
110 130
111 class ClassScope(Scope): 131 class ClassScope(Scope):
152 self.filename = filename 172 self.filename = filename
153 self.scopeStack = [ModuleScope()] 173 self.scopeStack = [ModuleScope()]
154 self.futuresAllowed = True 174 self.futuresAllowed = True
155 175
156 if isinstance(module, str): 176 if isinstance(module, str):
157 module = compiler.parse(module) 177 module = ast.parse(module, filename, "exec")
158 self.handleChildren(module) 178 self.handleChildren(module)
159 self._runDeferred(self._deferredFunctions) 179 self._runDeferred(self._deferredFunctions)
160 # Set _deferredFunctions to None so that deferFunction will fail 180 # Set _deferredFunctions to None so that deferFunction will fail
161 # noisily if called after we've run through the deferred functions. 181 # noisily if called after we've run through the deferred functions.
162 self._deferredFunctions = None 182 self._deferredFunctions = None
238 258
239 def report(self, messageClass, *args, **kwargs): 259 def report(self, messageClass, *args, **kwargs):
240 self.messages.append(messageClass(self.filename, *args, **kwargs)) 260 self.messages.append(messageClass(self.filename, *args, **kwargs))
241 261
242 def handleChildren(self, tree): 262 def handleChildren(self, tree):
243 for node in tree.getChildNodes(): 263 for node in iter_child_nodes(tree):
244 self.handleNode(node, tree) 264 self.handleNode(node, tree)
265
266 def isDocstring(self, node):
267 """
268 Determine if the given node is a docstring, as long as it is at the
269 correct place in the node tree.
270 """
271 return isinstance(node, _ast.Str) or \
272 (isinstance(node, _ast.Expr) and
273 isinstance(node.value, _ast.Str))
245 274
246 def handleNode(self, node, parent): 275 def handleNode(self, node, parent):
247 node.parent = parent 276 node.parent = parent
248 if self.traceTree: 277 if self.traceTree:
249 print ' ' * self.nodeDepth + node.__class__.__name__ 278 print ' ' * self.nodeDepth + node.__class__.__name__
250 self.nodeDepth += 1 279 self.nodeDepth += 1
280 if self.futuresAllowed and not \
281 (isinstance(node, _ast.ImportFrom) or self.isDocstring(node)):
282 self.futuresAllowed = False
251 nodeType = node.__class__.__name__.upper() 283 nodeType = node.__class__.__name__.upper()
252 if nodeType not in ('STMT', 'FROM'):
253 self.futuresAllowed = False
254 try: 284 try:
255 handler = getattr(self, nodeType) 285 handler = getattr(self, nodeType)
256 handler(node) 286 handler(node)
257 finally: 287 finally:
258 self.nodeDepth -= 1 288 self.nodeDepth -= 1
260 print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__ 290 print ' ' * self.nodeDepth + 'end ' + node.__class__.__name__
261 291
262 def ignore(self, node): 292 def ignore(self, node):
263 pass 293 pass
264 294
265 STMT = PRINT = PRINTNL = TUPLE = LIST = ASSTUPLE = ASSATTR = \ 295 # "stmt" type nodes
266 ASSLIST = GETATTR = SLICE = SLICEOBJ = IF = CALLFUNC = DISCARD = \ 296 RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \
267 RETURN = ADD = MOD = SUB = NOT = UNARYSUB = INVERT = ASSERT = COMPARE = \ 297 TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
268 SUBSCRIPT = AND = OR = TRYEXCEPT = RAISE = YIELD = DICT = LEFTSHIFT = \ 298
269 RIGHTSHIFT = KEYWORD = TRYFINALLY = WHILE = EXEC = MUL = DIV = POWER = \ 299 CONTINUE = BREAK = PASS = ignore
270 FLOORDIV = BITAND = BITOR = BITXOR = LISTCOMPFOR = LISTCOMPIF = \ 300
271 AUGASSIGN = BACKQUOTE = UNARYADD = GENEXPR = GENEXPRFOR = GENEXPRIF = \ 301 # "expr" type nodes
272 IFEXP = handleChildren 302 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \
273 303 CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren
274 CONST = PASS = CONTINUE = BREAK = ELLIPSIS = ignore 304
305 NUM = STR = ELLIPSIS = ignore
306
307 # "slice" type nodes
308 SLICE = EXTSLICE = INDEX = handleChildren
309
310 # expression contexts are node instances too, though being constants
311 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
312
313 # same for operators
314 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
315 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
316 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
317
318 # additional node types
319 COMPREHENSION = EXCEPTHANDLER = KEYWORD = handleChildren
275 320
276 def addBinding(self, lineno, value, reportRedef=True): 321 def addBinding(self, lineno, value, reportRedef=True):
277 ''' 322 '''
278 Called when a binding is altered. 323 Called when a binding is altered.
279 324
290 335
291 if not isinstance(self.scope, ClassScope): 336 if not isinstance(self.scope, ClassScope):
292 for scope in self.scopeStack[::-1]: 337 for scope in self.scopeStack[::-1]:
293 existing = scope.get(value.name) 338 existing = scope.get(value.name)
294 if (isinstance(existing, Importation) 339 if (isinstance(existing, Importation)
295 and not existing.used 340 and not existing.used
296 and (not isinstance(value, Importation) or value.fullName == existing.fullName) 341 and (not isinstance(value, Importation) or
297 and reportRedef): 342 value.fullName == existing.fullName)
343 and reportRedef):
298 344
299 self.report(messages.RedefinedWhileUnused, 345 self.report(messages.RedefinedWhileUnused,
300 lineno, value.name, scope[value.name].source.lineno) 346 lineno, value.name, scope[value.name].source.lineno)
301 347
302 if isinstance(value, UnBinding): 348 if isinstance(value, UnBinding):
305 except KeyError: 351 except KeyError:
306 self.report(messages.UndefinedName, lineno, value.name) 352 self.report(messages.UndefinedName, lineno, value.name)
307 else: 353 else:
308 self.scope[value.name] = value 354 self.scope[value.name] = value
309 355
310 def WITH(self, node):
311 """
312 Handle 'with' by checking the target of the statement (which can be an
313 identifier, a list or tuple of targets, an attribute, etc) for
314 undefined names and defining any it adds to the scope and by continuing
315 to process the suite within the statement.
316 """
317 # Check the "foo" part of a "with foo as bar" statement. Do this no
318 # matter what, since there's always a "foo" part.
319 self.handleNode(node.expr, node)
320
321 if node.vars is not None:
322 self.handleNode(node.vars, node)
323
324 self.handleChildren(node.body)
325
326
327 def GLOBAL(self, node): 356 def GLOBAL(self, node):
328 """ 357 """
329 Keep track of globals declarations. 358 Keep track of globals declarations.
330 """ 359 """
331 if isinstance(self.scope, FunctionScope): 360 if isinstance(self.scope, FunctionScope):
332 self.scope.globals.update(dict.fromkeys(node.names)) 361 self.scope.globals.update(dict.fromkeys(node.names))
333 362
334 def LISTCOMP(self, node): 363 def LISTCOMP(self, node):
335 for qual in node.quals: 364 # handle generators before element
336 self.handleNode(qual, node) 365 for gen in node.generators:
337 self.handleNode(node.expr, node) 366 self.handleNode(gen, node)
338 367 self.handleNode(node.elt, node)
339 GENEXPRINNER = LISTCOMP 368
369 GENERATOREXP = SETCOMP = LISTCOMP
370
371 # dictionary comprehensions; introduced in Python 2.7
372 def DICTCOMP(self, node):
373 for gen in node.generators:
374 self.handleNode(gen, node)
375 self.handleNode(node.key, node)
376 self.handleNode(node.value, node)
340 377
341 def FOR(self, node): 378 def FOR(self, node):
342 """ 379 """
343 Process bindings for loop variables. 380 Process bindings for loop variables.
344 """ 381 """
345 vars = [] 382 vars = []
346 def collectLoopVars(n): 383 def collectLoopVars(n):
347 if hasattr(n, 'name'): 384 if isinstance(n, _ast.Name):
348 vars.append(n.name) 385 vars.append(n.id)
386 elif isinstance(n, _ast.expr_context):
387 return
349 else: 388 else:
350 for c in n.getChildNodes(): 389 for c in iter_child_nodes(n):
351 collectLoopVars(c) 390 collectLoopVars(c)
352 391
353 collectLoopVars(node.assign) 392 collectLoopVars(node.target)
354 for varn in vars: 393 for varn in vars:
355 if (isinstance(self.scope.get(varn), Importation) 394 if (isinstance(self.scope.get(varn), Importation)
356 # unused ones will get an unused import warning 395 # unused ones will get an unused import warning
357 and self.scope[varn].used): 396 and self.scope[varn].used):
358 self.report(messages.ImportShadowedByLoopVar, 397 self.report(messages.ImportShadowedByLoopVar,
362 401
363 def NAME(self, node): 402 def NAME(self, node):
364 """ 403 """
365 Locate the name in locals / function / globals scopes. 404 Locate the name in locals / function / globals scopes.
366 """ 405 """
367 # try local scope 406 if isinstance(node.ctx, (_ast.Load, _ast.AugLoad)):
368 importStarred = self.scope.importStarred 407 # try local scope
369 try: 408 importStarred = self.scope.importStarred
370 self.scope[node.name].used = (self.scope, node.lineno)
371 except KeyError:
372 pass
373 else:
374 return
375
376 # try enclosing function scopes
377
378 for scope in self.scopeStack[-2:0:-1]:
379 importStarred = importStarred or scope.importStarred
380 if not isinstance(scope, FunctionScope):
381 continue
382 try: 409 try:
383 scope[node.name].used = (self.scope, node.lineno) 410 self.scope[node.id].used = (self.scope, node.lineno)
384 except KeyError: 411 except KeyError:
385 pass 412 pass
386 else: 413 else:
387 return 414 return
388 415
389 # try global scope 416 # try enclosing function scopes
390 417
391 importStarred = importStarred or self.scopeStack[0].importStarred 418 for scope in self.scopeStack[-2:0:-1]:
392 try: 419 importStarred = importStarred or scope.importStarred
393 self.scopeStack[0][node.name].used = (self.scope, node.lineno) 420 if not isinstance(scope, FunctionScope):
394 except KeyError: 421 continue
395 if ((not hasattr(__builtin__, node.name)) 422 try:
396 and node.name not in _MAGIC_GLOBALS 423 scope[node.id].used = (self.scope, node.lineno)
397 and not importStarred): 424 except KeyError:
398 if (os.path.basename(self.filename) == '__init__.py' and
399 node.name == '__path__'):
400 # the special name __path__ is valid only in packages
401 pass 425 pass
402 else: 426 else:
403 self.report(messages.UndefinedName, node.lineno, node.name) 427 return
404 428
405 429
406 def FUNCTION(self, node): 430 # try global scope
407 if getattr(node, "decorators", None) is not None: 431
408 self.handleChildren(node.decorators) 432 importStarred = importStarred or self.scopeStack[0].importStarred
433 try:
434 self.scopeStack[0][node.id].used = (self.scope, node.lineno)
435 except KeyError:
436 if ((not hasattr(__builtin__, node.id))
437 and node.id not in _MAGIC_GLOBALS
438 and not importStarred):
439 if (os.path.basename(self.filename) == '__init__.py' and
440 node.id == '__path__'):
441 # the special name __path__ is valid only in packages
442 pass
443 else:
444 self.report(messages.UndefinedName, node.lineno, node.id)
445 elif isinstance(node.ctx, (_ast.Store, _ast.AugStore)):
446 # if the name hasn't already been defined in the current scope
447 if isinstance(self.scope, FunctionScope) and node.id not in self.scope:
448 # for each function or module scope above us
449 for scope in self.scopeStack[:-1]:
450 if not isinstance(scope, (FunctionScope, ModuleScope)):
451 continue
452 # if the name was defined in that scope, and the name has
453 # been accessed already in the current scope, and hasn't
454 # been declared global
455 if (node.id in scope
456 and scope[node.id].used
457 and scope[node.id].used[0] is self.scope
458 and node.id not in self.scope.globals):
459 # then it's probably a mistake
460 self.report(messages.UndefinedLocal,
461 scope[node.id].used[1],
462 node.id,
463 scope[node.id].source.lineno)
464 break
465
466 if isinstance(node.parent,
467 (_ast.For, _ast.comprehension, _ast.Tuple, _ast.List)):
468 binding = Binding(node.id, node)
469 elif (node.id == '__all__' and
470 isinstance(self.scope, ModuleScope)):
471 binding = ExportBinding(node.id, node.parent.value)
472 else:
473 binding = Assignment(node.id, node)
474 if node.id in self.scope:
475 binding.used = self.scope[node.id].used
476 self.addBinding(node.lineno, binding)
477 elif isinstance(node.ctx, _ast.Del):
478 if isinstance(self.scope, FunctionScope) and \
479 node.id in self.scope.globals:
480 del self.scope.globals[node.id]
481 else:
482 self.addBinding(node.lineno, UnBinding(node.id, node))
483 else:
484 # must be a Param context -- this only happens for names in function
485 # arguments, but these aren't dispatched through here
486 raise RuntimeError(
487 "Got impossible expression context: %r" % (node.ctx,))
488
489 def FUNCTIONDEF(self, node):
490 if hasattr(node, 'decorators'):
491 for deco in node.decorators:
492 self.handleNode(deco, node)
493 else:
494 for deco in node.decorator_list:
495 self.handleNode(deco, node)
409 self.addBinding(node.lineno, FunctionDefinition(node.name, node)) 496 self.addBinding(node.lineno, FunctionDefinition(node.name, node))
410 self.LAMBDA(node) 497 self.LAMBDA(node)
411 498
412 def LAMBDA(self, node): 499 def LAMBDA(self, node):
413 for default in node.defaults: 500 for default in node.args.defaults:
414 self.handleNode(default, node) 501 self.handleNode(default, node)
415 502
416 def runFunction(): 503 def runFunction():
417 args = [] 504 args = []
418 505
419 def addArgs(arglist): 506 def addArgs(arglist):
420 for arg in arglist: 507 for arg in arglist:
421 if isinstance(arg, tuple): 508 if isinstance(arg, _ast.Tuple):
422 addArgs(arg) 509 addArgs(arg.elts)
423 else: 510 else:
424 if arg in args: 511 if arg.id in args:
425 self.report(messages.DuplicateArgument, node.lineno, arg) 512 self.report(messages.DuplicateArgument, node.lineno, arg.id)
426 args.append(arg) 513 args.append(arg.id)
427 514
428 self.pushFunctionScope() 515 self.pushFunctionScope()
429 addArgs(node.argnames) 516 addArgs(node.args.args)
517 # vararg/kwarg identifiers are not Name nodes
518 if node.args.vararg:
519 args.append(node.args.vararg)
520 if node.args.kwarg:
521 args.append(node.args.kwarg)
430 for name in args: 522 for name in args:
431 self.addBinding(node.lineno, Argument(name, node), reportRedef=False) 523 self.addBinding(node.lineno, Argument(name, node), reportRedef=False)
432 self.handleNode(node.code, node) 524 if isinstance(node.body, list):
525 # case for FunctionDefs
526 for stmt in node.body:
527 self.handleNode(stmt, node)
528 else:
529 # case for Lambdas
530 self.handleNode(node.body, node)
531
433 def checkUnusedAssignments(): 532 def checkUnusedAssignments():
434 """ 533 """
435 Check to see if any assignments have not been used. 534 Check to see if any assignments have not been used.
436 """ 535 """
437 for name, binding in self.scope.iteritems(): 536 for name, binding in self.scope.iteritems():
442 self.deferAssignment(checkUnusedAssignments) 541 self.deferAssignment(checkUnusedAssignments)
443 self.popScope() 542 self.popScope()
444 543
445 self.deferFunction(runFunction) 544 self.deferFunction(runFunction)
446 545
447 546 def CLASSDEF(self, node):
448 def CLASS(self, node):
449 """ 547 """
450 Check names used in a class definition, including its decorators, base 548 Check names used in a class definition, including its decorators, base
451 classes, and the body of its definition. Additionally, add its name to 549 classes, and the body of its definition. Additionally, add its name to
452 the current scope. 550 the current scope.
453 """ 551 """
454 if getattr(node, "decorators", None) is not None: 552 # decorator_list is present as of Python 2.6
455 self.handleChildren(node.decorators) 553 for deco in getattr(node, 'decorator_list', []):
554 self.handleNode(deco, node)
456 for baseNode in node.bases: 555 for baseNode in node.bases:
457 self.handleNode(baseNode, node) 556 self.handleNode(baseNode, node)
557 self.pushClassScope()
558 for stmt in node.body:
559 self.handleNode(stmt, node)
560 self.popScope()
458 self.addBinding(node.lineno, Binding(node.name, node)) 561 self.addBinding(node.lineno, Binding(node.name, node))
459 self.pushClassScope()
460 self.handleChildren(node.code)
461 self.popScope()
462
463
464 def ASSNAME(self, node):
465 if node.flags == 'OP_DELETE':
466 if isinstance(self.scope, FunctionScope) and node.name in self.scope.globals:
467 del self.scope.globals[node.name]
468 else:
469 self.addBinding(node.lineno, UnBinding(node.name, node))
470 else:
471 # if the name hasn't already been defined in the current scope
472 if isinstance(self.scope, FunctionScope) and node.name not in self.scope:
473 # for each function or module scope above us
474 for scope in self.scopeStack[:-1]:
475 if not isinstance(scope, (FunctionScope, ModuleScope)):
476 continue
477 # if the name was defined in that scope, and the name has
478 # been accessed already in the current scope, and hasn't
479 # been declared global
480 if (node.name in scope
481 and scope[node.name].used
482 and scope[node.name].used[0] is self.scope
483 and node.name not in self.scope.globals):
484 # then it's probably a mistake
485 self.report(messages.UndefinedLocal,
486 scope[node.name].used[1],
487 node.name,
488 scope[node.name].source.lineno)
489 break
490
491 if isinstance(node.parent,
492 (ast.For, ast.ListCompFor, ast.GenExprFor,
493 ast.AssTuple, ast.AssList)):
494 binding = Binding(node.name, node)
495 elif (node.name == '__all__' and
496 isinstance(self.scope, ModuleScope) and
497 isinstance(node.parent, ast.Assign)):
498 binding = ExportBinding(node.name, node.parent.expr)
499 else:
500 binding = Assignment(node.name, node)
501 if node.name in self.scope:
502 binding.used = self.scope[node.name].used
503 self.addBinding(node.lineno, binding)
504 562
505 def ASSIGN(self, node): 563 def ASSIGN(self, node):
506 self.handleNode(node.expr, node) 564 self.handleNode(node.value, node)
507 for subnode in node.nodes[::-1]: 565 for target in node.targets:
508 self.handleNode(subnode, node) 566 self.handleNode(target, node)
567
568 def AUGASSIGN(self, node):
569 # AugAssign is awkward: must set the context explicitly and visit twice,
570 # once with AugLoad context, once with AugStore context
571 node.target.ctx = _ast.AugLoad()
572 self.handleNode(node.target, node)
573 self.handleNode(node.value, node)
574 node.target.ctx = _ast.AugStore()
575 self.handleNode(node.target, node)
509 576
510 def IMPORT(self, node): 577 def IMPORT(self, node):
511 for name, alias in node.names: 578 for alias in node.names:
512 name = alias or name 579 name = alias.asname or alias.name
513 importation = Importation(name, node) 580 importation = Importation(name, node)
514 self.addBinding(node.lineno, importation) 581 self.addBinding(node.lineno, importation)
515 582
516 def FROM(self, node): 583 def IMPORTFROM(self, node):
517 if node.modname == '__future__': 584 if node.module == '__future__':
518 if not self.futuresAllowed: 585 if not self.futuresAllowed:
519 self.report(messages.LateFutureImport, node.lineno, [n[0] for n in node.names]) 586 self.report(messages.LateFutureImport, node.lineno,
587 [n.name for n in node.names])
520 else: 588 else:
521 self.futuresAllowed = False 589 self.futuresAllowed = False
522 590
523 for name, alias in node.names: 591 for alias in node.names:
524 if name == '*': 592 if alias.name == '*':
525 self.scope.importStarred = True 593 self.scope.importStarred = True
526 self.report(messages.ImportStarUsed, node.lineno, node.modname) 594 self.report(messages.ImportStarUsed, node.lineno, node.module)
527 continue 595 continue
528 name = alias or name 596 name = alias.asname or alias.name
529 importation = Importation(name, node) 597 importation = Importation(name, node)
530 if node.modname == '__future__': 598 if node.module == '__future__':
531 importation.used = (self.scope, node.lineno) 599 importation.used = (self.scope, node.lineno)
532 self.addBinding(node.lineno, importation) 600 self.addBinding(node.lineno, importation)
533 601
534 # 602 #
535 # eflag: FileType = Python2 603 # eflag: FileType = Python2

eric ide

mercurial