eric6/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py

changeset 8208
37836fa8e4ea
parent 7960
e8fc383322f7
child 8218
7c09585bd960
equal deleted inserted replaced
8207:d359172d11be 8208:37836fa8e4ea
84 else: 84 else:
85 FOR_TYPES = (ast.For,) 85 FOR_TYPES = (ast.For,)
86 LOOP_TYPES = (ast.While, ast.For) 86 LOOP_TYPES = (ast.While, ast.For)
87 FUNCTION_TYPES = (ast.FunctionDef,) 87 FUNCTION_TYPES = (ast.FunctionDef,)
88 88
89 if PY36_PLUS:
90 ANNASSIGN_TYPES = (ast.AnnAssign,)
91 else:
92 ANNASSIGN_TYPES = ()
89 93
90 if PY38_PLUS: 94 if PY38_PLUS:
91 def _is_singleton(node): # type: (ast.AST) -> bool 95 def _is_singleton(node): # type: (ast.AST) -> bool
92 return ( 96 return (
93 isinstance(node, ast.Constant) and 97 isinstance(node, ast.Constant) and
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
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):
547 """ 572 """
548 A binding created by an C{__all__} assignment. If the names in the list 573 A binding created by an C{__all__} assignment. If the names in the list
549 can be determined statically, they will be treated as names for export and 574 can be determined statically, they will be treated as names for export and
550 additional checking applied to them. 575 additional checking applied to them.
551 576
552 The only recognized C{__all__} assignment via list concatenation is in the 577 The only recognized C{__all__} assignment via list/tuple concatenation is in the
553 following format: 578 following format:
554 579
555 __all__ = ['a'] + ['b'] + ['c'] 580 __all__ = ['a'] + ['b'] + ['c']
556 581
557 Names which are imported and not otherwise used but appear in the value of 582 Names which are imported and not otherwise used but appear in the value of
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:
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):
831 _ast_node_scope[ast.AsyncFunctionDef] = FunctionScope 874 _ast_node_scope[ast.AsyncFunctionDef] = FunctionScope
832 875
833 nodeDepth = 0 876 nodeDepth = 0
834 offset = None 877 offset = None
835 traceTree = False 878 traceTree = False
836 _in_annotation = False 879 _in_annotation = AnnotationState.NONE
837 _in_typing_literal = False
838 _in_deferred = False 880 _in_deferred = False
839 881
840 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS) 882 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
841 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS') 883 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS')
842 if _customBuiltIns: 884 if _customBuiltIns:
959 if all_binding and not isinstance(all_binding, ExportBinding): 1001 if all_binding and not isinstance(all_binding, ExportBinding):
960 all_binding = None 1002 all_binding = None
961 1003
962 if all_binding: 1004 if all_binding:
963 all_names = set(all_binding.names) 1005 all_names = set(all_binding.names)
964 undefined = all_names.difference(scope) 1006 undefined = [
1007 name for name in all_binding.names
1008 if name not in scope
1009 ]
965 else: 1010 else:
966 all_names = undefined = [] 1011 all_names = undefined = []
967 1012
968 if undefined: 1013 if undefined:
969 if not scope.importStarred and \ 1014 if not scope.importStarred and \
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:
1733 ): 1873 ):
1734 self._handle_percent_format(node) 1874 self._handle_percent_format(node)
1735 self.handleChildren(node) 1875 self.handleChildren(node)
1736 1876
1737 def STR(self, node): 1877 def STR(self, node):
1738 if self._in_annotation and not self._in_typing_literal: 1878 if self._in_annotation:
1739 fn = functools.partial( 1879 fn = functools.partial(
1740 self.handleStringAnnotation, 1880 self.handleStringAnnotation,
1741 node.s, 1881 node.s,
1742 node, 1882 node,
1743 node.lineno, 1883 node.lineno,
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

eric ide

mercurial