Utilities/py3flakes/checker.py

branch
5_0_x
changeset 720
dd67928832ab
parent 423
3414c60a2a47
child 792
a13346916170
equal deleted inserted replaced
719:d564fadc6913 720:dd67928832ab
102 Class defining the scope base class. 102 Class defining the scope base class.
103 """ 103 """
104 importStarred = False # set to True when import * is found 104 importStarred = False # set to True when import * is found
105 105
106 def __repr__(self): 106 def __repr__(self):
107 return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self)) 107 return '<{0} at 0x{1:x} {2}>'.format(
108 self.__class__.__name__, id(self), dict.__repr__(self))
108 109
109 def __init__(self): 110 def __init__(self):
110 super(Scope, self).__init__() 111 super(Scope, self).__init__()
111 112
112 class ClassScope(Scope): 113 class ClassScope(Scope):
242 243
243 def handleBody(self, tree): 244 def handleBody(self, tree):
244 for node in tree.body: 245 for node in tree.body:
245 self.handleNode(node, tree) 246 self.handleNode(node, tree)
246 247
248 def handleChildren(self, tree):
249 for node in ast.iter_child_nodes(tree):
250 self.handleNode(node, tree)
251
252 def isDocstring(self, node):
253 """
254 Determine if the given node is a docstring, as long as it is at the
255 correct place in the node tree.
256 """
257 return isinstance(node, ast.Str) or \
258 (isinstance(node, ast.Expr) and
259 isinstance(node.value, ast.Str))
260
247 def handleNode(self, node, parent): 261 def handleNode(self, node, parent):
248 if node: 262 if node:
249 node.parent = parent 263 node.parent = parent
250 if self.traceTree: 264 if self.traceTree:
251 print(' ' * self.nodeDepth + node.__class__.__name__) 265 print(' ' * self.nodeDepth + node.__class__.__name__)
252 self.nodeDepth += 1 266 self.nodeDepth += 1
267 if self.futuresAllowed and not \
268 (isinstance(node, ast.ImportFrom) or self.isDocstring(node)):
269 self.futuresAllowed = False
253 nodeType = node.__class__.__name__.upper() 270 nodeType = node.__class__.__name__.upper()
254 try: 271 try:
255 handler = getattr(self, nodeType) 272 handler = getattr(self, nodeType)
256 handler(node) 273 handler(node)
257 finally: 274 finally:
262 def ignore(self, node): 279 def ignore(self, node):
263 pass 280 pass
264 281
265 # ast nodes to be ignored 282 # ast nodes to be ignored
266 PASS = CONTINUE = BREAK = ELLIPSIS = NUM = STR = BYTES = \ 283 PASS = CONTINUE = BREAK = ELLIPSIS = NUM = STR = BYTES = \
284 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = \
267 ATTRIBUTES = AND = OR = ADD = SUB = MULT = DIV = \ 285 ATTRIBUTES = AND = OR = ADD = SUB = MULT = DIV = \
268 MOD = POW = LSHIFT = RSHIFT = BITOR = BITXOR = BITAND = FLOORDIV = \ 286 MOD = POW = LSHIFT = RSHIFT = BITOR = BITXOR = BITAND = FLOORDIV = \
269 INVERT = NOT = UADD = USUB = INVERT = NOT = UADD = USUB = ignore 287 INVERT = NOT = UADD = USUB = EQ = NOTEQ = LT = LTE = GT = GTE = IS = \
270 288 ISNOT = IN = NOTIN = ignore
289
290 # "stmt" type nodes
291 RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \
292 TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
293
294 # "expr" type nodes
295 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \
296 CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren
297
298 # "slice" type nodes
299 SLICE = EXTSLICE = INDEX = handleChildren
300
301 # additional node types
302 COMPREHENSION = KEYWORD = handleChildren
303
271 def addBinding(self, lineno, value, reportRedef = True): 304 def addBinding(self, lineno, value, reportRedef = True):
272 '''Called when a binding is altered. 305 '''Called when a binding is altered.
273 306
274 @param lineno line of the statement responsible for the change (integer) 307 @param lineno line of the statement responsible for the change (integer)
275 @param value the optional new value, a Binding instance, associated 308 @param value the optional new value, a Binding instance, associated
283 lineno, value.name, self.scope[value.name].source.lineno) 316 lineno, value.name, self.scope[value.name].source.lineno)
284 317
285 if not isinstance(self.scope, ClassScope): 318 if not isinstance(self.scope, ClassScope):
286 for scope in self.scopeStack[::-1]: 319 for scope in self.scopeStack[::-1]:
287 existing = scope.get(value.name) 320 existing = scope.get(value.name)
288 if (isinstance(existing, Importation) 321 if isinstance(existing, Importation) and \
289 and not existing.used 322 not existing.used and \
290 and (not isinstance(value, Importation) or value.fullName == existing.fullName) 323 not isinstance(value, UnBinding) and \
291 and reportRedef): 324 (not isinstance(value, Importation) or \
292 325 value.fullName == existing.fullName) and \
326 reportRedef:
293 self.report(messages.RedefinedWhileUnused, 327 self.report(messages.RedefinedWhileUnused,
294 lineno, value.name, scope[value.name].source.lineno) 328 lineno, value.name, scope[value.name].source.lineno)
295 329
296 if isinstance(value, UnBinding): 330 if isinstance(value, UnBinding):
297 try: 331 try:
303 337
304 ############################################################ 338 ############################################################
305 ## individual handler methods below 339 ## individual handler methods below
306 ############################################################ 340 ############################################################
307 341
308 def LIST(self, node):
309 for elt in node.elts:
310 self.handleNode(elt, node)
311
312 SET = TUPLE = LIST
313
314 def DICT(self, node):
315 for key in node.keys:
316 self.handleNode(key, node)
317 for val in node.values:
318 self.handleNode(val, node)
319
320 def WITH(self, node):
321 """
322 Handle with by checking the target of the statement (which can be an
323 identifier, a list or tuple of targets, an attribute, etc) for
324 undefined names and defining any it adds to the scope and by continuing
325 to process the suite within the statement.
326 """
327 # Check the "foo" part of a "with foo as bar" statement. Do this no
328 # matter what, since there's always a "foo" part.
329 self.handleNode(node.context_expr, node)
330
331 arg = None
332 if node.optional_vars is not None:
333 arg = Argument(node.optional_vars.id, node)
334 self.addBinding(node.lineno, arg, reportRedef=False)
335 self.handleBody(node)
336 if arg:
337 del self.scope[arg.name]
338
339 def GLOBAL(self, node): 342 def GLOBAL(self, node):
340 """ 343 """
341 Keep track of globals declarations. 344 Keep track of globals declarations.
342 """ 345 """
343 if isinstance(self.scope, FunctionScope): 346 if isinstance(self.scope, FunctionScope):
356 for generator in node.generators: 359 for generator in node.generators:
357 self.handleNode(generator, node) 360 self.handleNode(generator, node)
358 self.handleNode(node.key, node) 361 self.handleNode(node.key, node)
359 self.handleNode(node.value, node) 362 self.handleNode(node.value, node)
360 363
361 def COMPREHENSION(self, node):
362 node.target.parent = node
363 self.handleAssignName(node.target)
364 self.handleNode(node.iter, node)
365 for elt in node.ifs:
366 self.handleNode(elt, node)
367
368 def FOR(self, node): 364 def FOR(self, node):
369 """ 365 """
370 Process bindings for loop variables. 366 Process bindings for loop variables.
371 """ 367 """
372 vars = [] 368 vars = []
373 def collectLoopVars(n): 369 def collectLoopVars(n):
374 if isinstance(n, ast.Name): 370 if isinstance(n, ast.Name):
375 vars.append(n.id) 371 vars.append(n.id)
372 elif isinstance(n, ast.expr_context):
373 return
374 else:
375 for c in ast.iter_child_nodes(n):
376 collectLoopVars(c)
376 377
377 collectLoopVars(node.target) 378 collectLoopVars(node.target)
378 for varn in vars: 379 for varn in vars:
379 if (isinstance(self.scope.get(varn), Importation) 380 if (isinstance(self.scope.get(varn), Importation)
380 # unused ones will get an unused import warning 381 # unused ones will get an unused import warning
381 and self.scope[varn].used): 382 and self.scope[varn].used):
382 self.report(messages.ImportShadowedByLoopVar, 383 self.report(messages.ImportShadowedByLoopVar,
383 node.lineno, varn, self.scope[varn].source.lineno) 384 node.lineno, varn, self.scope[varn].source.lineno)
384 385
385 node.target.parent = node 386 self.handleChildren(node)
386 self.handleAssignName(node.target)
387 self.handleNode(node.iter, node)
388 self.handleBody(node)
389 387
390 def NAME(self, node): 388 def NAME(self, node):
391 """ 389 """
392 Locate the name in locals / function / globals scopes. 390 Handle occurrence of Name (which can be a load/store/delete access.)
393 """ 391 """
394 # try local scope 392 # Locate the name in locals / function / globals scopes.
395 importStarred = self.scope.importStarred 393 if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
396 try: 394 # try local scope
397 self.scope[node.id].used = (self.scope, node.lineno) 395 importStarred = self.scope.importStarred
398 except KeyError:
399 pass
400 else:
401 return
402
403 # try enclosing function scopes
404 for scope in self.scopeStack[-2:0:-1]:
405 importStarred = importStarred or scope.importStarred
406 if not isinstance(scope, FunctionScope):
407 continue
408 try: 396 try:
409 scope[node.id].used = (self.scope, node.lineno) 397 self.scope[node.id].used = (self.scope, node.lineno)
410 except KeyError: 398 except KeyError:
411 pass 399 pass
412 else: 400 else:
413 return 401 return
414 402
415 # try global scope 403 # try enclosing function scopes
416 importStarred = importStarred or self.scopeStack[0].importStarred 404 for scope in self.scopeStack[-2:0:-1]:
417 try: 405 importStarred = importStarred or scope.importStarred
418 self.scopeStack[0][node.id].used = (self.scope, node.lineno) 406 if not isinstance(scope, FunctionScope):
419 except KeyError: 407 continue
420 if ((not hasattr(builtins, node.id)) 408 try:
421 and node.id not in _MAGIC_GLOBALS 409 scope[node.id].used = (self.scope, node.lineno)
422 and not importStarred): 410 except KeyError:
423 if (os.path.basename(self.filename) == '__init__.py' and
424 node.id == '__path__'):
425 # the special name __path__ is valid only in packages
426 pass 411 pass
427 else: 412 else:
428 self.report(messages.UndefinedName, node.lineno, node.id) 413 return
414
415 # try global scope
416 importStarred = importStarred or self.scopeStack[0].importStarred
417 try:
418 self.scopeStack[0][node.id].used = (self.scope, node.lineno)
419 except KeyError:
420 if ((not hasattr(builtins, node.id))
421 and node.id not in _MAGIC_GLOBALS
422 and not importStarred):
423 if (os.path.basename(self.filename) == '__init__.py' and
424 node.id == '__path__'):
425 # the special name __path__ is valid only in packages
426 pass
427 else:
428 self.report(messages.UndefinedName, node.lineno, node.id)
429 elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
430 # if the name hasn't already been defined in the current scope
431 if isinstance(self.scope, FunctionScope) and node.id not in self.scope:
432 # for each function or module scope above us
433 for scope in self.scopeStack[:-1]:
434 if not isinstance(scope, (FunctionScope, ModuleScope)):
435 continue
436 # if the name was defined in that scope, and the name has
437 # been accessed already in the current scope, and hasn't
438 # been declared global
439 if (node.id in scope
440 and scope[node.id].used
441 and scope[node.id].used[0] is self.scope
442 and node.id not in self.scope.globals):
443 # then it's probably a mistake
444 self.report(messages.UndefinedLocal,
445 scope[node.id].used[1],
446 node.id,
447 scope[node.id].source.lineno)
448 break
449
450 if isinstance(node.parent,
451 (ast.For, ast.comprehension, ast.Tuple, ast.List)):
452 binding = Binding(node.id, node)
453 elif (node.id == '__all__' and
454 isinstance(self.scope, ModuleScope)):
455 binding = ExportBinding(node.id, node.parent.value)
456 else:
457 binding = Assignment(node.id, node)
458 if node.id in self.scope:
459 binding.used = self.scope[node.id].used
460 self.addBinding(node.lineno, binding)
461 elif isinstance(node.ctx, ast.Del):
462 if isinstance(self.scope, FunctionScope) and \
463 node.id in self.scope.globals:
464 del self.scope.globals[node.id]
465 else:
466 self.addBinding(node.lineno, UnBinding(node.id, node))
467 else:
468 # must be a Param context -- this only happens for names in function
469 # arguments, but these aren't dispatched through here
470 raise RuntimeError(
471 "Got impossible expression context: {0:r}".format(node.ctx,))
429 472
430 def FUNCTIONDEF(self, node): 473 def FUNCTIONDEF(self, node):
431 if getattr(node, "decorator_list", None) is not None: 474 if getattr(node, "decorator_list", None) is not None:
432 for decorator in node.decorator_list: 475 for decorator in node.decorator_list:
433 self.handleNode(decorator, node) 476 self.handleNode(decorator, node)
461 binding.source.lineno, name) 504 binding.source.lineno, name)
462 505
463 self.pushFunctionScope() 506 self.pushFunctionScope()
464 addArgs(node.args.args) 507 addArgs(node.args.args)
465 addArgs(node.args.kwonlyargs) 508 addArgs(node.args.kwonlyargs)
509 # vararg/kwarg identifiers are not Name nodes
510 if node.args.vararg:
511 args.append(node.args.vararg)
512 if node.args.kwarg:
513 args.append(node.args.kwarg)
466 for name in args: 514 for name in args:
467 self.addBinding(node.lineno, Argument(name, node), reportRedef=False) 515 self.addBinding(node.lineno, Argument(name, node), reportRedef=False)
468 if node.args.vararg:
469 self.addBinding(node.lineno, Argument(node.args.vararg, node),
470 reportRedef=False)
471 if node.args.kwarg:
472 self.addBinding(node.lineno, Argument(node.args.kwarg, node),
473 reportRedef=False)
474 if isinstance(node.body, list): 516 if isinstance(node.body, list):
475 self.handleBody(node) 517 self.handleBody(node)
476 else: 518 else:
477 self.handleNode(node.body, node) 519 self.handleNode(node.body, node)
478 self.deferAssignment(checkUnusedAssignments) 520 self.deferAssignment(checkUnusedAssignments)
553 binding.used = self.scope[node.id].used 595 binding.used = self.scope[node.id].used
554 self.addBinding(node.lineno, binding) 596 self.addBinding(node.lineno, binding)
555 597
556 def ASSIGN(self, node): 598 def ASSIGN(self, node):
557 self.handleNode(node.value, node) 599 self.handleNode(node.value, node)
558 for subnode in node.targets[::-1]: 600 for target in node.targets:
559 subnode.parent = node 601 self.handleNode(target, node)
560 if isinstance(subnode, ast.Attribute):
561 self.handleNode(subnode.value, subnode)
562 else:
563 self.handleAssignName(subnode)
564 602
565 def AUGASSIGN(self, node): 603 def AUGASSIGN(self, node):
604 # AugAssign is awkward: must set the context explicitly and visit twice,
605 # once with AugLoad context, once with AugStore context
606 node.target.ctx = ast.AugLoad()
607 self.handleNode(node.target, node)
566 self.handleNode(node.value, node) 608 self.handleNode(node.value, node)
609 node.target.ctx = ast.AugStore()
567 self.handleNode(node.target, node) 610 self.handleNode(node.target, node)
568 611
569 def IMPORT(self, node): 612 def IMPORT(self, node):
570 for alias in node.names: 613 for alias in node.names:
571 name = alias.asname or alias.name 614 name = alias.asname or alias.name
589 importation = Importation(name, node) 632 importation = Importation(name, node)
590 if node.module == '__future__': 633 if node.module == '__future__':
591 importation.used = (self.scope, node.lineno) 634 importation.used = (self.scope, node.lineno)
592 self.addBinding(node.lineno, importation) 635 self.addBinding(node.lineno, importation)
593 636
594 def CALL(self, node):
595 self.handleNode(node.func, node)
596 for arg in node.args:
597 self.handleNode(arg, node)
598 for kw in node.keywords:
599 self.handleNode(kw, node)
600 node.starargs and self.handleNode(node.starargs, node)
601 node.kwargs and self.handleNode(node.kwargs, node)
602
603 def KEYWORD(self, node):
604 self.handleNode(node.value, node)
605
606 def BOOLOP(self, node):
607 for val in node.values:
608 self.handleNode(val, node)
609
610 def BINOP(self, node):
611 self.handleNode(node.left, node)
612 self.handleNode(node.right, node)
613
614 def UNARYOP(self, node):
615 self.handleNode(node.operand, node)
616
617 def RETURN(self, node):
618 node.value and self.handleNode(node.value, node)
619
620 def DELETE(self, node):
621 for tgt in node.targets:
622 self.handleNode(tgt, node)
623
624 def EXPR(self, node):
625 self.handleNode(node.value, node)
626
627 def ATTRIBUTE(self, node):
628 self.handleNode(node.value, node)
629
630 def IF(self, node):
631 self.handleNode(node.test, node)
632 self.handleBody(node)
633 for stmt in node.orelse:
634 self.handleNode(stmt, node)
635
636 WHILE = IF
637
638 def RAISE(self, node):
639 node.exc and self.handleNode(node.exc, node)
640 node.cause and self.handleNode(node.cause, node)
641
642 def TRYEXCEPT(self, node):
643 self.handleBody(node)
644 for handler in node.handlers:
645 self.handleNode(handler, node)
646 for stmt in node.orelse:
647 self.handleNode(stmt, node)
648
649 def TRYFINALLY(self, node):
650 self.handleBody(node)
651 for stmt in node.finalbody:
652 self.handleNode(stmt, node)
653
654 def EXCEPTHANDLER(self, node): 637 def EXCEPTHANDLER(self, node):
655 node.type and self.handleNode(node.type, node) 638 node.type and self.handleNode(node.type, node)
656 if node.name: 639 if node.name:
657 node.id = node.name 640 node.id = node.name
658 self.handleAssignName(node) 641 self.handleAssignName(node)
659 self.handleBody(node) 642 self.handleBody(node)
660 643
661 def ASSERT(self, node):
662 self.handleNode(node.test, node)
663 node.msg and self.handleNode(node.msg, node)
664
665 def COMPARE(self, node):
666 self.handleNode(node.left, node)
667 for comparator in node.comparators:
668 self.handleNode(comparator, node)
669
670 def YIELD(self, node):
671 node.value and self.handleNode(node.value, node)
672
673 def SUBSCRIPT(self, node):
674 self.handleNode(node.value, node)
675 self.handleNode(node.slice, node)
676
677 def SLICE(self, node):
678 node.lower and self.handleNode(node.lower, node)
679 node.upper and self.handleNode(node.upper, node)
680 node.step and self.handleNode(node.step, node)
681
682 def EXTSLICE(self, node):
683 for slice in node.dims:
684 self.handleNode(slice, node)
685
686 def INDEX(self, node):
687 self.handleNode(node.value, node)
688
689 def IFEXP(self, node):
690 self.handleNode(node.test, node)
691 self.handleNode(node.body, node)
692 self.handleNode(node.orelse, node)
693
694 def STARRED(self, node): 644 def STARRED(self, node):
695 self.handleNode(node.value, node) 645 self.handleNode(node.value, node)

eric ide

mercurial