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

branch
maintenance
changeset 7437
1148ca40ea36
parent 7391
894e3b08d5cf
parent 7395
dd50d0f4c588
child 7642
72721823d453
equal deleted inserted replaced
7391:894e3b08d5cf 7437:1148ca40ea36
17 import collections 17 import collections
18 import doctest 18 import doctest
19 import functools 19 import functools
20 import os 20 import os
21 import re 21 import re
22 import string
22 import sys 23 import sys
23 import tokenize 24 import tokenize
24 25
25 from . import messages 26 from . import messages
26 27
33 PYPY = True 34 PYPY = True
34 except AttributeError: 35 except AttributeError:
35 PYPY = False 36 PYPY = False
36 37
37 builtin_vars = dir(__import__('__builtin__' if PY2 else 'builtins')) 38 builtin_vars = dir(__import__('__builtin__' if PY2 else 'builtins'))
39
40 parse_format_string = string.Formatter().parse
38 41
39 if PY2: 42 if PY2:
40 tokenize_tokenize = tokenize.generate_tokens 43 tokenize_tokenize = tokenize.generate_tokens
41 else: 44 else:
42 tokenize_tokenize = tokenize.tokenize 45 tokenize_tokenize = tokenize.tokenize
74 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers] 77 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
75 78
76 if PY35_PLUS: 79 if PY35_PLUS:
77 FOR_TYPES = (ast.For, ast.AsyncFor) 80 FOR_TYPES = (ast.For, ast.AsyncFor)
78 LOOP_TYPES = (ast.While, ast.For, ast.AsyncFor) 81 LOOP_TYPES = (ast.While, ast.For, ast.AsyncFor)
82 FUNCTION_TYPES = (ast.FunctionDef, ast.AsyncFunctionDef)
79 else: 83 else:
80 FOR_TYPES = (ast.For,) 84 FOR_TYPES = (ast.For,)
81 LOOP_TYPES = (ast.While, ast.For) 85 LOOP_TYPES = (ast.While, ast.For)
82 86 FUNCTION_TYPES = (ast.FunctionDef,)
83 # https://github.com/python/typed_ast/blob/55420396/ast27/Parser/tokenizer.c#L102-L104 87
88 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L102-L104
84 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*') 89 TYPE_COMMENT_RE = re.compile(r'^#\s*type:\s*')
85 # https://github.com/python/typed_ast/blob/55420396/ast27/Parser/tokenizer.c#L1400 90 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Parser/tokenizer.c#L1408-L1413
86 TYPE_IGNORE_RE = re.compile(TYPE_COMMENT_RE.pattern + r'ignore\s*(#|$)') 91 ASCII_NON_ALNUM = ''.join([chr(i) for i in range(128) if not chr(i).isalnum()])
87 # https://github.com/python/typed_ast/blob/55420396/ast27/Grammar/Grammar#L147 92 TYPE_IGNORE_RE = re.compile(
93 TYPE_COMMENT_RE.pattern + r'ignore([{}]|$)'.format(ASCII_NON_ALNUM))
94 # https://github.com/python/typed_ast/blob/1.4.0/ast27/Grammar/Grammar#L147
88 TYPE_FUNC_RE = re.compile(r'^(\(.*?\))\s*->\s*(.*)$') 95 TYPE_FUNC_RE = re.compile(r'^(\(.*?\))\s*->\s*(.*)$')
96
97
98 MAPPING_KEY_RE = re.compile(r'\(([^()]*)\)')
99 CONVERSION_FLAG_RE = re.compile('[#0+ -]*')
100 WIDTH_RE = re.compile(r'(?:\*|\d*)')
101 PRECISION_RE = re.compile(r'(?:\.(?:\*|\d*))?')
102 LENGTH_RE = re.compile('[hlL]?')
103 # https://docs.python.org/3/library/stdtypes.html#old-string-formatting
104 VALID_CONVERSIONS = frozenset('diouxXeEfFgGcrsa%')
105
106
107 def _must_match(regex, string, pos):
108 # type: (Pattern[str], str, int) -> Match[str]
109 match = regex.match(string, pos)
110 assert match is not None
111 return match
112
113
114 def parse_percent_format(s): # type: (str) -> Tuple[PercentFormat, ...]
115 """Parses the string component of a `'...' % ...` format call
116
117 Copied from https://github.com/asottile/pyupgrade at v1.20.1
118 """
119
120 def _parse_inner():
121 # type: () -> Generator[PercentFormat, None, None]
122 string_start = 0
123 string_end = 0
124 in_fmt = False
125
126 i = 0
127 while i < len(s):
128 if not in_fmt:
129 try:
130 i = s.index('%', i)
131 except ValueError: # no more % fields!
132 yield s[string_start:], None
133 return
134 else:
135 string_end = i
136 i += 1
137 in_fmt = True
138 else:
139 key_match = MAPPING_KEY_RE.match(s, i)
140 if key_match:
141 key = key_match.group(1) # type: Optional[str]
142 i = key_match.end()
143 else:
144 key = None
145
146 conversion_flag_match = _must_match(CONVERSION_FLAG_RE, s, i)
147 conversion_flag = conversion_flag_match.group() or None
148 i = conversion_flag_match.end()
149
150 width_match = _must_match(WIDTH_RE, s, i)
151 width = width_match.group() or None
152 i = width_match.end()
153
154 precision_match = _must_match(PRECISION_RE, s, i)
155 precision = precision_match.group() or None
156 i = precision_match.end()
157
158 # length modifier is ignored
159 i = _must_match(LENGTH_RE, s, i).end()
160
161 try:
162 conversion = s[i]
163 except IndexError:
164 raise ValueError('end-of-string while parsing format')
165 i += 1
166
167 fmt = (key, conversion_flag, width, precision, conversion)
168 yield s[string_start:string_end], fmt
169
170 in_fmt = False
171 string_start = i
172
173 if in_fmt:
174 raise ValueError('end-of-string while parsing format')
175
176 return tuple(_parse_inner())
89 177
90 178
91 class _FieldsOrder(dict): 179 class _FieldsOrder(dict):
92 """Fix order of AST node fields.""" 180 """Fix order of AST node fields."""
93 181
541 def name_is_typing_overload(name): # type: (str) -> bool 629 def name_is_typing_overload(name): # type: (str) -> bool
542 for scope in reversed(scope_stack): 630 for scope in reversed(scope_stack):
543 if name in scope: 631 if name in scope:
544 return ( 632 return (
545 isinstance(scope[name], ImportationFrom) and 633 isinstance(scope[name], ImportationFrom) and
546 scope[name].fullName == 'typing.overload' 634 scope[name].fullName in (
635 'typing.overload', 'typing_extensions.overload',
636 )
547 ) 637 )
548 else: 638
549 return False 639 return False
550 640
551 def is_typing_overload_decorator(node): 641 def is_typing_overload_decorator(node):
552 return ( 642 return (
553 ( 643 (
554 isinstance(node, ast.Name) and name_is_typing_overload(node.id) 644 isinstance(node, ast.Name) and name_is_typing_overload(node.id)
555 ) or ( 645 ) or (
559 node.attr == 'overload' 649 node.attr == 'overload'
560 ) 650 )
561 ) 651 )
562 652
563 return ( 653 return (
564 isinstance(value.source, ast.FunctionDef) and 654 isinstance(value.source, FUNCTION_TYPES) and
565 any( 655 any(
566 is_typing_overload_decorator(dec) 656 is_typing_overload_decorator(dec)
567 for dec in value.source.decorator_list 657 for dec in value.source.decorator_list
568 ) 658 )
569 ) 659 )
827 self.messages.append(messageClass(self.filename, *args, **kwargs)) 917 self.messages.append(messageClass(self.filename, *args, **kwargs))
828 918
829 def getParent(self, node): 919 def getParent(self, node):
830 # Lookup the first parent which is not Tuple, List or Starred 920 # Lookup the first parent which is not Tuple, List or Starred
831 while True: 921 while True:
832 node = node.parent 922 node = node._pyflakes_parent
833 if not hasattr(node, 'elts') and not hasattr(node, 'ctx'): 923 if not hasattr(node, 'elts') and not hasattr(node, 'ctx'):
834 return node 924 return node
835 925
836 def getCommonAncestor(self, lnode, rnode, stop): 926 def getCommonAncestor(self, lnode, rnode, stop):
837 if stop in (lnode, rnode) or not (hasattr(lnode, 'parent') and 927 if (
838 hasattr(rnode, 'parent')): 928 stop in (lnode, rnode) or
929 not (
930 hasattr(lnode, '_pyflakes_parent') and
931 hasattr(rnode, '_pyflakes_parent')
932 )
933 ):
839 return None 934 return None
840 if lnode is rnode: 935 if lnode is rnode:
841 return lnode 936 return lnode
842 937
843 if (lnode.depth > rnode.depth): 938 if (lnode._pyflakes_depth > rnode._pyflakes_depth):
844 return self.getCommonAncestor(lnode.parent, rnode, stop) 939 return self.getCommonAncestor(lnode._pyflakes_parent, rnode, stop)
845 if (lnode.depth < rnode.depth): 940 if (lnode._pyflakes_depth < rnode._pyflakes_depth):
846 return self.getCommonAncestor(lnode, rnode.parent, stop) 941 return self.getCommonAncestor(lnode, rnode._pyflakes_parent, stop)
847 return self.getCommonAncestor(lnode.parent, rnode.parent, stop) 942 return self.getCommonAncestor(
943 lnode._pyflakes_parent,
944 rnode._pyflakes_parent,
945 stop,
946 )
848 947
849 def descendantOf(self, node, ancestors, stop): 948 def descendantOf(self, node, ancestors, stop):
850 for a in ancestors: 949 for a in ancestors:
851 if self.getCommonAncestor(node, a, stop): 950 if self.getCommonAncestor(node, a, stop):
852 return True 951 return True
880 Called when a binding is altered. 979 Called when a binding is altered.
881 980
882 - `node` is the statement responsible for the change 981 - `node` is the statement responsible for the change
883 - `value` is the new value, a Binding instance 982 - `value` is the new value, a Binding instance
884 """ 983 """
885 # assert value.source in (node, node.parent): 984 # assert value.source in (node, node._pyflakes_parent):
886 for scope in self.scopeStack[::-1]: 985 for scope in self.scopeStack[::-1]:
887 if value.name in scope: 986 if value.name in scope:
888 break 987 break
889 existing = scope.get(value.name) 988 existing = scope.get(value.name)
890 989
1019 scope[name].used[1], name, scope[name].source) 1118 scope[name].used[1], name, scope[name].source)
1020 break 1119 break
1021 1120
1022 parent_stmt = self.getParent(node) 1121 parent_stmt = self.getParent(node)
1023 if isinstance(parent_stmt, (FOR_TYPES, ast.comprehension)) or ( 1122 if isinstance(parent_stmt, (FOR_TYPES, ast.comprehension)) or (
1024 parent_stmt != node.parent and 1123 parent_stmt != node._pyflakes_parent and
1025 not self.isLiteralTupleUnpacking(parent_stmt)): 1124 not self.isLiteralTupleUnpacking(parent_stmt)):
1026 binding = Binding(name, node) 1125 binding = Binding(name, node)
1027 elif name == '__all__' and isinstance(self.scope, ModuleScope): 1126 elif name == '__all__' and isinstance(self.scope, ModuleScope):
1028 binding = ExportBinding(name, node.parent, self.scope) 1127 binding = ExportBinding(name, node._pyflakes_parent, self.scope)
1029 elif isinstance(getattr(node, 'ctx', None), ast.Param): 1128 elif isinstance(getattr(node, 'ctx', None), ast.Param):
1030 binding = Argument(name, self.getScopeNode(node)) 1129 binding = Argument(name, self.getScopeNode(node))
1031 else: 1130 else:
1032 binding = Assignment(name, node) 1131 binding = Assignment(name, node)
1033 self.addBinding(node, binding) 1132 self.addBinding(node, binding)
1036 1135
1037 def on_conditional_branch(): 1136 def on_conditional_branch():
1038 """ 1137 """
1039 Return `True` if node is part of a conditional body. 1138 Return `True` if node is part of a conditional body.
1040 """ 1139 """
1041 current = getattr(node, 'parent', None) 1140 current = getattr(node, '_pyflakes_parent', None)
1042 while current: 1141 while current:
1043 if isinstance(current, (ast.If, ast.While, ast.IfExp)): 1142 if isinstance(current, (ast.If, ast.While, ast.IfExp)):
1044 return True 1143 return True
1045 current = getattr(current, 'parent', None) 1144 current = getattr(current, '_pyflakes_parent', None)
1046 return False 1145 return False
1047 1146
1048 name = getNodeName(node) 1147 name = getNodeName(node)
1049 if not name: 1148 if not name:
1050 return 1149 return
1127 print(' ' * self.nodeDepth + node.__class__.__name__) 1226 print(' ' * self.nodeDepth + node.__class__.__name__)
1128 if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or 1227 if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or
1129 self.isDocstring(node)): 1228 self.isDocstring(node)):
1130 self.futuresAllowed = False 1229 self.futuresAllowed = False
1131 self.nodeDepth += 1 1230 self.nodeDepth += 1
1132 node.depth = self.nodeDepth 1231 node._pyflakes_depth = self.nodeDepth
1133 node.parent = parent 1232 node._pyflakes_parent = parent
1134 try: 1233 try:
1135 handler = self.getNodeHandler(node.__class__) 1234 handler = self.getNodeHandler(node.__class__)
1136 handler(node) 1235 handler(node)
1137 finally: 1236 finally:
1138 self.nodeDepth -= 1 1237 self.nodeDepth -= 1
1237 EXPR = ASSIGN = handleChildren 1336 EXPR = ASSIGN = handleChildren
1238 1337
1239 PASS = ignore 1338 PASS = ignore
1240 1339
1241 # "expr" type nodes 1340 # "expr" type nodes
1242 BOOLOP = BINOP = UNARYOP = IFEXP = SET = \ 1341 BOOLOP = UNARYOP = IFEXP = SET = \
1243 CALL = REPR = ATTRIBUTE = SUBSCRIPT = \ 1342 REPR = ATTRIBUTE = SUBSCRIPT = \
1244 STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren 1343 STARRED = NAMECONSTANT = NAMEDEXPR = handleChildren
1344
1345 def _handle_string_dot_format(self, node):
1346 try:
1347 placeholders = tuple(parse_format_string(node.func.value.s))
1348 except ValueError as e:
1349 self.report(messages.StringDotFormatInvalidFormat, node, e)
1350 return
1351
1352 class state: # py2-compatible `nonlocal`
1353 auto = None
1354 next_auto = 0
1355
1356 placeholder_positional = set()
1357 placeholder_named = set()
1358
1359 def _add_key(fmtkey):
1360 """Returns True if there is an error which should early-exit"""
1361 if fmtkey is None: # end of string or `{` / `}` escapes
1362 return False
1363
1364 # attributes / indices are allowed in `.format(...)`
1365 fmtkey, _, _ = fmtkey.partition('.')
1366 fmtkey, _, _ = fmtkey.partition('[')
1367
1368 try:
1369 fmtkey = int(fmtkey)
1370 except ValueError:
1371 pass
1372 else: # fmtkey was an integer
1373 if state.auto is True:
1374 self.report(messages.StringDotFormatMixingAutomatic, node)
1375 return True
1376 else:
1377 state.auto = False
1378
1379 if fmtkey == '':
1380 if state.auto is False:
1381 self.report(messages.StringDotFormatMixingAutomatic, node)
1382 return True
1383 else:
1384 state.auto = True
1385
1386 fmtkey = state.next_auto
1387 state.next_auto += 1
1388
1389 if isinstance(fmtkey, int):
1390 placeholder_positional.add(fmtkey)
1391 else:
1392 placeholder_named.add(fmtkey)
1393
1394 return False
1395
1396 for _, fmtkey, spec, _ in placeholders:
1397 if _add_key(fmtkey):
1398 return
1399
1400 # spec can also contain format specifiers
1401 if spec is not None:
1402 try:
1403 spec_placeholders = tuple(parse_format_string(spec))
1404 except ValueError as e:
1405 self.report(messages.StringDotFormatInvalidFormat, node, e)
1406 return
1407
1408 for _, spec_fmtkey, spec_spec, _ in spec_placeholders:
1409 # can't recurse again
1410 if spec_spec is not None and '{' in spec_spec:
1411 self.report(
1412 messages.StringDotFormatInvalidFormat,
1413 node,
1414 'Max string recursion exceeded',
1415 )
1416 return
1417 if _add_key(spec_fmtkey):
1418 return
1419
1420 # bail early if there is *args or **kwargs
1421 if (
1422 # python 2.x *args / **kwargs
1423 getattr(node, 'starargs', None) or
1424 getattr(node, 'kwargs', None) or
1425 # python 3.x *args
1426 any(
1427 isinstance(arg, getattr(ast, 'Starred', ()))
1428 for arg in node.args
1429 ) or
1430 # python 3.x **kwargs
1431 any(kwd.arg is None for kwd in node.keywords)
1432 ):
1433 return
1434
1435 substitution_positional = set(range(len(node.args)))
1436 substitution_named = {kwd.arg for kwd in node.keywords}
1437
1438 extra_positional = substitution_positional - placeholder_positional
1439 extra_named = substitution_named - placeholder_named
1440
1441 missing_arguments = (
1442 (placeholder_positional | placeholder_named) -
1443 (substitution_positional | substitution_named)
1444 )
1445
1446 if extra_positional:
1447 self.report(
1448 messages.StringDotFormatExtraPositionalArguments,
1449 node,
1450 ', '.join(sorted(str(x) for x in extra_positional)),
1451 )
1452 if extra_named:
1453 self.report(
1454 messages.StringDotFormatExtraNamedArguments,
1455 node,
1456 ', '.join(sorted(extra_named)),
1457 )
1458 if missing_arguments:
1459 self.report(
1460 messages.StringDotFormatMissingArgument,
1461 node,
1462 ', '.join(sorted(str(x) for x in missing_arguments)),
1463 )
1464
1465 def CALL(self, node):
1466 if (
1467 isinstance(node.func, ast.Attribute) and
1468 isinstance(node.func.value, ast.Str) and
1469 node.func.attr == 'format'
1470 ):
1471 self._handle_string_dot_format(node)
1472 self.handleChildren(node)
1473
1474 def _handle_percent_format(self, node):
1475 try:
1476 placeholders = parse_percent_format(node.left.s)
1477 except ValueError:
1478 self.report(
1479 messages.PercentFormatInvalidFormat,
1480 node,
1481 'incomplete format',
1482 )
1483 return
1484
1485 named = set()
1486 positional_count = 0
1487 positional = None
1488 for _, placeholder in placeholders:
1489 if placeholder is None:
1490 continue
1491 name, _, width, precision, conversion = placeholder
1492
1493 if conversion == '%':
1494 continue
1495
1496 if conversion not in VALID_CONVERSIONS:
1497 self.report(
1498 messages.PercentFormatUnsupportedFormatCharacter,
1499 node,
1500 conversion,
1501 )
1502
1503 if positional is None and conversion:
1504 positional = name is None
1505
1506 for part in (width, precision):
1507 if part is not None and '*' in part:
1508 if not positional:
1509 self.report(
1510 messages.PercentFormatStarRequiresSequence,
1511 node,
1512 )
1513 else:
1514 positional_count += 1
1515
1516 if positional and name is not None:
1517 self.report(
1518 messages.PercentFormatMixedPositionalAndNamed,
1519 node,
1520 )
1521 return
1522 elif not positional and name is None:
1523 self.report(
1524 messages.PercentFormatMixedPositionalAndNamed,
1525 node,
1526 )
1527 return
1528
1529 if positional:
1530 positional_count += 1
1531 else:
1532 named.add(name)
1533
1534 if (
1535 isinstance(node.right, (ast.List, ast.Tuple)) and
1536 # does not have any *splats (py35+ feature)
1537 not any(
1538 isinstance(elt, getattr(ast, 'Starred', ()))
1539 for elt in node.right.elts
1540 )
1541 ):
1542 substitution_count = len(node.right.elts)
1543 if positional and positional_count != substitution_count:
1544 self.report(
1545 messages.PercentFormatPositionalCountMismatch,
1546 node,
1547 positional_count,
1548 substitution_count,
1549 )
1550 elif not positional:
1551 self.report(messages.PercentFormatExpectedMapping, node)
1552
1553 if (
1554 isinstance(node.right, ast.Dict) and
1555 all(isinstance(k, ast.Str) for k in node.right.keys)
1556 ):
1557 if positional and positional_count > 1:
1558 self.report(messages.PercentFormatExpectedSequence, node)
1559 return
1560
1561 substitution_keys = {k.s for k in node.right.keys}
1562 extra_keys = substitution_keys - named
1563 missing_keys = named - substitution_keys
1564 if not positional and extra_keys:
1565 self.report(
1566 messages.PercentFormatExtraNamedArguments,
1567 node,
1568 ', '.join(sorted(extra_keys)),
1569 )
1570 if not positional and missing_keys:
1571 self.report(
1572 messages.PercentFormatMissingArgument,
1573 node,
1574 ', '.join(sorted(missing_keys)),
1575 )
1576
1577 def BINOP(self, node):
1578 if (
1579 isinstance(node.op, ast.Mod) and
1580 isinstance(node.left, ast.Str)
1581 ):
1582 self._handle_percent_format(node)
1583 self.handleChildren(node)
1245 1584
1246 NUM = STR = BYTES = ELLIPSIS = CONSTANT = ignore 1585 NUM = STR = BYTES = ELLIPSIS = CONSTANT = ignore
1247 1586
1248 # "slice" type nodes 1587 # "slice" type nodes
1249 SLICE = EXTSLICE = INDEX = handleChildren 1588 SLICE = EXTSLICE = INDEX = handleChildren
1269 elif is_notimplemented_name_node(arg): 1608 elif is_notimplemented_name_node(arg):
1270 # Handle "raise NotImplemented" 1609 # Handle "raise NotImplemented"
1271 self.report(messages.RaiseNotImplemented, node) 1610 self.report(messages.RaiseNotImplemented, node)
1272 1611
1273 # additional node types 1612 # additional node types
1274 COMPREHENSION = KEYWORD = FORMATTEDVALUE = JOINEDSTR = handleChildren 1613 COMPREHENSION = KEYWORD = FORMATTEDVALUE = handleChildren
1614
1615 _in_fstring = False
1616
1617 def JOINEDSTR(self, node):
1618 if (
1619 # the conversion / etc. flags are parsed as f-strings without
1620 # placeholders
1621 not self._in_fstring and
1622 not any(isinstance(x, ast.FormattedValue) for x in node.values)
1623 ):
1624 self.report(messages.FStringMissingPlaceholders, node)
1625
1626 self._in_fstring, orig = True, self._in_fstring
1627 try:
1628 self.handleChildren(node)
1629 finally:
1630 self._in_fstring = orig
1275 1631
1276 def DICT(self, node): 1632 def DICT(self, node):
1277 # Complain if there are duplicate keys with different values 1633 # Complain if there are duplicate keys with different values
1278 # If they have the same value it's not going to cause potentially 1634 # If they have the same value it's not going to cause potentially
1279 # unexpected behaviour so we'll not complain. 1635 # unexpected behaviour so we'll not complain.
1361 """ 1717 """
1362 # Locate the name in locals / function / globals scopes. 1718 # Locate the name in locals / function / globals scopes.
1363 if isinstance(node.ctx, (ast.Load, ast.AugLoad)): 1719 if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
1364 self.handleNodeLoad(node) 1720 self.handleNodeLoad(node)
1365 if (node.id == 'locals' and isinstance(self.scope, FunctionScope) and 1721 if (node.id == 'locals' and isinstance(self.scope, FunctionScope) and
1366 isinstance(node.parent, ast.Call)): 1722 isinstance(node._pyflakes_parent, ast.Call)):
1367 # we are doing locals() call in current scope 1723 # we are doing locals() call in current scope
1368 self.scope.usesLocals = True 1724 self.scope.usesLocals = True
1369 elif isinstance(node.ctx, (ast.Store, ast.AugStore, ast.Param)): 1725 elif isinstance(node.ctx, (ast.Store, ast.AugStore, ast.Param)):
1370 self.handleNodeStore(node) 1726 self.handleNodeStore(node)
1371 elif isinstance(node.ctx, ast.Del): 1727 elif isinstance(node.ctx, ast.Del):
1377 def CONTINUE(self, node): 1733 def CONTINUE(self, node):
1378 # Walk the tree up until we see a loop (OK), a function or class 1734 # Walk the tree up until we see a loop (OK), a function or class
1379 # definition (not OK), for 'continue', a finally block (not OK), or 1735 # definition (not OK), for 'continue', a finally block (not OK), or
1380 # the top module scope (not OK) 1736 # the top module scope (not OK)
1381 n = node 1737 n = node
1382 while hasattr(n, 'parent'): 1738 while hasattr(n, '_pyflakes_parent'):
1383 n, n_child = n.parent, n 1739 n, n_child = n._pyflakes_parent, n
1384 if isinstance(n, LOOP_TYPES): 1740 if isinstance(n, LOOP_TYPES):
1385 # Doesn't apply unless it's in the loop itself 1741 # Doesn't apply unless it's in the loop itself
1386 if n_child not in n.orelse: 1742 if n_child not in n.orelse:
1387 return 1743 return
1388 if isinstance(n, (ast.FunctionDef, ast.ClassDef)): 1744 if isinstance(n, (ast.FunctionDef, ast.ClassDef)):
1389 break 1745 break
1390 # Handle Try/TryFinally difference in Python < and >= 3.3 1746 # Handle Try/TryFinally difference in Python < and >= 3.3
1391 if hasattr(n, 'finalbody') and isinstance(node, ast.Continue): 1747 if hasattr(n, 'finalbody') and isinstance(node, ast.Continue):
1392 if n_child in n.finalbody: 1748 if n_child in n.finalbody and not PY38_PLUS:
1393 self.report(messages.ContinueInFinally, node) 1749 self.report(messages.ContinueInFinally, node)
1394 return 1750 return
1395 if isinstance(node, ast.Continue): 1751 if isinstance(node, ast.Continue):
1396 self.report(messages.ContinueOutsideLoop, node) 1752 self.report(messages.ContinueOutsideLoop, node)
1397 else: # ast.Break 1753 else: # ast.Break
1448 else: 1804 else:
1449 args.append(arg.id) 1805 args.append(arg.id)
1450 addArgs(node.args.args) 1806 addArgs(node.args.args)
1451 defaults = node.args.defaults 1807 defaults = node.args.defaults
1452 else: 1808 else:
1809 if PY38_PLUS:
1810 for arg in node.args.posonlyargs:
1811 args.append(arg.arg)
1812 annotations.append(arg.annotation)
1453 for arg in node.args.args + node.args.kwonlyargs: 1813 for arg in node.args.args + node.args.kwonlyargs:
1454 args.append(arg.arg) 1814 args.append(arg.arg)
1455 annotations.append(arg.annotation) 1815 annotations.append(arg.annotation)
1456 defaults = node.args.defaults + node.args.kw_defaults 1816 defaults = node.args.defaults + node.args.kw_defaults
1457 1817
1712 self.report(messages.IsLiteral, node) 2072 self.report(messages.IsLiteral, node)
1713 left = right 2073 left = right
1714 2074
1715 self.handleChildren(node) 2075 self.handleChildren(node)
1716 2076
1717
1718 # 2077 #
1719 # eflag: noqa = M702 2078 # eflag: noqa = M702

eric ide

mercurial