61 return str(unicode(node_class.__name__).upper()) # __IGNORE_WARNING__ |
39 return str(unicode(node_class.__name__).upper()) # __IGNORE_WARNING__ |
62 else: |
40 else: |
63 def getNodeType(node_class): |
41 def getNodeType(node_class): |
64 return node_class.__name__.upper() |
42 return node_class.__name__.upper() |
65 |
43 |
|
44 # Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally) |
|
45 if PY32: |
|
46 def getAlternatives(n): |
|
47 if isinstance(n, (ast.If, ast.TryFinally)): |
|
48 return [n.body] |
|
49 if isinstance(n, ast.TryExcept): |
|
50 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers] |
|
51 else: |
|
52 def getAlternatives(n): |
|
53 if isinstance(n, ast.If): |
|
54 return [n.body] |
|
55 if isinstance(n, ast.Try): |
|
56 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers] |
|
57 |
|
58 |
|
59 class _FieldsOrder(dict): |
|
60 """Fix order of AST node fields.""" |
|
61 |
|
62 def _get_fields(self, node_class): |
|
63 # handle iter before target, and generators before element |
|
64 fields = node_class._fields |
|
65 if 'iter' in fields: |
|
66 key_first = 'iter'.find |
|
67 elif 'generators' in fields: |
|
68 key_first = 'generators'.find |
|
69 else: |
|
70 key_first = 'value'.find |
|
71 return tuple(sorted(fields, key=key_first, reverse=True)) |
|
72 |
|
73 def __missing__(self, node_class): |
|
74 self[node_class] = fields = self._get_fields(node_class) |
|
75 return fields |
|
76 |
|
77 |
|
78 def iter_child_nodes(node, omit=None, _fields_order=_FieldsOrder()): |
|
79 """ |
|
80 Yield all direct child nodes of *node*, that is, all fields that |
|
81 are nodes and all items of fields that are lists of nodes. |
|
82 """ |
|
83 for name in _fields_order[node.__class__]: |
|
84 if name == omit: |
|
85 continue |
|
86 field = getattr(node, name, None) |
|
87 if isinstance(field, ast.AST): |
|
88 yield field |
|
89 elif isinstance(field, list): |
|
90 for item in field: |
|
91 yield item |
|
92 |
66 |
93 |
67 class Binding(object): |
94 class Binding(object): |
68 """ |
95 """ |
69 Represents the binding of a value to a name. |
96 Represents the binding of a value to a name. |
70 |
97 |
308 """ |
349 """ |
309 Look at scopes which have been fully examined and report names in them |
350 Look at scopes which have been fully examined and report names in them |
310 which were imported but unused. |
351 which were imported but unused. |
311 """ |
352 """ |
312 for scope in self.deadScopes: |
353 for scope in self.deadScopes: |
313 export = isinstance(scope.get('__all__'), ExportBinding) |
354 if isinstance(scope.get('__all__'), ExportBinding): |
314 if export: |
355 all_names = set(scope['__all__'].names) |
315 all = scope['__all__'].names() |
|
316 if not scope.importStarred and \ |
356 if not scope.importStarred and \ |
317 os.path.basename(self.filename) != '__init__.py': |
357 os.path.basename(self.filename) != '__init__.py': |
318 # Look for possible mistakes in the export list |
358 # Look for possible mistakes in the export list |
319 undefined = set(all) - set(scope) |
359 undefined = all_names.difference(scope) |
320 for name in undefined: |
360 for name in undefined: |
321 self.report(messages.UndefinedExport, |
361 self.report(messages.UndefinedExport, |
322 scope['__all__'].source, name) |
362 scope['__all__'].source, name) |
323 else: |
363 else: |
324 all = [] |
364 all_names = [] |
325 |
365 |
326 # Look for imported names that aren't used. |
366 # Look for imported names that aren't used. |
327 for importation in scope.values(): |
367 for value in scope.values(): |
328 if isinstance(importation, Importation): |
368 if isinstance(value, Importation): |
329 if not importation.used and importation.name not in all: |
369 used = value.used or value.name in all_names |
330 self.report(messages.UnusedImport, |
370 if not used: |
331 importation.source, importation.name) |
371 messg = messages.UnusedImport |
|
372 self.report(messg, value.source, value.name) |
|
373 for node in value.redefined: |
|
374 if isinstance(self.getParent(node), ast.For): |
|
375 messg = messages.ImportShadowedByLoopVar |
|
376 elif used: |
|
377 continue |
|
378 else: |
|
379 messg = messages.RedefinedWhileUnused |
|
380 self.report(messg, node, value.name, value.source) |
332 |
381 |
333 def pushScope(self, scopeClass=FunctionScope): |
382 def pushScope(self, scopeClass=FunctionScope): |
334 self.scopeStack.append(scopeClass()) |
383 self.scopeStack.append(scopeClass()) |
335 |
384 |
336 def pushFunctionScope(self): # XXX Deprecated |
|
337 self.pushScope(FunctionScope) |
|
338 |
|
339 def pushClassScope(self): # XXX Deprecated |
|
340 self.pushScope(ClassScope) |
|
341 |
|
342 def report(self, messageClass, *args, **kwargs): |
385 def report(self, messageClass, *args, **kwargs): |
343 self.messages.append(messageClass(self.filename, *args, **kwargs)) |
386 self.messages.append(messageClass(self.filename, *args, **kwargs)) |
344 |
387 |
345 def hasParent(self, node, kind): |
388 def getParent(self, node): |
346 while hasattr(node, 'parent'): |
389 # Lookup the first parent which is not Tuple, List or Starred |
|
390 while True: |
347 node = node.parent |
391 node = node.parent |
348 if isinstance(node, kind): |
392 if not hasattr(node, 'elts') and not hasattr(node, 'ctx'): |
349 return True |
393 return node |
350 |
394 |
351 def getCommonAncestor(self, lnode, rnode, stop=None): |
395 def getCommonAncestor(self, lnode, rnode, stop): |
352 if not stop: |
396 if stop in (lnode, rnode) or not (hasattr(lnode, 'parent') and |
353 stop = self.root |
397 hasattr(rnode, 'parent')): |
|
398 return None |
354 if lnode is rnode: |
399 if lnode is rnode: |
355 return lnode |
400 return lnode |
356 if stop in (lnode, rnode): |
401 |
357 return stop |
402 if (lnode.depth > rnode.depth): |
358 |
|
359 if not hasattr(lnode, 'parent') or not hasattr(rnode, 'parent'): |
|
360 return |
|
361 if (lnode.level > rnode.level): |
|
362 return self.getCommonAncestor(lnode.parent, rnode, stop) |
403 return self.getCommonAncestor(lnode.parent, rnode, stop) |
363 if (rnode.level > lnode.level): |
404 if (lnode.depth < rnode.depth): |
364 return self.getCommonAncestor(lnode, rnode.parent, stop) |
405 return self.getCommonAncestor(lnode, rnode.parent, stop) |
365 return self.getCommonAncestor(lnode.parent, rnode.parent, stop) |
406 return self.getCommonAncestor(lnode.parent, rnode.parent, stop) |
366 |
407 |
367 def descendantOf(self, node, ancestors, stop=None): |
408 def descendantOf(self, node, ancestors, stop): |
368 for a in ancestors: |
409 for a in ancestors: |
369 if self.getCommonAncestor(node, a, stop) not in (stop, None): |
410 if self.getCommonAncestor(node, a, stop): |
370 return True |
411 return True |
371 return False |
412 return False |
372 |
413 |
373 def onFork(self, parent, lnode, rnode, items): |
|
374 return (self.descendantOf(lnode, items, parent) ^ |
|
375 self.descendantOf(rnode, items, parent)) |
|
376 |
|
377 def differentForks(self, lnode, rnode): |
414 def differentForks(self, lnode, rnode): |
378 """True, if lnode and rnode are located on different forks of IF/TRY""" |
415 """True, if lnode and rnode are located on different forks of IF/TRY""" |
379 ancestor = self.getCommonAncestor(lnode, rnode) |
416 ancestor = self.getCommonAncestor(lnode, rnode, self.root) |
380 if isinstance(ancestor, ast.If): |
417 parts = getAlternatives(ancestor) |
381 for fork in (ancestor.body, ancestor.orelse): |
418 if parts: |
382 if self.onFork(ancestor, lnode, rnode, fork): |
419 for items in parts: |
|
420 if self.descendantOf(lnode, items, ancestor) ^ \ |
|
421 self.descendantOf(rnode, items, ancestor): |
383 return True |
422 return True |
384 elif isinstance(ancestor, ast_TryExcept): |
|
385 body = ancestor.body + ancestor.orelse |
|
386 for fork in [body] + [[hdl] for hdl in ancestor.handlers]: |
|
387 if self.onFork(ancestor, lnode, rnode, fork): |
|
388 return True |
|
389 elif isinstance(ancestor, ast_TryFinally): |
|
390 if self.onFork(ancestor, lnode, rnode, ancestor.body): |
|
391 return True |
|
392 return False |
423 return False |
393 |
424 |
394 def addBinding(self, node, value, reportRedef=True): |
425 def addBinding(self, node, value): |
395 """ |
426 """ |
396 Called when a binding is altered. |
427 Called when a binding is altered. |
397 |
428 |
398 - `node` is the statement responsible for the change |
429 - `node` is the statement responsible for the change |
399 - `value` is the optional new value, a Binding instance, associated |
430 - `value` is the new value, a Binding instance |
400 with the binding; if None, the binding is deleted if it exists. |
431 """ |
401 - if `reportRedef` is True (default), rebinding while unused will be |
432 # assert value.source in (node, node.parent): |
402 reported. |
433 for scope in self.scopeStack[::-1]: |
403 """ |
434 if value.name in scope: |
404 redefinedWhileUnused = False |
435 break |
405 if not isinstance(self.scope, ClassScope): |
436 existing = scope.get(value.name) |
406 for scope in self.scopeStack[::-1]: |
437 |
407 existing = scope.get(value.name) |
438 if existing and not self.differentForks(node, existing.source): |
408 if (isinstance(existing, Importation) |
439 |
409 and not existing.used |
440 parent_stmt = self.getParent(value.source) |
410 and (not isinstance(value, Importation) or |
441 if isinstance(existing, Importation) and isinstance(parent_stmt, ast.For): |
411 value.fullName == existing.fullName) |
442 self.report(messages.ImportShadowedByLoopVar, |
412 and reportRedef |
443 node, value.name, existing.source) |
413 and not self.differentForks(node, existing.source)): |
444 |
414 redefinedWhileUnused = True |
445 elif scope is self.scope: |
|
446 if (isinstance(parent_stmt, ast.comprehension) and |
|
447 not isinstance(self.getParent(existing.source), |
|
448 (ast.For, ast.comprehension))): |
|
449 self.report(messages.RedefinedInListComp, |
|
450 node, value.name, existing.source) |
|
451 elif not existing.used and value.redefines(existing): |
415 self.report(messages.RedefinedWhileUnused, |
452 self.report(messages.RedefinedWhileUnused, |
416 node, value.name, existing.source) |
453 node, value.name, existing.source) |
417 |
454 |
418 existing = self.scope.get(value.name) |
455 elif isinstance(existing, Importation) and value.redefines(existing): |
419 if not redefinedWhileUnused and self.hasParent(value.source, ast.ListComp): |
456 existing.redefined.append(node) |
420 if (existing and reportRedef |
457 |
421 and not self.hasParent(existing.source, (ast.For, ast.ListComp)) |
458 self.scope[value.name] = value |
422 and not self.differentForks(node, existing.source)): |
|
423 self.report(messages.RedefinedInListComp, |
|
424 node, value.name, existing.source) |
|
425 |
|
426 if (isinstance(existing, Definition) |
|
427 and not existing.used |
|
428 and not self.differentForks(node, existing.source)): |
|
429 self.report(messages.RedefinedWhileUnused, |
|
430 node, value.name, existing.source) |
|
431 else: |
|
432 self.scope[value.name] = value |
|
433 |
459 |
434 def getNodeHandler(self, node_class): |
460 def getNodeHandler(self, node_class): |
435 try: |
461 try: |
436 return self._nodeHandlers[node_class] |
462 return self._nodeHandlers[node_class] |
437 except KeyError: |
463 except KeyError: |
567 |
601 |
568 _getDoctestExamples = doctest.DocTestParser().get_examples |
602 _getDoctestExamples = doctest.DocTestParser().get_examples |
569 |
603 |
570 def handleDoctests(self, node): |
604 def handleDoctests(self, node): |
571 try: |
605 try: |
572 docstring, node_lineno = self.getDocstring(node.body[0]) |
606 (docstring, node_lineno) = self.getDocstring(node.body[0]) |
573 if not docstring: |
607 examples = docstring and self._getDoctestExamples(docstring) |
574 return |
|
575 examples = self._getDoctestExamples(docstring) |
|
576 except (ValueError, IndexError): |
608 except (ValueError, IndexError): |
577 # e.g. line 6 of the docstring for <string> has inconsistent |
609 # e.g. line 6 of the docstring for <string> has inconsistent |
578 # leading whitespace: ... |
610 # leading whitespace: ... |
579 return |
611 return |
|
612 if not examples: |
|
613 return |
580 node_offset = self.offset or (0, 0) |
614 node_offset = self.offset or (0, 0) |
581 self.pushScope() |
615 self.pushScope() |
|
616 underscore_in_builtins = '_' in self.builtIns |
|
617 if not underscore_in_builtins: |
|
618 self.builtIns.add('_') |
582 for example in examples: |
619 for example in examples: |
583 try: |
620 try: |
584 tree = compile(example.source, "<doctest>", "exec", ast.PyCF_ONLY_AST) |
621 tree = compile(example.source, "<doctest>", "exec", ast.PyCF_ONLY_AST) |
585 except SyntaxError: |
622 except SyntaxError: |
586 e = sys.exc_info()[1] |
623 e = sys.exc_info()[1] |
587 position = (node_lineno + example.lineno + e.lineno, |
624 position = (node_lineno + example.lineno + e.lineno, |
588 example.indent + 4 + e.offset) |
625 example.indent + 4 + (e.offset or 0)) |
589 self.report(messages.DoctestSyntaxError, node, position) |
626 self.report(messages.DoctestSyntaxError, node, position) |
590 else: |
627 else: |
591 self.offset = (node_offset[0] + node_lineno + example.lineno, |
628 self.offset = (node_offset[0] + node_lineno + example.lineno, |
592 node_offset[1] + example.indent + 4) |
629 node_offset[1] + example.indent + 4) |
593 self.handleChildren(tree) |
630 self.handleChildren(tree) |
594 self.offset = node_offset |
631 self.offset = node_offset |
|
632 if not underscore_in_builtins: |
|
633 self.builtIns.remove('_') |
595 self.popScope() |
634 self.popScope() |
596 |
635 |
597 def ignore(self, node): |
636 def ignore(self, node): |
598 pass |
637 pass |
599 |
638 |
600 # "stmt" type nodes |
639 # "stmt" type nodes |
601 RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \ |
640 DELETE = PRINT = FOR = WHILE = IF = WITH = WITHITEM = RAISE = \ |
602 TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren |
641 TRYFINALLY = ASSERT = EXEC = EXPR = ASSIGN = handleChildren |
603 |
642 |
604 CONTINUE = BREAK = PASS = ignore |
643 CONTINUE = BREAK = PASS = ignore |
605 |
644 |
606 # "expr" type nodes |
645 # "expr" type nodes |
607 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = YIELDFROM = \ |
646 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = \ |
608 COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \ |
647 COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \ |
609 STARRED = handleChildren |
648 STARRED = NAMECONSTANT = handleChildren |
610 |
649 |
611 NUM = STR = BYTES = ELLIPSIS = ignore |
650 NUM = STR = BYTES = ELLIPSIS = ignore |
612 |
651 |
613 # "slice" type nodes |
652 # "slice" type nodes |
614 SLICE = EXTSLICE = INDEX = handleChildren |
653 SLICE = EXTSLICE = INDEX = handleChildren |
620 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \ |
659 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \ |
621 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \ |
660 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \ |
622 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore |
661 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore |
623 |
662 |
624 # additional node types |
663 # additional node types |
625 COMPREHENSION = KEYWORD = handleChildren |
664 LISTCOMP = COMPREHENSION = KEYWORD = handleChildren |
626 |
665 |
627 def GLOBAL(self, node): |
666 def GLOBAL(self, node): |
628 """ |
667 """ |
629 Keep track of globals declarations. |
668 Keep track of globals declarations. |
630 """ |
669 """ |
631 if isinstance(self.scope, FunctionScope): |
670 if isinstance(self.scope, FunctionScope): |
632 self.scope.globals.update(node.names) |
671 self.scope.globals.update(node.names) |
633 |
672 |
634 NONLOCAL = GLOBAL |
673 NONLOCAL = GLOBAL |
635 |
674 |
636 def LISTCOMP(self, node): |
|
637 # handle generators before element |
|
638 for gen in node.generators: |
|
639 self.handleNode(gen, node) |
|
640 self.handleNode(node.elt, node) |
|
641 |
|
642 def GENERATOREXP(self, node): |
675 def GENERATOREXP(self, node): |
643 self.pushScope(GeneratorScope) |
676 self.pushScope(GeneratorScope) |
644 # handle generators before element |
677 self.handleChildren(node) |
645 for gen in node.generators: |
|
646 self.handleNode(gen, node) |
|
647 self.handleNode(node.elt, node) |
|
648 self.popScope() |
678 self.popScope() |
649 |
679 |
650 SETCOMP = GENERATOREXP |
680 DICTCOMP = SETCOMP = GENERATOREXP |
651 |
|
652 def DICTCOMP(self, node): |
|
653 self.pushScope(GeneratorScope) |
|
654 for gen in node.generators: |
|
655 self.handleNode(gen, node) |
|
656 self.handleNode(node.key, node) |
|
657 self.handleNode(node.value, node) |
|
658 self.popScope() |
|
659 |
|
660 def FOR(self, node): |
|
661 """ |
|
662 Process bindings for loop variables. |
|
663 """ |
|
664 vars = [] |
|
665 |
|
666 def collectLoopVars(n): |
|
667 if isinstance(n, ast.Name): |
|
668 vars.append(n.id) |
|
669 elif isinstance(n, ast.expr_context): |
|
670 return |
|
671 else: |
|
672 for c in iter_child_nodes(n): |
|
673 collectLoopVars(c) |
|
674 |
|
675 collectLoopVars(node.target) |
|
676 for varn in vars: |
|
677 if (isinstance(self.scope.get(varn), Importation) |
|
678 # unused ones will get an unused import warning |
|
679 and self.scope[varn].used): |
|
680 self.report(messages.ImportShadowedByLoopVar, |
|
681 node, varn, self.scope[varn].source) |
|
682 |
|
683 self.handleChildren(node) |
|
684 |
681 |
685 def NAME(self, node): |
682 def NAME(self, node): |
686 """ |
683 """ |
687 Handle occurrence of Name (which can be a load/store/delete access.) |
684 Handle occurrence of Name (which can be a load/store/delete access.) |
688 """ |
685 """ |
700 else: |
697 else: |
701 # must be a Param context -- this only happens for names in function |
698 # must be a Param context -- this only happens for names in function |
702 # arguments, but these aren't dispatched through here |
699 # arguments, but these aren't dispatched through here |
703 raise RuntimeError("Got impossible expression context: %r" % (node.ctx,)) |
700 raise RuntimeError("Got impossible expression context: %r" % (node.ctx,)) |
704 |
701 |
|
702 def RETURN(self, node): |
|
703 if node.value and not self.scope.returnValue: |
|
704 self.scope.returnValue = node.value |
|
705 self.handleNode(node.value, node) |
|
706 |
|
707 def YIELD(self, node): |
|
708 self.scope.isGenerator = True |
|
709 self.handleNode(node.value, node) |
|
710 |
|
711 YIELDFROM = YIELD |
|
712 |
705 def FUNCTIONDEF(self, node): |
713 def FUNCTIONDEF(self, node): |
706 for deco in node.decorator_list: |
714 for deco in node.decorator_list: |
707 self.handleNode(deco, node) |
715 self.handleNode(deco, node) |
|
716 self.LAMBDA(node) |
708 self.addBinding(node, FunctionDefinition(node.name, node)) |
717 self.addBinding(node, FunctionDefinition(node.name, node)) |
709 self.LAMBDA(node) |
|
710 if self.withDoctest: |
718 if self.withDoctest: |
711 self.deferFunction(lambda: self.handleDoctests(node)) |
719 self.deferFunction(lambda: self.handleDoctests(node)) |
712 |
720 |
713 def LAMBDA(self, node): |
721 def LAMBDA(self, node): |
714 args = [] |
722 args = [] |
|
723 annotations = [] |
715 |
724 |
716 if PY2: |
725 if PY2: |
717 def addArgs(arglist): |
726 def addArgs(arglist): |
718 for arg in arglist: |
727 for arg in arglist: |
719 if isinstance(arg, ast.Tuple): |
728 if isinstance(arg, ast.Tuple): |
720 addArgs(arg.elts) |
729 addArgs(arg.elts) |
721 else: |
730 else: |
722 if arg.id in args: |
|
723 self.report(messages.DuplicateArgument, |
|
724 node, arg.id) |
|
725 args.append(arg.id) |
731 args.append(arg.id) |
726 addArgs(node.args.args) |
732 addArgs(node.args.args) |
727 defaults = node.args.defaults |
733 defaults = node.args.defaults |
728 else: |
734 else: |
729 for arg in node.args.args + node.args.kwonlyargs: |
735 for arg in node.args.args + node.args.kwonlyargs: |
730 if arg.arg in args: |
|
731 self.report(messages.DuplicateArgument, |
|
732 node, arg.arg) |
|
733 args.append(arg.arg) |
736 args.append(arg.arg) |
734 self.handleNode(arg.annotation, node) |
737 annotations.append(arg.annotation) |
735 if hasattr(node, 'returns'): # Only for FunctionDefs |
|
736 for annotation in (node.args.varargannotation, |
|
737 node.args.kwargannotation, node.returns): |
|
738 self.handleNode(annotation, node) |
|
739 defaults = node.args.defaults + node.args.kw_defaults |
738 defaults = node.args.defaults + node.args.kw_defaults |
740 |
739 |
741 # vararg/kwarg identifiers are not Name nodes |
740 # Only for Python3 FunctionDefs |
742 for wildcard in (node.args.vararg, node.args.kwarg): |
741 is_py3_func = hasattr(node, 'returns') |
|
742 |
|
743 for arg_name in ('vararg', 'kwarg'): |
|
744 wildcard = getattr(node.args, arg_name) |
743 if not wildcard: |
745 if not wildcard: |
744 continue |
746 continue |
745 if wildcard in args: |
747 args.append(wildcard if PY33 else wildcard.arg) |
746 self.report(messages.DuplicateArgument, node, wildcard) |
748 if is_py3_func: |
747 args.append(wildcard) |
749 if PY33: # Python 2.5 to 3.3 |
748 for default in defaults: |
750 argannotation = arg_name + 'annotation' |
749 self.handleNode(default, node) |
751 annotations.append(getattr(node.args, argannotation)) |
|
752 else: # Python >= 3.4 |
|
753 annotations.append(wildcard.annotation) |
|
754 |
|
755 if is_py3_func: |
|
756 annotations.append(node.returns) |
|
757 |
|
758 if len(set(args)) < len(args): |
|
759 for (idx, arg) in enumerate(args): |
|
760 if arg in args[:idx]: |
|
761 self.report(messages.DuplicateArgument, node, arg) |
|
762 |
|
763 for child in annotations + defaults: |
|
764 if child: |
|
765 self.handleNode(child, node) |
750 |
766 |
751 def runFunction(): |
767 def runFunction(): |
752 |
768 |
753 self.pushScope() |
769 self.pushScope() |
754 for name in args: |
770 for name in args: |
755 self.addBinding(node, Argument(name, node), reportRedef=False) |
771 self.addBinding(node, Argument(name, node)) |
756 if isinstance(node.body, list): |
772 if isinstance(node.body, list): |
757 # case for FunctionDefs |
773 # case for FunctionDefs |
758 for stmt in node.body: |
774 for stmt in node.body: |
759 self.handleNode(stmt, node) |
775 self.handleNode(stmt, node) |
760 else: |
776 else: |