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

changeset 7617
a0e162a50ad7
parent 7395
dd50d0f4c588
child 7639
422fd05e9c91
equal deleted inserted replaced
7616:01d646569115 7617:a0e162a50ad7
13 """ 13 """
14 import __future__ 14 import __future__
15 import ast 15 import ast
16 import bisect 16 import bisect
17 import collections 17 import collections
18 import contextlib
18 import doctest 19 import doctest
19 import functools 20 import functools
20 import os 21 import os
21 import re 22 import re
22 import string 23 import string
82 FUNCTION_TYPES = (ast.FunctionDef, ast.AsyncFunctionDef) 83 FUNCTION_TYPES = (ast.FunctionDef, ast.AsyncFunctionDef)
83 else: 84 else:
84 FOR_TYPES = (ast.For,) 85 FOR_TYPES = (ast.For,)
85 LOOP_TYPES = (ast.While, ast.For) 86 LOOP_TYPES = (ast.While, ast.For)
86 FUNCTION_TYPES = (ast.FunctionDef,) 87 FUNCTION_TYPES = (ast.FunctionDef,)
88
89
90 if PY38_PLUS:
91 def _is_singleton(node): # type: (ast.AST) -> bool
92 return (
93 isinstance(node, ast.Constant) and
94 isinstance(node.value, (bool, type(Ellipsis), type(None)))
95 )
96 elif not PY2:
97 def _is_singleton(node): # type: (ast.AST) -> bool
98 return isinstance(node, (ast.NameConstant, ast.Ellipsis))
99 else:
100 def _is_singleton(node): # type: (ast.AST) -> bool
101 return (
102 isinstance(node, ast.Name) and
103 node.id in {'True', 'False', 'Ellipsis', 'None'}
104 )
105
106
107 def _is_tuple_constant(node): # type: (ast.AST) -> bool
108 return (
109 isinstance(node, ast.Tuple) and
110 all(_is_constant(elt) for elt in node.elts)
111 )
112
113
114 if PY38_PLUS:
115 def _is_constant(node):
116 return isinstance(node, ast.Constant) or _is_tuple_constant(node)
117 else:
118 _const_tps = (ast.Str, ast.Num)
119 if not PY2:
120 _const_tps += (ast.Bytes,)
121
122 def _is_constant(node):
123 return (
124 isinstance(node, _const_tps) or
125 _is_singleton(node) or
126 _is_tuple_constant(node)
127 )
128
129
130 def _is_const_non_singleton(node): # type: (ast.AST) -> bool
131 return _is_constant(node) and not _is_singleton(node)
132
87 133
88 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104 134 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104
89 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*') 135 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*')
90 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413 136 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413
91 ASCII_NON_ALNUM = ''.join([chr(i) for i in range(128) if not chr(i).isalnum()]) 137 ASCII_NON_ALNUM = ''.join([chr(i) for i in range(128) if not chr(i).isalnum()])
623 return node.id 669 return node.id
624 if hasattr(node, 'name'): # an ExceptHandler node 670 if hasattr(node, 'name'): # an ExceptHandler node
625 return node.name 671 return node.name
626 672
627 673
628 def is_typing_overload(value, scope_stack): 674 TYPING_MODULES = frozenset(('typing', 'typing_extensions'))
629 def name_is_typing_overload(name): # type: (str) -> bool 675
676
677 def _is_typing_helper(node, is_name_match_fn, scope_stack):
678 """
679 Internal helper to determine whether or not something is a member of a
680 typing module. This is used as part of working out whether we are within a
681 type annotation context.
682
683 Note: you probably don't want to use this function directly. Instead see the
684 utils below which wrap it (`_is_typing` and `_is_any_typing_member`).
685 """
686
687 def _bare_name_is_attr(name):
630 for scope in reversed(scope_stack): 688 for scope in reversed(scope_stack):
631 if name in scope: 689 if name in scope:
632 return ( 690 return (
633 isinstance(scope[name], ImportationFrom) and 691 isinstance(scope[name], ImportationFrom) and
634 scope[name].fullName in ( 692 scope[name].module in TYPING_MODULES and
635 'typing.overload', 'typing_extensions.overload', 693 is_name_match_fn(scope[name].real_name)
636 )
637 ) 694 )
638 695
639 return False 696 return False
640 697
641 def is_typing_overload_decorator(node): 698 return (
642 return ( 699 (
643 ( 700 isinstance(node, ast.Name) and
644 isinstance(node, ast.Name) and name_is_typing_overload(node.id) 701 _bare_name_is_attr(node.id)
645 ) or ( 702 ) or (
646 isinstance(node, ast.Attribute) and 703 isinstance(node, ast.Attribute) and
647 isinstance(node.value, ast.Name) and 704 isinstance(node.value, ast.Name) and
648 node.value.id == 'typing' and 705 node.value.id in TYPING_MODULES and
649 node.attr == 'overload' 706 is_name_match_fn(node.attr)
650 )
651 ) 707 )
652 708 )
709
710
711 def _is_typing(node, typing_attr, scope_stack):
712 """
713 Determine whether `node` represents the member of a typing module specified
714 by `typing_attr`.
715
716 This is used as part of working out whether we are within a type annotation
717 context.
718 """
719 return _is_typing_helper(node, lambda x: x == typing_attr, scope_stack)
720
721
722 def _is_any_typing_member(node, scope_stack):
723 """
724 Determine whether `node` represents any member of a typing module.
725
726 This is used as part of working out whether we are within a type annotation
727 context.
728 """
729 return _is_typing_helper(node, lambda x: True, scope_stack)
730
731
732 def is_typing_overload(value, scope_stack):
653 return ( 733 return (
654 isinstance(value.source, FUNCTION_TYPES) and 734 isinstance(value.source, FUNCTION_TYPES) and
655 any( 735 any(
656 is_typing_overload_decorator(dec) 736 _is_typing(dec, 'overload', scope_stack)
657 for dec in value.source.decorator_list 737 for dec in value.source.decorator_list
658 ) 738 )
659 ) 739 )
740
741
742 def in_annotation(func):
743 @functools.wraps(func)
744 def in_annotation_func(self, *args, **kwargs):
745 with self._enter_annotation():
746 return func(self, *args, **kwargs)
747 return in_annotation_func
660 748
661 749
662 def make_tokens(code): 750 def make_tokens(code):
663 # PY3: tokenize.tokenize requires readline of bytes 751 # PY3: tokenize.tokenize requires readline of bytes
664 if not isinstance(code, bytes): 752 if not isinstance(code, bytes):
738 ast.SetComp: GeneratorScope, 826 ast.SetComp: GeneratorScope,
739 ast.GeneratorExp: GeneratorScope, 827 ast.GeneratorExp: GeneratorScope,
740 ast.DictComp: GeneratorScope, 828 ast.DictComp: GeneratorScope,
741 } 829 }
742 if PY35_PLUS: 830 if PY35_PLUS:
743 _ast_node_scope[ast.AsyncFunctionDef] = FunctionScope, 831 _ast_node_scope[ast.AsyncFunctionDef] = FunctionScope
744 832
745 nodeDepth = 0 833 nodeDepth = 0
746 offset = None 834 offset = None
747 traceTree = False 835 traceTree = False
836 _in_annotation = False
837 _in_typing_literal = False
838 _in_deferred = False
748 839
749 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS) 840 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
750 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS') 841 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS')
751 if _customBuiltIns: 842 if _customBuiltIns:
752 builtIns.update(_customBuiltIns.split(',')) 843 builtIns.update(_customBuiltIns.split(','))
774 self.root = tree 865 self.root = tree
775 self._type_comments = _collect_type_comments(tree, file_tokens) 866 self._type_comments = _collect_type_comments(tree, file_tokens)
776 for builtin in self.builtIns: 867 for builtin in self.builtIns:
777 self.addBinding(None, Builtin(builtin)) 868 self.addBinding(None, Builtin(builtin))
778 self.handleChildren(tree) 869 self.handleChildren(tree)
870 self._in_deferred = True
779 self.runDeferred(self._deferredFunctions) 871 self.runDeferred(self._deferredFunctions)
780 # Set _deferredFunctions to None so that deferFunction will fail 872 # Set _deferredFunctions to None so that deferFunction will fail
781 # noisily if called after we've run through the deferred functions. 873 # noisily if called after we've run through the deferred functions.
782 self._deferredFunctions = None 874 self._deferredFunctions = None
783 self.runDeferred(self._deferredAssignments) 875 self.runDeferred(self._deferredAssignments)
1014 # then assume the rebound name is used as a global or within a loop 1106 # then assume the rebound name is used as a global or within a loop
1015 value.used = self.scope[value.name].used 1107 value.used = self.scope[value.name].used
1016 1108
1017 self.scope[value.name] = value 1109 self.scope[value.name] = value
1018 1110
1111 def _unknown_handler(self, node):
1112 # this environment variable configures whether to error on unknown
1113 # ast types.
1114 #
1115 # this is silent by default but the error is enabled for the pyflakes
1116 # testsuite.
1117 #
1118 # this allows new syntax to be added to python without *requiring*
1119 # changes from the pyflakes side. but will still produce an error
1120 # in the pyflakes testsuite (so more specific handling can be added if
1121 # needed).
1122 if os.environ.get('PYFLAKES_ERROR_UNKNOWN'):
1123 raise NotImplementedError('Unexpected type: {}'.format(type(node)))
1124 else:
1125 self.handleChildren(node)
1126
1019 def getNodeHandler(self, node_class): 1127 def getNodeHandler(self, node_class):
1020 try: 1128 try:
1021 return self._nodeHandlers[node_class] 1129 return self._nodeHandlers[node_class]
1022 except KeyError: 1130 except KeyError:
1023 nodeType = getNodeType(node_class) 1131 nodeType = getNodeType(node_class)
1024 self._nodeHandlers[node_class] = handler = getattr(self, nodeType) 1132 self._nodeHandlers[node_class] = handler = getattr(
1133 self, nodeType, self._unknown_handler,
1134 )
1025 return handler 1135 return handler
1026 1136
1027 def handleNodeLoad(self, node): 1137 def handleNodeLoad(self, node):
1028 name = getNodeName(node) 1138 name = getNodeName(node)
1029 if not name: 1139 if not name:
1123 parent_stmt != node._pyflakes_parent and 1233 parent_stmt != node._pyflakes_parent and
1124 not self.isLiteralTupleUnpacking(parent_stmt)): 1234 not self.isLiteralTupleUnpacking(parent_stmt)):
1125 binding = Binding(name, node) 1235 binding = Binding(name, node)
1126 elif name == '__all__' and isinstance(self.scope, ModuleScope): 1236 elif name == '__all__' and isinstance(self.scope, ModuleScope):
1127 binding = ExportBinding(name, node._pyflakes_parent, self.scope) 1237 binding = ExportBinding(name, node._pyflakes_parent, self.scope)
1128 elif isinstance(getattr(node, 'ctx', None), ast.Param): 1238 elif PY2 and isinstance(getattr(node, 'ctx', None), ast.Param):
1129 binding = Argument(name, self.getScopeNode(node)) 1239 binding = Argument(name, self.getScopeNode(node))
1130 else: 1240 else:
1131 binding = Assignment(name, node) 1241 binding = Assignment(name, node)
1132 self.addBinding(node, binding) 1242 self.addBinding(node, binding)
1133 1243
1158 else: 1268 else:
1159 try: 1269 try:
1160 del self.scope[name] 1270 del self.scope[name]
1161 except KeyError: 1271 except KeyError:
1162 self.report(messages.UndefinedName, node, name) 1272 self.report(messages.UndefinedName, node, name)
1273
1274 @contextlib.contextmanager
1275 def _enter_annotation(self):
1276 orig, self._in_annotation = self._in_annotation, True
1277 try:
1278 yield
1279 finally:
1280 self._in_annotation = orig
1163 1281
1164 def _handle_type_comments(self, node): 1282 def _handle_type_comments(self, node):
1165 for (lineno, col_offset), comment in self._type_comments.get(node, ()): 1283 for (lineno, col_offset), comment in self._type_comments.get(node, ()):
1166 comment = comment.split(':', 1)[1].strip() 1284 comment = comment.split(':', 1)[1].strip()
1167 func_match = TYPE_FUNC_RE.match(comment) 1285 func_match = TYPE_FUNC_RE.match(comment)
1286 self.handleChildren(tree) 1404 self.handleChildren(tree)
1287 self.offset = node_offset 1405 self.offset = node_offset
1288 self.popScope() 1406 self.popScope()
1289 self.scopeStack = saved_stack 1407 self.scopeStack = saved_stack
1290 1408
1409 @in_annotation
1291 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err): 1410 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err):
1292 try: 1411 try:
1293 tree = ast.parse(s) 1412 tree = ast.parse(s)
1294 except SyntaxError: 1413 except SyntaxError:
1295 self.report(err, node, s) 1414 self.report(err, node, s)
1309 descendant.lineno = ref_lineno 1428 descendant.lineno = ref_lineno
1310 descendant.col_offset = ref_col_offset 1429 descendant.col_offset = ref_col_offset
1311 1430
1312 self.handleNode(parsed_annotation, node) 1431 self.handleNode(parsed_annotation, node)
1313 1432
1433 @in_annotation
1314 def handleAnnotation(self, annotation, node): 1434 def handleAnnotation(self, annotation, node):
1315 if isinstance(annotation, ast.Str): 1435 if isinstance(annotation, ast.Str):
1316 # Defer handling forward annotation. 1436 # Defer handling forward annotation.
1317 self.deferFunction(functools.partial( 1437 self.deferFunction(functools.partial(
1318 self.handleStringAnnotation, 1438 self.handleStringAnnotation,
1321 annotation.lineno, 1441 annotation.lineno,
1322 annotation.col_offset, 1442 annotation.col_offset,
1323 messages.ForwardAnnotationSyntaxError, 1443 messages.ForwardAnnotationSyntaxError,
1324 )) 1444 ))
1325 elif self.annotationsFutureEnabled: 1445 elif self.annotationsFutureEnabled:
1326 self.deferFunction(lambda: self.handleNode(annotation, node)) 1446 fn = in_annotation(Checker.handleNode)
1447 self.deferFunction(lambda: fn(self, annotation, node))
1327 else: 1448 else:
1328 self.handleNode(annotation, node) 1449 self.handleNode(annotation, node)
1329 1450
1330 def ignore(self, node): 1451 def ignore(self, node):
1331 pass 1452 pass
1332 1453
1333 # "stmt" type nodes 1454 # "stmt" type nodes
1334 DELETE = PRINT = FOR = ASYNCFOR = WHILE = IF = WITH = WITHITEM = \ 1455 DELETE = PRINT = FOR = ASYNCFOR = WHILE = WITH = WITHITEM = \
1335 ASYNCWITH = ASYNCWITHITEM = TRYFINALLY = EXEC = \ 1456 ASYNCWITH = ASYNCWITHITEM = TRYFINALLY = EXEC = \
1336 EXPR = ASSIGN = handleChildren 1457 EXPR = ASSIGN = handleChildren
1337 1458
1338 PASS = ignore 1459 PASS = ignore
1339 1460
1340 # "expr" type nodes 1461 # "expr" type nodes
1341 BOOLOP = UNARYOP = IFEXP = SET = \ 1462 BOOLOP = UNARYOP = SET = \
1342 REPR = ATTRIBUTE = SUBSCRIPT = \ 1463 REPR = ATTRIBUTE = \
1343 STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren 1464 STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren
1465
1466 def SUBSCRIPT(self, node):
1467 if (
1468 (
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)
1479 finally:
1480 self._in_typing_literal = orig
1481 else:
1482 if _is_any_typing_member(node.value, self.scopeStack):
1483 with self._enter_annotation():
1484 self.handleChildren(node)
1485 else:
1486 self.handleChildren(node)
1344 1487
1345 def _handle_string_dot_format(self, node): 1488 def _handle_string_dot_format(self, node):
1346 try: 1489 try:
1347 placeholders = tuple(parse_format_string(node.func.value.s)) 1490 placeholders = tuple(parse_format_string(node.func.value.s))
1348 except ValueError as e: 1491 except ValueError as e:
1467 isinstance(node.func, ast.Attribute) and 1610 isinstance(node.func, ast.Attribute) and
1468 isinstance(node.func.value, ast.Str) and 1611 isinstance(node.func.value, ast.Str) and
1469 node.func.attr == 'format' 1612 node.func.attr == 'format'
1470 ): 1613 ):
1471 self._handle_string_dot_format(node) 1614 self._handle_string_dot_format(node)
1615
1616 if (
1617 _is_typing(node.func, 'cast', self.scopeStack) and
1618 len(node.args) >= 1 and
1619 isinstance(node.args[0], ast.Str)
1620 ):
1621 with self._enter_annotation():
1622 self.handleNode(node.args[0], node)
1623
1472 self.handleChildren(node) 1624 self.handleChildren(node)
1473 1625
1474 def _handle_percent_format(self, node): 1626 def _handle_percent_format(self, node):
1475 try: 1627 try:
1476 placeholders = parse_percent_format(node.left.s) 1628 placeholders = parse_percent_format(node.left.s)
1580 isinstance(node.left, ast.Str) 1732 isinstance(node.left, ast.Str)
1581 ): 1733 ):
1582 self._handle_percent_format(node) 1734 self._handle_percent_format(node)
1583 self.handleChildren(node) 1735 self.handleChildren(node)
1584 1736
1585 NUM = STR = BYTES = ELLIPSIS = CONSTANT = ignore 1737 def STR(self, node):
1738 if self._in_annotation and not self._in_typing_literal:
1739 fn = functools.partial(
1740 self.handleStringAnnotation,
1741 node.s,
1742 node,
1743 node.lineno,
1744 node.col_offset,
1745 messages.ForwardAnnotationSyntaxError,
1746 )
1747 if self._in_deferred:
1748 fn()
1749 else:
1750 self.deferFunction(fn)
1751
1752 if PY38_PLUS:
1753 def CONSTANT(self, node):
1754 if isinstance(node.value, str):
1755 return self.STR(node)
1756 else:
1757 NUM = BYTES = ELLIPSIS = CONSTANT = ignore
1586 1758
1587 # "slice" type nodes 1759 # "slice" type nodes
1588 SLICE = EXTSLICE = INDEX = handleChildren 1760 SLICE = EXTSLICE = INDEX = handleChildren
1589 1761
1590 # expression contexts are node instances too, though being constants 1762 # expression contexts are node instances too, though being constants
1663 key_node, 1835 key_node,
1664 key, 1836 key,
1665 ) 1837 )
1666 self.handleChildren(node) 1838 self.handleChildren(node)
1667 1839
1840 def IF(self, node):
1841 if isinstance(node.test, ast.Tuple) and node.test.elts != []:
1842 self.report(messages.IfTuple, node)
1843 self.handleChildren(node)
1844
1845 IFEXP = IF
1846
1668 def ASSERT(self, node): 1847 def ASSERT(self, node):
1669 if isinstance(node.test, ast.Tuple) and node.test.elts != []: 1848 if isinstance(node.test, ast.Tuple) and node.test.elts != []:
1670 self.report(messages.AssertTuple, node) 1849 self.report(messages.AssertTuple, node)
1671 self.handleChildren(node) 1850 self.handleChildren(node)
1672 1851
1714 def NAME(self, node): 1893 def NAME(self, node):
1715 """ 1894 """
1716 Handle occurrence of Name (which can be a load/store/delete access.) 1895 Handle occurrence of Name (which can be a load/store/delete access.)
1717 """ 1896 """
1718 # Locate the name in locals / function / globals scopes. 1897 # Locate the name in locals / function / globals scopes.
1719 if isinstance(node.ctx, (ast.Load, ast.AugLoad)): 1898 if isinstance(node.ctx, ast.Load):
1720 self.handleNodeLoad(node) 1899 self.handleNodeLoad(node)
1721 if (node.id == 'locals' and isinstance(self.scope, FunctionScope) and 1900 if (node.id == 'locals' and isinstance(self.scope, FunctionScope) and
1722 isinstance(node._pyflakes_parent, ast.Call)): 1901 isinstance(node._pyflakes_parent, ast.Call)):
1723 # we are doing locals() call in current scope 1902 # we are doing locals() call in current scope
1724 self.scope.usesLocals = True 1903 self.scope.usesLocals = True
1725 elif isinstance(node.ctx, (ast.Store, ast.AugStore, ast.Param)): 1904 elif isinstance(node.ctx, ast.Store):
1905 self.handleNodeStore(node)
1906 elif PY2 and isinstance(node.ctx, ast.Param):
1726 self.handleNodeStore(node) 1907 self.handleNodeStore(node)
1727 elif isinstance(node.ctx, ast.Del): 1908 elif isinstance(node.ctx, ast.Del):
1728 self.handleNodeDelete(node) 1909 self.handleNodeDelete(node)
1729 else: 1910 else:
1730 # Unknown context 1911 # Unknown context
2059 if node.value: 2240 if node.value:
2060 # If the assignment has value, handle the *value* now. 2241 # If the assignment has value, handle the *value* now.
2061 self.handleNode(node.value, node) 2242 self.handleNode(node.value, node)
2062 2243
2063 def COMPARE(self, node): 2244 def COMPARE(self, node):
2064 literals = (ast.Str, ast.Num)
2065 if not PY2:
2066 literals += (ast.Bytes,)
2067
2068 left = node.left 2245 left = node.left
2069 for op, right in zip(node.ops, node.comparators): 2246 for op, right in zip(node.ops, node.comparators):
2070 if (isinstance(op, (ast.Is, ast.IsNot)) and 2247 if (
2071 (isinstance(left, literals) or isinstance(right, literals))): 2248 isinstance(op, (ast.Is, ast.IsNot)) and (
2249 _is_const_non_singleton(left) or
2250 _is_const_non_singleton(right)
2251 )
2252 ):
2072 self.report(messages.IsLiteral, node) 2253 self.report(messages.IsLiteral, node)
2073 left = right 2254 left = right
2074 2255
2075 self.handleChildren(node) 2256 self.handleChildren(node)
2076 2257

eric ide

mercurial