Utilities/py3flakes/checker.py

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

eric ide

mercurial