127 ) |
131 ) |
128 |
132 |
129 |
133 |
130 def _is_const_non_singleton(node): # type: (ast.AST) -> bool |
134 def _is_const_non_singleton(node): # type: (ast.AST) -> bool |
131 return _is_constant(node) and not _is_singleton(node) |
135 return _is_constant(node) and not _is_singleton(node) |
|
136 |
|
137 |
|
138 def _is_name_or_attr(node, name): # type: (ast.Ast, str) -> bool |
|
139 return ( |
|
140 (isinstance(node, ast.Name) and node.id == name) or |
|
141 (isinstance(node, ast.Attribute) and node.attr == name) |
|
142 ) |
132 |
143 |
133 |
144 |
134 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104 |
145 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104 |
135 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*') |
146 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*') |
136 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413 |
147 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413 |
388 """ |
399 """ |
389 |
400 |
390 def __init__(self, name, source, full_name=None): |
401 def __init__(self, name, source, full_name=None): |
391 self.fullName = full_name or name |
402 self.fullName = full_name or name |
392 self.redefined = [] |
403 self.redefined = [] |
393 super(Importation, self).__init__(name, source) |
404 super().__init__(name, source) |
394 |
405 |
395 def redefines(self, other): |
406 def redefines(self, other): |
396 if isinstance(other, SubmoduleImportation): |
407 if isinstance(other, SubmoduleImportation): |
397 # See note in SubmoduleImportation about RedefinedWhileUnused |
408 # See note in SubmoduleImportation about RedefinedWhileUnused |
398 return self.fullName == other.fullName |
409 return self.fullName == other.fullName |
437 |
448 |
438 def __init__(self, name, source): |
449 def __init__(self, name, source): |
439 # A dot should only appear in the name when it is a submodule import |
450 # A dot should only appear in the name when it is a submodule import |
440 assert '.' in name and (not source or isinstance(source, ast.Import)) |
451 assert '.' in name and (not source or isinstance(source, ast.Import)) |
441 package_name = name.split('.')[0] |
452 package_name = name.split('.')[0] |
442 super(SubmoduleImportation, self).__init__(package_name, source) |
453 super().__init__(package_name, source) |
443 self.fullName = name |
454 self.fullName = name |
444 |
455 |
445 def redefines(self, other): |
456 def redefines(self, other): |
446 if isinstance(other, Importation): |
457 if isinstance(other, Importation): |
447 return self.fullName == other.fullName |
458 return self.fullName == other.fullName |
448 return super(SubmoduleImportation, self).redefines(other) |
459 return super().redefines(other) |
449 |
460 |
450 def __str__(self): |
461 def __str__(self): |
451 return self.fullName |
462 return self.fullName |
452 |
463 |
453 @property |
464 @property |
464 if module.endswith('.'): |
475 if module.endswith('.'): |
465 full_name = module + self.real_name |
476 full_name = module + self.real_name |
466 else: |
477 else: |
467 full_name = module + '.' + self.real_name |
478 full_name = module + '.' + self.real_name |
468 |
479 |
469 super(ImportationFrom, self).__init__(name, source, full_name) |
480 super().__init__(name, source, full_name) |
470 |
481 |
471 def __str__(self): |
482 def __str__(self): |
472 """Return import full name with alias.""" |
483 """Return import full name with alias.""" |
473 if self.real_name != self.name: |
484 if self.real_name != self.name: |
474 return self.fullName + ' as ' + self.name |
485 return self.fullName + ' as ' + self.name |
487 |
498 |
488 class StarImportation(Importation): |
499 class StarImportation(Importation): |
489 """A binding created by a 'from x import *' statement.""" |
500 """A binding created by a 'from x import *' statement.""" |
490 |
501 |
491 def __init__(self, name, source): |
502 def __init__(self, name, source): |
492 super(StarImportation, self).__init__('*', source) |
503 super().__init__('*', source) |
493 # Each star importation needs a unique name, and |
504 # Each star importation needs a unique name, and |
494 # may not be the module name otherwise it will be deemed imported |
505 # may not be the module name otherwise it will be deemed imported |
495 self.name = name + '.*' |
506 self.name = name + '.*' |
496 self.fullName = name |
507 self.fullName = name |
497 |
508 |
533 the checker does not consider assignments in tuple/list unpacking to be |
544 the checker does not consider assignments in tuple/list unpacking to be |
534 Assignments, rather it treats them as simple Bindings. |
545 Assignments, rather it treats them as simple Bindings. |
535 """ |
546 """ |
536 |
547 |
537 |
548 |
|
549 class Annotation(Binding): |
|
550 """ |
|
551 Represents binding a name to a type without an associated value. |
|
552 |
|
553 As long as this name is not assigned a value in another binding, it is considered |
|
554 undefined for most purposes. One notable exception is using the name as a type |
|
555 annotation. |
|
556 """ |
|
557 |
|
558 def redefines(self, other): |
|
559 """An Annotation doesn't define any name, so it cannot redefine one.""" |
|
560 return False |
|
561 |
|
562 |
538 class FunctionDefinition(Definition): |
563 class FunctionDefinition(Definition): |
539 pass |
564 pass |
540 |
565 |
541 |
566 |
542 class ClassDefinition(Definition): |
567 class ClassDefinition(Definition): |
569 if isinstance(node, ast.Str): |
594 if isinstance(node, ast.Str): |
570 self.names.append(node.s) |
595 self.names.append(node.s) |
571 |
596 |
572 if isinstance(source.value, (ast.List, ast.Tuple)): |
597 if isinstance(source.value, (ast.List, ast.Tuple)): |
573 _add_to_names(source.value) |
598 _add_to_names(source.value) |
574 # If concatenating lists |
599 # If concatenating lists or tuples |
575 elif isinstance(source.value, ast.BinOp): |
600 elif isinstance(source.value, ast.BinOp): |
576 currentValue = source.value |
601 currentValue = source.value |
577 while isinstance(currentValue.right, ast.List): |
602 while isinstance(currentValue.right, (ast.List, ast.Tuple)): |
578 left = currentValue.left |
603 left = currentValue.left |
579 right = currentValue.right |
604 right = currentValue.right |
580 _add_to_names(right) |
605 _add_to_names(right) |
581 # If more lists are being added |
606 # If more lists are being added |
582 if isinstance(left, ast.BinOp): |
607 if isinstance(left, ast.BinOp): |
583 currentValue = left |
608 currentValue = left |
584 # If just two lists are being added |
609 # If just two lists are being added |
585 elif isinstance(left, ast.List): |
610 elif isinstance(left, (ast.List, ast.Tuple)): |
586 _add_to_names(left) |
611 _add_to_names(left) |
587 # All lists accounted for - done |
612 # All lists accounted for - done |
588 break |
613 break |
589 # If not list concatenation |
614 # If not list concatenation |
590 else: |
615 else: |
591 break |
616 break |
592 super(ExportBinding, self).__init__(name, source) |
617 super().__init__(name, source) |
593 |
618 |
594 |
619 |
595 class Scope(dict): |
620 class Scope(dict): |
596 importStarred = False # set to True when import * is found |
621 importStarred = False # set to True when import * is found |
597 |
622 |
613 usesLocals = False |
638 usesLocals = False |
614 alwaysUsed = {'__tracebackhide__', '__traceback_info__', |
639 alwaysUsed = {'__tracebackhide__', '__traceback_info__', |
615 '__traceback_supplement__'} |
640 '__traceback_supplement__'} |
616 |
641 |
617 def __init__(self): |
642 def __init__(self): |
618 super(FunctionScope, self).__init__() |
643 super().__init__() |
619 # Simplify: manage the special locals as globals |
644 # Simplify: manage the special locals as globals |
620 self.globals = self.alwaysUsed.copy() |
645 self.globals = self.alwaysUsed.copy() |
621 self.returnValue = None # First non-empty return |
646 self.returnValue = None # First non-empty return |
622 self.isGenerator = False # Detect a generator |
647 self.isGenerator = False # Detect a generator |
623 |
648 |
653 def __init__(self, lineno, col_offset): |
678 def __init__(self, lineno, col_offset): |
654 self.lineno = lineno |
679 self.lineno = lineno |
655 self.col_offset = col_offset |
680 self.col_offset = col_offset |
656 |
681 |
657 |
682 |
|
683 class DetectClassScopedMagic: |
|
684 names = dir() |
|
685 |
|
686 |
658 # Globally defined names which are not attributes of the builtins module, or |
687 # Globally defined names which are not attributes of the builtins module, or |
659 # are only present on some platforms. |
688 # are only present on some platforms. |
660 _MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError'] |
689 _MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError'] |
661 # module scope annotation will store in `__annotations__`, see also PEP 526. |
690 # module scope annotation will store in `__annotations__`, see also PEP 526. |
662 if PY36_PLUS: |
691 if PY36_PLUS: |
737 for dec in value.source.decorator_list |
766 for dec in value.source.decorator_list |
738 ) |
767 ) |
739 ) |
768 ) |
740 |
769 |
741 |
770 |
|
771 class AnnotationState: |
|
772 NONE = 0 |
|
773 STRING = 1 |
|
774 BARE = 2 |
|
775 |
|
776 |
742 def in_annotation(func): |
777 def in_annotation(func): |
743 @functools.wraps(func) |
778 @functools.wraps(func) |
744 def in_annotation_func(self, *args, **kwargs): |
779 def in_annotation_func(self, *args, **kwargs): |
745 with self._enter_annotation(): |
780 with self._enter_annotation(): |
|
781 return func(self, *args, **kwargs) |
|
782 return in_annotation_func |
|
783 |
|
784 |
|
785 def in_string_annotation(func): |
|
786 @functools.wraps(func) |
|
787 def in_annotation_func(self, *args, **kwargs): |
|
788 with self._enter_annotation(AnnotationState.STRING): |
746 return func(self, *args, **kwargs) |
789 return func(self, *args, **kwargs) |
747 return in_annotation_func |
790 return in_annotation_func |
748 |
791 |
749 |
792 |
750 def make_tokens(code): |
793 def make_tokens(code): |
1104 |
1149 |
1105 if value.name in self.scope: |
1150 if value.name in self.scope: |
1106 # then assume the rebound name is used as a global or within a loop |
1151 # then assume the rebound name is used as a global or within a loop |
1107 value.used = self.scope[value.name].used |
1152 value.used = self.scope[value.name].used |
1108 |
1153 |
1109 self.scope[value.name] = value |
1154 # don't treat annotations as assignments if there is an existing value |
|
1155 # in scope |
|
1156 if value.name not in self.scope or not isinstance(value, Annotation): |
|
1157 self.scope[value.name] = value |
1110 |
1158 |
1111 def _unknown_handler(self, node): |
1159 def _unknown_handler(self, node): |
1112 # this environment variable configures whether to error on unknown |
1160 # this environment variable configures whether to error on unknown |
1113 # ast types. |
1161 # ast types. |
1114 # |
1162 # |
1151 # only generators used in a class scope can access the |
1199 # only generators used in a class scope can access the |
1152 # names of the class. this is skipped during the first |
1200 # names of the class. this is skipped during the first |
1153 # iteration |
1201 # iteration |
1154 continue |
1202 continue |
1155 |
1203 |
1156 if (name == 'print' and |
1204 binding = scope.get(name, None) |
1157 isinstance(scope.get(name, None), Builtin)): |
1205 if isinstance(binding, Annotation) and not self._in_postponed_annotation: |
|
1206 continue |
|
1207 |
|
1208 if name == 'print' and isinstance(binding, Builtin): |
1158 parent = self.getParent(node) |
1209 parent = self.getParent(node) |
1159 if (isinstance(parent, ast.BinOp) and |
1210 if (isinstance(parent, ast.BinOp) and |
1160 isinstance(parent.op, ast.RShift)): |
1211 isinstance(parent.op, ast.RShift)): |
1161 self.report(messages.InvalidPrintSyntax, node) |
1212 self.report(messages.InvalidPrintSyntax, node) |
1162 |
1213 |
1199 |
1250 |
1200 if name == '__path__' and os.path.basename(self.filename) == '__init__.py': |
1251 if name == '__path__' and os.path.basename(self.filename) == '__init__.py': |
1201 # the special name __path__ is valid only in packages |
1252 # the special name __path__ is valid only in packages |
1202 return |
1253 return |
1203 |
1254 |
1204 if name == '__module__' and isinstance(self.scope, ClassScope): |
1255 if name in DetectClassScopedMagic.names and isinstance(self.scope, ClassScope): |
1205 return |
1256 return |
1206 |
1257 |
1207 # protected with a NameError handler? |
1258 # protected with a NameError handler? |
1208 if 'NameError' not in self.exceptHandlers[-1]: |
1259 if 'NameError' not in self.exceptHandlers[-1]: |
1209 self.report(messages.UndefinedName, node, name) |
1260 self.report(messages.UndefinedName, node, name) |
1227 self.report(messages.UndefinedLocal, |
1278 self.report(messages.UndefinedLocal, |
1228 scope[name].used[1], name, scope[name].source) |
1279 scope[name].used[1], name, scope[name].source) |
1229 break |
1280 break |
1230 |
1281 |
1231 parent_stmt = self.getParent(node) |
1282 parent_stmt = self.getParent(node) |
1232 if isinstance(parent_stmt, (FOR_TYPES, ast.comprehension)) or ( |
1283 if isinstance(parent_stmt, ANNASSIGN_TYPES) and parent_stmt.value is None: |
|
1284 binding = Annotation(name, node) |
|
1285 elif isinstance(parent_stmt, (FOR_TYPES, ast.comprehension)) or ( |
1233 parent_stmt != node._pyflakes_parent and |
1286 parent_stmt != node._pyflakes_parent and |
1234 not self.isLiteralTupleUnpacking(parent_stmt)): |
1287 not self.isLiteralTupleUnpacking(parent_stmt)): |
1235 binding = Binding(name, node) |
1288 binding = Binding(name, node) |
1236 elif name == '__all__' and isinstance(self.scope, ModuleScope): |
1289 elif name == '__all__' and isinstance(self.scope, ModuleScope): |
1237 binding = ExportBinding(name, node._pyflakes_parent, self.scope) |
1290 binding = ExportBinding(name, node._pyflakes_parent, self.scope) |
1270 del self.scope[name] |
1323 del self.scope[name] |
1271 except KeyError: |
1324 except KeyError: |
1272 self.report(messages.UndefinedName, node, name) |
1325 self.report(messages.UndefinedName, node, name) |
1273 |
1326 |
1274 @contextlib.contextmanager |
1327 @contextlib.contextmanager |
1275 def _enter_annotation(self): |
1328 def _enter_annotation(self, ann_type=AnnotationState.BARE): |
1276 orig, self._in_annotation = self._in_annotation, True |
1329 orig, self._in_annotation = self._in_annotation, ann_type |
1277 try: |
1330 try: |
1278 yield |
1331 yield |
1279 finally: |
1332 finally: |
1280 self._in_annotation = orig |
1333 self._in_annotation = orig |
|
1334 |
|
1335 @property |
|
1336 def _in_postponed_annotation(self): |
|
1337 return ( |
|
1338 self._in_annotation == AnnotationState.STRING or |
|
1339 self.annotationsFutureEnabled |
|
1340 ) |
1281 |
1341 |
1282 def _handle_type_comments(self, node): |
1342 def _handle_type_comments(self, node): |
1283 for (lineno, col_offset), comment in self._type_comments.get(node, ()): |
1343 for (lineno, col_offset), comment in self._type_comments.get(node, ()): |
1284 comment = comment.split(':', 1)[1].strip() |
1344 comment = comment.split(':', 1)[1].strip() |
1285 func_match = TYPE_FUNC_RE.match(comment) |
1345 func_match = TYPE_FUNC_RE.match(comment) |
1404 self.handleChildren(tree) |
1464 self.handleChildren(tree) |
1405 self.offset = node_offset |
1465 self.offset = node_offset |
1406 self.popScope() |
1466 self.popScope() |
1407 self.scopeStack = saved_stack |
1467 self.scopeStack = saved_stack |
1408 |
1468 |
1409 @in_annotation |
1469 @in_string_annotation |
1410 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err): |
1470 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err): |
1411 try: |
1471 try: |
1412 tree = ast.parse(s) |
1472 tree = ast.parse(s) |
1413 except SyntaxError: |
1473 except SyntaxError: |
1414 self.report(err, node, s) |
1474 self.report(err, node, s) |
1462 BOOLOP = UNARYOP = SET = \ |
1522 BOOLOP = UNARYOP = SET = \ |
1463 REPR = ATTRIBUTE = \ |
1523 REPR = ATTRIBUTE = \ |
1464 STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren |
1524 STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren |
1465 |
1525 |
1466 def SUBSCRIPT(self, node): |
1526 def SUBSCRIPT(self, node): |
1467 if ( |
1527 if _is_name_or_attr(node.value, 'Literal'): |
1468 ( |
1528 with self._enter_annotation(AnnotationState.NONE): |
1469 isinstance(node.value, ast.Name) and |
|
1470 node.value.id == 'Literal' |
|
1471 ) or ( |
|
1472 isinstance(node.value, ast.Attribute) and |
|
1473 node.value.attr == 'Literal' |
|
1474 ) |
|
1475 ): |
|
1476 orig, self._in_typing_literal = self._in_typing_literal, True |
|
1477 try: |
|
1478 self.handleChildren(node) |
1529 self.handleChildren(node) |
1479 finally: |
1530 elif _is_name_or_attr(node.value, 'Annotated'): |
1480 self._in_typing_literal = orig |
1531 self.handleNode(node.value, node) |
|
1532 |
|
1533 # py39+ |
|
1534 if isinstance(node.slice, ast.Tuple): |
|
1535 slice_tuple = node.slice |
|
1536 # <py39 |
|
1537 elif ( |
|
1538 isinstance(node.slice, ast.Index) and |
|
1539 isinstance(node.slice.value, ast.Tuple) |
|
1540 ): |
|
1541 slice_tuple = node.slice.value |
|
1542 else: |
|
1543 slice_tuple = None |
|
1544 |
|
1545 # not a multi-arg `Annotated` |
|
1546 if slice_tuple is None or len(slice_tuple.elts) < 2: |
|
1547 self.handleNode(node.slice, node) |
|
1548 else: |
|
1549 # the first argument is the type |
|
1550 self.handleNode(slice_tuple.elts[0], node) |
|
1551 # the rest of the arguments are not |
|
1552 with self._enter_annotation(AnnotationState.NONE): |
|
1553 for arg in slice_tuple.elts[1:]: |
|
1554 self.handleNode(arg, node) |
|
1555 |
|
1556 self.handleNode(node.ctx, node) |
1481 else: |
1557 else: |
1482 if _is_any_typing_member(node.value, self.scopeStack): |
1558 if _is_any_typing_member(node.value, self.scopeStack): |
1483 with self._enter_annotation(): |
1559 with self._enter_annotation(): |
1484 self.handleChildren(node) |
1560 self.handleChildren(node) |
1485 else: |
1561 else: |
1611 isinstance(node.func.value, ast.Str) and |
1687 isinstance(node.func.value, ast.Str) and |
1612 node.func.attr == 'format' |
1688 node.func.attr == 'format' |
1613 ): |
1689 ): |
1614 self._handle_string_dot_format(node) |
1690 self._handle_string_dot_format(node) |
1615 |
1691 |
|
1692 omit = [] |
|
1693 annotated = [] |
|
1694 not_annotated = [] |
|
1695 |
1616 if ( |
1696 if ( |
1617 _is_typing(node.func, 'cast', self.scopeStack) and |
1697 _is_typing(node.func, 'cast', self.scopeStack) and |
1618 len(node.args) >= 1 and |
1698 len(node.args) >= 1 |
1619 isinstance(node.args[0], ast.Str) |
|
1620 ): |
1699 ): |
1621 with self._enter_annotation(): |
1700 with self._enter_annotation(): |
1622 self.handleNode(node.args[0], node) |
1701 self.handleNode(node.args[0], node) |
1623 |
1702 |
1624 self.handleChildren(node) |
1703 elif _is_typing(node.func, 'TypeVar', self.scopeStack): |
|
1704 |
|
1705 # TypeVar("T", "int", "str") |
|
1706 omit += ["args"] |
|
1707 annotated += [arg for arg in node.args[1:]] |
|
1708 |
|
1709 # TypeVar("T", bound="str") |
|
1710 omit += ["keywords"] |
|
1711 annotated += [k.value for k in node.keywords if k.arg == "bound"] |
|
1712 not_annotated += [ |
|
1713 (k, ["value"] if k.arg == "bound" else None) |
|
1714 for k in node.keywords |
|
1715 ] |
|
1716 |
|
1717 elif _is_typing(node.func, "TypedDict", self.scopeStack): |
|
1718 # TypedDict("a", {"a": int}) |
|
1719 if len(node.args) > 1 and isinstance(node.args[1], ast.Dict): |
|
1720 omit += ["args"] |
|
1721 annotated += node.args[1].values |
|
1722 not_annotated += [ |
|
1723 (arg, ["values"] if i == 1 else None) |
|
1724 for i, arg in enumerate(node.args) |
|
1725 ] |
|
1726 |
|
1727 # TypedDict("a", a=int) |
|
1728 omit += ["keywords"] |
|
1729 annotated += [k.value for k in node.keywords] |
|
1730 not_annotated += [(k, ["value"]) for k in node.keywords] |
|
1731 |
|
1732 elif _is_typing(node.func, "NamedTuple", self.scopeStack): |
|
1733 # NamedTuple("a", [("a", int)]) |
|
1734 if ( |
|
1735 len(node.args) > 1 and |
|
1736 isinstance(node.args[1], (ast.Tuple, ast.List)) and |
|
1737 all(isinstance(x, (ast.Tuple, ast.List)) and |
|
1738 len(x.elts) == 2 for x in node.args[1].elts) |
|
1739 ): |
|
1740 omit += ["args"] |
|
1741 annotated += [elt.elts[1] for elt in node.args[1].elts] |
|
1742 not_annotated += [(elt.elts[0], None) for elt in node.args[1].elts] |
|
1743 not_annotated += [ |
|
1744 (arg, ["elts"] if i == 1 else None) |
|
1745 for i, arg in enumerate(node.args) |
|
1746 ] |
|
1747 not_annotated += [(elt, "elts") for elt in node.args[1].elts] |
|
1748 |
|
1749 # NamedTuple("a", a=int) |
|
1750 omit += ["keywords"] |
|
1751 annotated += [k.value for k in node.keywords] |
|
1752 not_annotated += [(k, ["value"]) for k in node.keywords] |
|
1753 |
|
1754 if omit: |
|
1755 with self._enter_annotation(AnnotationState.NONE): |
|
1756 for na_node, na_omit in not_annotated: |
|
1757 self.handleChildren(na_node, omit=na_omit) |
|
1758 self.handleChildren(node, omit=omit) |
|
1759 |
|
1760 with self._enter_annotation(): |
|
1761 for annotated_node in annotated: |
|
1762 self.handleNode(annotated_node, node) |
|
1763 else: |
|
1764 self.handleChildren(node) |
1625 |
1765 |
1626 def _handle_percent_format(self, node): |
1766 def _handle_percent_format(self, node): |
1627 try: |
1767 try: |
1628 placeholders = parse_percent_format(node.left.s) |
1768 placeholders = parse_percent_format(node.left.s) |
1629 except ValueError: |
1769 except ValueError: |
2229 # Restore. |
2369 # Restore. |
2230 if prev_definition: |
2370 if prev_definition: |
2231 self.scope[node.name] = prev_definition |
2371 self.scope[node.name] = prev_definition |
2232 |
2372 |
2233 def ANNASSIGN(self, node): |
2373 def ANNASSIGN(self, node): |
2234 if node.value: |
2374 self.handleNode(node.target, node) |
2235 # Only bind the *targets* if the assignment has a value. |
|
2236 # Otherwise it's not really ast.Store and shouldn't silence |
|
2237 # UndefinedLocal warnings. |
|
2238 self.handleNode(node.target, node) |
|
2239 self.handleAnnotation(node.annotation, node) |
2375 self.handleAnnotation(node.annotation, node) |
2240 if node.value: |
2376 if node.value: |
2241 # If the assignment has value, handle the *value* now. |
2377 # If the assignment has value, handle the *value* now. |
2242 self.handleNode(node.value, node) |
2378 self.handleNode(node.value, node) |
2243 |
2379 |