23 import sys |
24 import sys |
24 import warnings |
25 import warnings |
25 |
26 |
26 from pyflakes import messages |
27 from pyflakes import messages |
27 |
28 |
28 PY38_PLUS = sys.version_info >= (3, 8) |
|
29 PYPY = hasattr(sys, 'pypy_version_info') |
29 PYPY = hasattr(sys, 'pypy_version_info') |
30 |
30 |
31 builtin_vars = dir(builtins) |
31 builtin_vars = dir(builtins) |
32 |
32 |
33 parse_format_string = string.Formatter().parse |
33 parse_format_string = string.Formatter().parse |
34 |
34 |
35 |
35 |
36 def getAlternatives(n): |
36 def getAlternatives(n): |
37 if isinstance(n, ast.If): |
37 if isinstance(n, ast.If): |
38 return [n.body] |
38 return [n.body] |
39 if isinstance(n, ast.Try): |
39 elif isinstance(n, ast.Try): |
40 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers] |
40 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers] |
|
41 elif sys.version_info >= (3, 10) and isinstance(n, ast.Match): |
|
42 return [mc.body for mc in n.cases] |
41 |
43 |
42 |
44 |
43 FOR_TYPES = (ast.For, ast.AsyncFor) |
45 FOR_TYPES = (ast.For, ast.AsyncFor) |
44 |
46 |
45 if PY38_PLUS: |
47 |
46 def _is_singleton(node): # type: (ast.AST) -> bool |
48 def _is_singleton(node): # type: (ast.AST) -> bool |
47 return ( |
49 return ( |
48 isinstance(node, ast.Constant) and |
50 isinstance(node, ast.Constant) and |
49 isinstance(node.value, (bool, type(Ellipsis), type(None))) |
51 isinstance(node.value, (bool, type(Ellipsis), type(None))) |
50 ) |
52 ) |
51 else: |
|
52 def _is_singleton(node): # type: (ast.AST) -> bool |
|
53 return isinstance(node, (ast.NameConstant, ast.Ellipsis)) |
|
54 |
53 |
55 |
54 |
56 def _is_tuple_constant(node): # type: (ast.AST) -> bool |
55 def _is_tuple_constant(node): # type: (ast.AST) -> bool |
57 return ( |
56 return ( |
58 isinstance(node, ast.Tuple) and |
57 isinstance(node, ast.Tuple) and |
59 all(_is_constant(elt) for elt in node.elts) |
58 all(_is_constant(elt) for elt in node.elts) |
60 ) |
59 ) |
61 |
60 |
62 |
61 |
63 if PY38_PLUS: |
62 def _is_constant(node): |
64 def _is_constant(node): |
63 return isinstance(node, ast.Constant) or _is_tuple_constant(node) |
65 return isinstance(node, ast.Constant) or _is_tuple_constant(node) |
|
66 else: |
|
67 def _is_constant(node): |
|
68 return ( |
|
69 isinstance(node, (ast.Str, ast.Num, ast.Bytes)) or |
|
70 _is_singleton(node) or |
|
71 _is_tuple_constant(node) |
|
72 ) |
|
73 |
64 |
74 |
65 |
75 def _is_const_non_singleton(node): # type: (ast.AST) -> bool |
66 def _is_const_non_singleton(node): # type: (ast.AST) -> bool |
76 return _is_constant(node) and not _is_singleton(node) |
67 return _is_constant(node) and not _is_singleton(node) |
77 |
68 |
214 if isinstance(item, ast.AST): |
205 if isinstance(item, ast.AST): |
215 yield item |
206 yield item |
216 |
207 |
217 |
208 |
218 def convert_to_value(item): |
209 def convert_to_value(item): |
219 if isinstance(item, ast.Str): |
210 if isinstance(item, ast.Constant): |
220 return item.s |
211 return item.value |
221 elif hasattr(ast, 'Bytes') and isinstance(item, ast.Bytes): |
|
222 return item.s |
|
223 elif isinstance(item, ast.Tuple): |
212 elif isinstance(item, ast.Tuple): |
224 return tuple(convert_to_value(i) for i in item.elts) |
213 return tuple(convert_to_value(i) for i in item.elts) |
225 elif isinstance(item, ast.Num): |
|
226 return item.n |
|
227 elif isinstance(item, ast.Name): |
214 elif isinstance(item, ast.Name): |
228 result = VariableKey(item=item) |
215 return VariableKey(item=item) |
229 constants_lookup = { |
|
230 'True': True, |
|
231 'False': False, |
|
232 'None': None, |
|
233 } |
|
234 return constants_lookup.get( |
|
235 result.name, |
|
236 result, |
|
237 ) |
|
238 elif isinstance(item, ast.NameConstant): |
|
239 return item.value |
|
240 else: |
216 else: |
241 return UnhandledKeyType() |
217 return UnhandledKeyType() |
242 |
218 |
243 |
219 |
244 def is_notimplemented_name_node(node): |
220 def is_notimplemented_name_node(node): |
526 else: |
507 else: |
527 self.names = [] |
508 self.names = [] |
528 |
509 |
529 def _add_to_names(container): |
510 def _add_to_names(container): |
530 for node in container.elts: |
511 for node in container.elts: |
531 if isinstance(node, ast.Str): |
512 if isinstance(node, ast.Constant) and isinstance(node.value, str): |
532 self.names.append(node.s) |
513 self.names.append(node.value) |
533 |
514 |
534 if isinstance(source.value, (ast.List, ast.Tuple)): |
515 if isinstance(source.value, (ast.List, ast.Tuple)): |
535 _add_to_names(source.value) |
516 _add_to_names(source.value) |
536 # If concatenating lists or tuples |
517 # If concatenating lists or tuples |
537 elif isinstance(source.value, ast.BinOp): |
518 elif isinstance(source.value, ast.BinOp): |
735 return func(self, *args, **kwargs) |
720 return func(self, *args, **kwargs) |
736 return in_annotation_func |
721 return in_annotation_func |
737 |
722 |
738 |
723 |
739 class Checker: |
724 class Checker: |
740 """ |
725 """I check the cleanliness and sanity of Python code.""" |
741 I check the cleanliness and sanity of Python code. |
|
742 |
|
743 @ivar _deferredFunctions: Tracking list used by L{deferFunction}. Elements |
|
744 of the list are two-tuples. The first element is the callable passed |
|
745 to L{deferFunction}. The second element is a copy of the scope stack |
|
746 at the time L{deferFunction} was called. |
|
747 |
|
748 @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for |
|
749 callables which are deferred assignment checks. |
|
750 """ |
|
751 |
726 |
752 _ast_node_scope = { |
727 _ast_node_scope = { |
753 ast.Module: ModuleScope, |
728 ast.Module: ModuleScope, |
754 ast.ClassDef: ClassScope, |
729 ast.ClassDef: ClassScope, |
755 ast.FunctionDef: FunctionScope, |
730 ast.FunctionDef: FunctionScope, |
762 } |
737 } |
763 |
738 |
764 nodeDepth = 0 |
739 nodeDepth = 0 |
765 offset = None |
740 offset = None |
766 _in_annotation = AnnotationState.NONE |
741 _in_annotation = AnnotationState.NONE |
767 _in_deferred = False |
|
768 |
742 |
769 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS) |
743 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS) |
770 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS') |
744 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS') |
771 if _customBuiltIns: |
745 if _customBuiltIns: |
772 builtIns.update(_customBuiltIns.split(',')) |
746 builtIns.update(_customBuiltIns.split(',')) |
773 del _customBuiltIns |
747 del _customBuiltIns |
774 |
748 |
775 def __init__(self, tree, filename='(none)', builtins=None, |
749 def __init__(self, tree, filename='(none)', builtins=None, |
776 withDoctest='PYFLAKES_DOCTEST' in os.environ, file_tokens=()): |
750 withDoctest='PYFLAKES_DOCTEST' in os.environ, file_tokens=()): |
777 self._nodeHandlers = {} |
751 self._nodeHandlers = {} |
778 self._deferredFunctions = [] |
752 self._deferred = collections.deque() |
779 self._deferredAssignments = [] |
|
780 self.deadScopes = [] |
753 self.deadScopes = [] |
781 self.messages = [] |
754 self.messages = [] |
782 self.filename = filename |
755 self.filename = filename |
783 if builtins: |
756 if builtins: |
784 self.builtIns = self.builtIns.union(builtins) |
757 self.builtIns = self.builtIns.union(builtins) |
785 self.withDoctest = withDoctest |
758 self.withDoctest = withDoctest |
|
759 self.exceptHandlers = [()] |
|
760 self.root = tree |
|
761 |
|
762 self.scopeStack = [] |
786 try: |
763 try: |
787 self.scopeStack = [Checker._ast_node_scope[type(tree)]()] |
764 scope_tp = Checker._ast_node_scope[type(tree)] |
788 except KeyError: |
765 except KeyError: |
789 raise RuntimeError('No scope implemented for the node %r' % tree) |
766 raise RuntimeError('No scope implemented for the node %r' % tree) |
790 self.exceptHandlers = [()] |
767 |
791 self.root = tree |
768 with self.in_scope(scope_tp): |
792 for builtin in self.builtIns: |
769 for builtin in self.builtIns: |
793 self.addBinding(None, Builtin(builtin)) |
770 self.addBinding(None, Builtin(builtin)) |
794 self.handleChildren(tree) |
771 self.handleChildren(tree) |
795 self._in_deferred = True |
772 self._run_deferred() |
796 self.runDeferred(self._deferredFunctions) |
773 |
797 # Set _deferredFunctions to None so that deferFunction will fail |
|
798 # noisily if called after we've run through the deferred functions. |
|
799 self._deferredFunctions = None |
|
800 self.runDeferred(self._deferredAssignments) |
|
801 # Set _deferredAssignments to None so that deferAssignment will fail |
|
802 # noisily if called after we've run through the deferred assignments. |
|
803 self._deferredAssignments = None |
|
804 del self.scopeStack[1:] |
|
805 self.popScope() |
|
806 self.checkDeadScopes() |
774 self.checkDeadScopes() |
807 |
775 |
808 if file_tokens: |
776 if file_tokens: |
809 warnings.warn( |
777 warnings.warn( |
810 '`file_tokens` will be removed in a future version', |
778 '`file_tokens` will be removed in a future version', |
818 This is used for handling function bodies, which must be deferred |
786 This is used for handling function bodies, which must be deferred |
819 because code later in the file might modify the global scope. When |
787 because code later in the file might modify the global scope. When |
820 `callable` is called, the scope at the time this is called will be |
788 `callable` is called, the scope at the time this is called will be |
821 restored, however it will contain any new bindings added to it. |
789 restored, however it will contain any new bindings added to it. |
822 """ |
790 """ |
823 self._deferredFunctions.append((callable, self.scopeStack[:], self.offset)) |
791 self._deferred.append((callable, self.scopeStack[:], self.offset)) |
824 |
792 |
825 def deferAssignment(self, callable): |
793 def _run_deferred(self): |
826 """ |
794 orig = (self.scopeStack, self.offset) |
827 Schedule an assignment handler to be called just after deferred |
795 |
828 function handlers. |
796 while self._deferred: |
829 """ |
797 handler, scope, offset = self._deferred.popleft() |
830 self._deferredAssignments.append((callable, self.scopeStack[:], self.offset)) |
798 self.scopeStack, self.offset = scope, offset |
831 |
|
832 def runDeferred(self, deferred): |
|
833 """ |
|
834 Run the callables in C{deferred} using their associated scope stack. |
|
835 """ |
|
836 for handler, scope, offset in deferred: |
|
837 self.scopeStack = scope |
|
838 self.offset = offset |
|
839 handler() |
799 handler() |
|
800 |
|
801 self.scopeStack, self.offset = orig |
840 |
802 |
841 def _in_doctest(self): |
803 def _in_doctest(self): |
842 return (len(self.scopeStack) >= 2 and |
804 return (len(self.scopeStack) >= 2 and |
843 isinstance(self.scopeStack[1], DoctestScope)) |
805 isinstance(self.scopeStack[1], DoctestScope)) |
844 |
806 |
871 |
833 |
872 @property |
834 @property |
873 def scope(self): |
835 def scope(self): |
874 return self.scopeStack[-1] |
836 return self.scopeStack[-1] |
875 |
837 |
876 def popScope(self): |
838 @contextlib.contextmanager |
877 self.deadScopes.append(self.scopeStack.pop()) |
839 def in_scope(self, cls): |
|
840 self.scopeStack.append(cls()) |
|
841 try: |
|
842 yield |
|
843 finally: |
|
844 self.deadScopes.append(self.scopeStack.pop()) |
878 |
845 |
879 def checkDeadScopes(self): |
846 def checkDeadScopes(self): |
880 """ |
847 """ |
881 Look at scopes which have been fully examined and report names in them |
848 Look at scopes which have been fully examined and report names in them |
882 which were imported but unused. |
849 which were imported but unused. |
883 """ |
850 """ |
884 for scope in self.deadScopes: |
851 for scope in self.deadScopes: |
885 # imports in classes are public members |
852 # imports in classes are public members |
886 if isinstance(scope, ClassScope): |
853 if isinstance(scope, ClassScope): |
887 continue |
854 continue |
|
855 |
|
856 if isinstance(scope, FunctionScope): |
|
857 for name, binding in scope.unused_assignments(): |
|
858 self.report(messages.UnusedVariable, binding.source, name) |
|
859 for name, binding in scope.unused_annotations(): |
|
860 self.report(messages.UnusedAnnotation, binding.source, name) |
888 |
861 |
889 all_binding = scope.get('__all__') |
862 all_binding = scope.get('__all__') |
890 if all_binding and not isinstance(all_binding, ExportBinding): |
863 if all_binding and not isinstance(all_binding, ExportBinding): |
891 all_binding = None |
864 all_binding = None |
892 |
865 |
934 continue |
907 continue |
935 else: |
908 else: |
936 messg = messages.RedefinedWhileUnused |
909 messg = messages.RedefinedWhileUnused |
937 self.report(messg, node, value.name, value.source) |
910 self.report(messg, node, value.name, value.source) |
938 |
911 |
939 def pushScope(self, scopeClass=FunctionScope): |
|
940 self.scopeStack.append(scopeClass()) |
|
941 |
|
942 def report(self, messageClass, *args, **kwargs): |
912 def report(self, messageClass, *args, **kwargs): |
943 self.messages.append(messageClass(self.filename, *args, **kwargs)) |
913 self.messages.append(messageClass(self.filename, *args, **kwargs)) |
944 |
914 |
945 def getParent(self, node): |
915 def getParent(self, node): |
946 # Lookup the first parent which is not Tuple, List or Starred |
916 # Lookup the first parent which is not Tuple, List or Starred |
1078 def handleNodeLoad(self, node, parent): |
1048 def handleNodeLoad(self, node, parent): |
1079 name = getNodeName(node) |
1049 name = getNodeName(node) |
1080 if not name: |
1050 if not name: |
1081 return |
1051 return |
1082 |
1052 |
1083 in_generators = None |
1053 # only the following can access class scoped variables (since classes |
|
1054 # aren't really a scope) |
|
1055 # - direct accesses (not within a nested scope) |
|
1056 # - generators |
|
1057 # - type annotations (for generics, etc.) |
|
1058 can_access_class_vars = None |
1084 importStarred = None |
1059 importStarred = None |
1085 |
1060 |
1086 # try enclosing function scopes and global scope |
1061 # try enclosing function scopes and global scope |
1087 for scope in self.scopeStack[-1::-1]: |
1062 for scope in self.scopeStack[-1::-1]: |
1088 if isinstance(scope, ClassScope): |
1063 if isinstance(scope, ClassScope): |
1089 if name == '__class__': |
1064 if name == '__class__': |
1090 return |
1065 return |
1091 elif in_generators is False: |
1066 elif can_access_class_vars is False: |
1092 # only generators used in a class scope can access the |
1067 # only generators used in a class scope can access the |
1093 # names of the class. this is skipped during the first |
1068 # names of the class. this is skipped during the first |
1094 # iteration |
1069 # iteration |
1095 continue |
1070 continue |
1096 |
1071 |
1097 binding = scope.get(name, None) |
1072 binding = scope.get(name, None) |
1098 if isinstance(binding, Annotation) and not self._in_postponed_annotation: |
1073 if isinstance(binding, Annotation) and not self._in_postponed_annotation: |
1099 scope[name].used = True |
1074 scope[name].used = (self.scope, node) |
1100 continue |
1075 continue |
1101 |
1076 |
1102 if name == 'print' and isinstance(binding, Builtin): |
1077 if name == 'print' and isinstance(binding, Builtin): |
1103 if (isinstance(parent, ast.BinOp) and |
1078 if (isinstance(parent, ast.BinOp) and |
1104 isinstance(parent.op, ast.RShift)): |
1079 isinstance(parent.op, ast.RShift)): |
1186 node._pyflakes_parent, |
1163 node._pyflakes_parent, |
1187 (ast.Assign, ast.AugAssign, ast.AnnAssign) |
1164 (ast.Assign, ast.AugAssign, ast.AnnAssign) |
1188 ) |
1165 ) |
1189 ): |
1166 ): |
1190 binding = ExportBinding(name, node._pyflakes_parent, self.scope) |
1167 binding = ExportBinding(name, node._pyflakes_parent, self.scope) |
1191 elif PY38_PLUS and isinstance(parent_stmt, ast.NamedExpr): |
1168 elif isinstance(parent_stmt, ast.NamedExpr): |
1192 binding = NamedExprAssignment(name, node) |
1169 binding = NamedExprAssignment(name, node) |
1193 else: |
1170 else: |
1194 binding = Assignment(name, node) |
1171 binding = Assignment(name, node) |
1195 self.addBinding(node, binding) |
1172 self.addBinding(node, binding) |
1196 |
1173 |
1253 def isDocstring(self, node): |
1230 def isDocstring(self, node): |
1254 """ |
1231 """ |
1255 Determine if the given node is a docstring, as long as it is at the |
1232 Determine if the given node is a docstring, as long as it is at the |
1256 correct place in the node tree. |
1233 correct place in the node tree. |
1257 """ |
1234 """ |
1258 return isinstance(node, ast.Str) or (isinstance(node, ast.Expr) and |
1235 return ( |
1259 isinstance(node.value, ast.Str)) |
1236 isinstance(node, ast.Expr) and |
|
1237 isinstance(node.value, ast.Constant) and |
|
1238 isinstance(node.value.value, str) |
|
1239 ) |
1260 |
1240 |
1261 def getDocstring(self, node): |
1241 def getDocstring(self, node): |
1262 if isinstance(node, ast.Expr): |
1242 if ( |
1263 node = node.value |
1243 isinstance(node, ast.Expr) and |
1264 if not isinstance(node, ast.Str): |
1244 isinstance(node.value, ast.Constant) and |
1265 return (None, None) |
1245 isinstance(node.value.value, str) |
1266 |
1246 ): |
1267 if PYPY or PY38_PLUS: |
1247 return node.value.value, node.lineno - 1 |
1268 doctest_lineno = node.lineno - 1 |
|
1269 else: |
1248 else: |
1270 # Computed incorrectly if the docstring has backslash |
1249 return None, None |
1271 doctest_lineno = node.lineno - node.s.count('\n') - 1 |
|
1272 |
|
1273 return (node.s, doctest_lineno) |
|
1274 |
1250 |
1275 def handleNode(self, node, parent): |
1251 def handleNode(self, node, parent): |
1276 if node is None: |
1252 if node is None: |
1277 return |
1253 return |
1278 if self.offset and getattr(node, 'lineno', None) is not None: |
1254 if self.offset and getattr(node, 'lineno', None) is not None: |
1279 node.lineno += self.offset[0] |
1255 node.lineno += self.offset[0] |
1280 node.col_offset += self.offset[1] |
1256 node.col_offset += self.offset[1] |
1281 if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or |
1257 if ( |
1282 self.isDocstring(node)): |
1258 self.futuresAllowed and |
|
1259 self.nodeDepth == 0 and |
|
1260 not isinstance(node, ast.ImportFrom) and |
|
1261 not self.isDocstring(node) |
|
1262 ): |
1283 self.futuresAllowed = False |
1263 self.futuresAllowed = False |
1284 self.nodeDepth += 1 |
1264 self.nodeDepth += 1 |
1285 node._pyflakes_depth = self.nodeDepth |
1265 node._pyflakes_depth = self.nodeDepth |
1286 node._pyflakes_parent = parent |
1266 node._pyflakes_parent = parent |
1287 try: |
1267 try: |
1305 |
1285 |
1306 # Place doctest in module scope |
1286 # Place doctest in module scope |
1307 saved_stack = self.scopeStack |
1287 saved_stack = self.scopeStack |
1308 self.scopeStack = [self.scopeStack[0]] |
1288 self.scopeStack = [self.scopeStack[0]] |
1309 node_offset = self.offset or (0, 0) |
1289 node_offset = self.offset or (0, 0) |
1310 self.pushScope(DoctestScope) |
1290 with self.in_scope(DoctestScope): |
1311 if '_' not in self.scopeStack[0]: |
1291 if '_' not in self.scopeStack[0]: |
1312 self.addBinding(None, Builtin('_')) |
1292 self.addBinding(None, Builtin('_')) |
1313 for example in examples: |
1293 for example in examples: |
1314 try: |
1294 try: |
1315 tree = ast.parse(example.source, "<doctest>") |
1295 tree = ast.parse(example.source, "<doctest>") |
1316 except SyntaxError as e: |
1296 except SyntaxError as e: |
1317 position = (node_lineno + example.lineno + e.lineno, |
1297 position = (node_lineno + example.lineno + e.lineno, |
1318 example.indent + 4 + (e.offset or 0)) |
1298 example.indent + 4 + (e.offset or 0)) |
1319 self.report(messages.DoctestSyntaxError, node, position) |
1299 self.report(messages.DoctestSyntaxError, node, position) |
1320 else: |
1300 else: |
1321 self.offset = (node_offset[0] + node_lineno + example.lineno, |
1301 self.offset = (node_offset[0] + node_lineno + example.lineno, |
1322 node_offset[1] + example.indent + 4) |
1302 node_offset[1] + example.indent + 4) |
1323 self.handleChildren(tree) |
1303 self.handleChildren(tree) |
1324 self.offset = node_offset |
1304 self.offset = node_offset |
1325 self.popScope() |
|
1326 self.scopeStack = saved_stack |
1305 self.scopeStack = saved_stack |
1327 |
1306 |
1328 @in_string_annotation |
1307 @in_string_annotation |
1329 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err): |
1308 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err): |
1330 try: |
1309 try: |
1347 descendant.lineno = ref_lineno |
1326 descendant.lineno = ref_lineno |
1348 descendant.col_offset = ref_col_offset |
1327 descendant.col_offset = ref_col_offset |
1349 |
1328 |
1350 self.handleNode(parsed_annotation, node) |
1329 self.handleNode(parsed_annotation, node) |
1351 |
1330 |
|
1331 def handle_annotation_always_deferred(self, annotation, parent): |
|
1332 fn = in_annotation(Checker.handleNode) |
|
1333 self.deferFunction(lambda: fn(self, annotation, parent)) |
|
1334 |
1352 @in_annotation |
1335 @in_annotation |
1353 def handleAnnotation(self, annotation, node): |
1336 def handleAnnotation(self, annotation, node): |
1354 if isinstance(annotation, ast.Str): |
1337 if ( |
|
1338 isinstance(annotation, ast.Constant) and |
|
1339 isinstance(annotation.value, str) |
|
1340 ): |
1355 # Defer handling forward annotation. |
1341 # Defer handling forward annotation. |
1356 self.deferFunction(functools.partial( |
1342 self.deferFunction(functools.partial( |
1357 self.handleStringAnnotation, |
1343 self.handleStringAnnotation, |
1358 annotation.s, |
1344 annotation.value, |
1359 node, |
1345 node, |
1360 annotation.lineno, |
1346 annotation.lineno, |
1361 annotation.col_offset, |
1347 annotation.col_offset, |
1362 messages.ForwardAnnotationSyntaxError, |
1348 messages.ForwardAnnotationSyntaxError, |
1363 )) |
1349 )) |
1364 elif self.annotationsFutureEnabled: |
1350 elif self.annotationsFutureEnabled: |
1365 fn = in_annotation(Checker.handleNode) |
1351 self.handle_annotation_always_deferred(annotation, node) |
1366 self.deferFunction(lambda: fn(self, annotation, node)) |
|
1367 else: |
1352 else: |
1368 self.handleNode(annotation, node) |
1353 self.handleNode(annotation, node) |
1369 |
1354 |
1370 def ignore(self, node): |
1355 def ignore(self, node): |
1371 pass |
1356 pass |
1418 else: |
1403 else: |
1419 self.handleChildren(node) |
1404 self.handleChildren(node) |
1420 |
1405 |
1421 def _handle_string_dot_format(self, node): |
1406 def _handle_string_dot_format(self, node): |
1422 try: |
1407 try: |
1423 placeholders = tuple(parse_format_string(node.func.value.s)) |
1408 placeholders = tuple(parse_format_string(node.func.value.value)) |
1424 except ValueError as e: |
1409 except ValueError as e: |
1425 self.report(messages.StringDotFormatInvalidFormat, node, e) |
1410 self.report(messages.StringDotFormatInvalidFormat, node, e) |
1426 return |
1411 return |
1427 |
1412 |
1428 auto = None |
1413 auto = None |
1534 ) |
1519 ) |
1535 |
1520 |
1536 def CALL(self, node): |
1521 def CALL(self, node): |
1537 if ( |
1522 if ( |
1538 isinstance(node.func, ast.Attribute) and |
1523 isinstance(node.func, ast.Attribute) and |
1539 isinstance(node.func.value, ast.Str) and |
1524 isinstance(node.func.value, ast.Constant) and |
|
1525 isinstance(node.func.value.value, str) and |
1540 node.func.attr == 'format' |
1526 node.func.attr == 'format' |
1541 ): |
1527 ): |
1542 self._handle_string_dot_format(node) |
1528 self._handle_string_dot_format(node) |
1543 |
1529 |
1544 omit = [] |
1530 omit = [] |
1694 elif not positional: |
1680 elif not positional: |
1695 self.report(messages.PercentFormatExpectedMapping, node) |
1681 self.report(messages.PercentFormatExpectedMapping, node) |
1696 |
1682 |
1697 if ( |
1683 if ( |
1698 isinstance(node.right, ast.Dict) and |
1684 isinstance(node.right, ast.Dict) and |
1699 all(isinstance(k, ast.Str) for k in node.right.keys) |
1685 all( |
|
1686 isinstance(k, ast.Constant) and isinstance(k.value, str) |
|
1687 for k in node.right.keys |
|
1688 ) |
1700 ): |
1689 ): |
1701 if positional and positional_count > 1: |
1690 if positional and positional_count > 1: |
1702 self.report(messages.PercentFormatExpectedSequence, node) |
1691 self.report(messages.PercentFormatExpectedSequence, node) |
1703 return |
1692 return |
1704 |
1693 |
1705 substitution_keys = {k.s for k in node.right.keys} |
1694 substitution_keys = {k.value for k in node.right.keys} |
1706 extra_keys = substitution_keys - named |
1695 extra_keys = substitution_keys - named |
1707 missing_keys = named - substitution_keys |
1696 missing_keys = named - substitution_keys |
1708 if not positional and extra_keys: |
1697 if not positional and extra_keys: |
1709 self.report( |
1698 self.report( |
1710 messages.PercentFormatExtraNamedArguments, |
1699 messages.PercentFormatExtraNamedArguments, |
1719 ) |
1708 ) |
1720 |
1709 |
1721 def BINOP(self, node): |
1710 def BINOP(self, node): |
1722 if ( |
1711 if ( |
1723 isinstance(node.op, ast.Mod) and |
1712 isinstance(node.op, ast.Mod) and |
1724 isinstance(node.left, ast.Str) |
1713 isinstance(node.left, ast.Constant) and |
|
1714 isinstance(node.left.value, str) |
1725 ): |
1715 ): |
1726 self._handle_percent_format(node) |
1716 self._handle_percent_format(node) |
1727 self.handleChildren(node) |
1717 self.handleChildren(node) |
1728 |
1718 |
1729 def STR(self, node): |
1719 def CONSTANT(self, node): |
1730 if self._in_annotation: |
1720 if isinstance(node.value, str) and self._in_annotation: |
1731 fn = functools.partial( |
1721 fn = functools.partial( |
1732 self.handleStringAnnotation, |
1722 self.handleStringAnnotation, |
1733 node.s, |
1723 node.value, |
1734 node, |
1724 node, |
1735 node.lineno, |
1725 node.lineno, |
1736 node.col_offset, |
1726 node.col_offset, |
1737 messages.ForwardAnnotationSyntaxError, |
1727 messages.ForwardAnnotationSyntaxError, |
1738 ) |
1728 ) |
1739 if self._in_deferred: |
1729 self.deferFunction(fn) |
1740 fn() |
|
1741 else: |
|
1742 self.deferFunction(fn) |
|
1743 |
|
1744 if PY38_PLUS: |
|
1745 def CONSTANT(self, node): |
|
1746 if isinstance(node.value, str): |
|
1747 return self.STR(node) |
|
1748 else: |
|
1749 NUM = BYTES = ELLIPSIS = CONSTANT = ignore |
|
1750 |
1730 |
1751 # "slice" type nodes |
1731 # "slice" type nodes |
1752 SLICE = EXTSLICE = INDEX = handleChildren |
1732 SLICE = EXTSLICE = INDEX = handleChildren |
1753 |
1733 |
1754 # expression contexts are node instances too, though being constants |
1734 # expression contexts are node instances too, though being constants |
1872 scope[node_name] = node_value |
1852 scope[node_name] = node_value |
1873 |
1853 |
1874 NONLOCAL = GLOBAL |
1854 NONLOCAL = GLOBAL |
1875 |
1855 |
1876 def GENERATOREXP(self, node): |
1856 def GENERATOREXP(self, node): |
1877 self.pushScope(GeneratorScope) |
1857 with self.in_scope(GeneratorScope): |
1878 self.handleChildren(node) |
1858 self.handleChildren(node) |
1879 self.popScope() |
|
1880 |
1859 |
1881 LISTCOMP = DICTCOMP = SETCOMP = GENERATOREXP |
1860 LISTCOMP = DICTCOMP = SETCOMP = GENERATOREXP |
1882 |
1861 |
1883 def NAME(self, node): |
1862 def NAME(self, node): |
1884 """ |
1863 """ |
1910 # Doesn't apply unless it's in the loop itself |
1889 # Doesn't apply unless it's in the loop itself |
1911 if n_child not in n.orelse: |
1890 if n_child not in n.orelse: |
1912 return |
1891 return |
1913 if isinstance(n, (ast.FunctionDef, ast.ClassDef)): |
1892 if isinstance(n, (ast.FunctionDef, ast.ClassDef)): |
1914 break |
1893 break |
1915 # Handle Try/TryFinally difference in Python < and >= 3.3 |
|
1916 if hasattr(n, 'finalbody') and isinstance(node, ast.Continue): |
|
1917 if n_child in n.finalbody and not PY38_PLUS: |
|
1918 self.report(messages.ContinueInFinally, node) |
|
1919 return |
|
1920 if isinstance(node, ast.Continue): |
1894 if isinstance(node, ast.Continue): |
1921 self.report(messages.ContinueOutsideLoop, node) |
1895 self.report(messages.ContinueOutsideLoop, node) |
1922 else: # ast.Break |
1896 else: # ast.Break |
1923 self.report(messages.BreakOutsideLoop, node) |
1897 self.report(messages.BreakOutsideLoop, node) |
1924 |
1898 |
1947 AWAIT = YIELDFROM = YIELD |
1921 AWAIT = YIELDFROM = YIELD |
1948 |
1922 |
1949 def FUNCTIONDEF(self, node): |
1923 def FUNCTIONDEF(self, node): |
1950 for deco in node.decorator_list: |
1924 for deco in node.decorator_list: |
1951 self.handleNode(deco, node) |
1925 self.handleNode(deco, node) |
1952 self.LAMBDA(node) |
1926 |
|
1927 with self._type_param_scope(node): |
|
1928 self.LAMBDA(node) |
|
1929 |
1953 self.addBinding(node, FunctionDefinition(node.name, node)) |
1930 self.addBinding(node, FunctionDefinition(node.name, node)) |
1954 # doctest does not process doctest within a doctest, |
1931 # doctest does not process doctest within a doctest, |
1955 # or in nested functions. |
1932 # or in nested functions. |
1956 if (self.withDoctest and |
1933 if (self.withDoctest and |
1957 not self._in_doctest() and |
1934 not self._in_doctest() and |
1962 |
1939 |
1963 def LAMBDA(self, node): |
1940 def LAMBDA(self, node): |
1964 args = [] |
1941 args = [] |
1965 annotations = [] |
1942 annotations = [] |
1966 |
1943 |
1967 if PY38_PLUS: |
1944 for arg in node.args.posonlyargs: |
1968 for arg in node.args.posonlyargs: |
1945 args.append(arg.arg) |
1969 args.append(arg.arg) |
1946 annotations.append(arg.annotation) |
1970 annotations.append(arg.annotation) |
|
1971 for arg in node.args.args + node.args.kwonlyargs: |
1947 for arg in node.args.args + node.args.kwonlyargs: |
1972 args.append(arg.arg) |
1948 args.append(arg.arg) |
1973 annotations.append(arg.annotation) |
1949 annotations.append(arg.annotation) |
1974 defaults = node.args.defaults + node.args.kw_defaults |
1950 defaults = node.args.defaults + node.args.kw_defaults |
1975 |
1951 |
1996 |
1972 |
1997 for default in defaults: |
1973 for default in defaults: |
1998 self.handleNode(default, node) |
1974 self.handleNode(default, node) |
1999 |
1975 |
2000 def runFunction(): |
1976 def runFunction(): |
2001 |
1977 with self.in_scope(FunctionScope): |
2002 self.pushScope() |
1978 self.handleChildren( |
2003 |
1979 node, |
2004 self.handleChildren(node, omit=['decorator_list', 'returns']) |
1980 omit=('decorator_list', 'returns', 'type_params'), |
2005 |
1981 ) |
2006 def check_unused_assignments(): |
|
2007 """ |
|
2008 Check to see if any assignments have not been used. |
|
2009 """ |
|
2010 for name, binding in self.scope.unused_assignments(): |
|
2011 self.report(messages.UnusedVariable, binding.source, name) |
|
2012 |
|
2013 def check_unused_annotations(): |
|
2014 """ |
|
2015 Check to see if any annotations have not been used. |
|
2016 """ |
|
2017 for name, binding in self.scope.unused_annotations(): |
|
2018 self.report(messages.UnusedAnnotation, binding.source, name) |
|
2019 |
|
2020 self.deferAssignment(check_unused_assignments) |
|
2021 self.deferAssignment(check_unused_annotations) |
|
2022 |
|
2023 self.popScope() |
|
2024 |
1982 |
2025 self.deferFunction(runFunction) |
1983 self.deferFunction(runFunction) |
2026 |
1984 |
2027 def ARGUMENTS(self, node): |
1985 def ARGUMENTS(self, node): |
2028 self.handleChildren(node, omit=('defaults', 'kw_defaults')) |
1986 self.handleChildren(node, omit=('defaults', 'kw_defaults')) |
2036 classes, and the body of its definition. Additionally, add its name to |
1994 classes, and the body of its definition. Additionally, add its name to |
2037 the current scope. |
1995 the current scope. |
2038 """ |
1996 """ |
2039 for deco in node.decorator_list: |
1997 for deco in node.decorator_list: |
2040 self.handleNode(deco, node) |
1998 self.handleNode(deco, node) |
2041 for baseNode in node.bases: |
1999 |
2042 self.handleNode(baseNode, node) |
2000 with self._type_param_scope(node): |
2043 for keywordNode in node.keywords: |
2001 for baseNode in node.bases: |
2044 self.handleNode(keywordNode, node) |
2002 self.handleNode(baseNode, node) |
2045 self.pushScope(ClassScope) |
2003 for keywordNode in node.keywords: |
2046 # doctest does not process doctest within a doctest |
2004 self.handleNode(keywordNode, node) |
2047 # classes within classes are processed. |
2005 with self.in_scope(ClassScope): |
2048 if (self.withDoctest and |
2006 # doctest does not process doctest within a doctest |
2049 not self._in_doctest() and |
2007 # classes within classes are processed. |
2050 not isinstance(self.scope, FunctionScope)): |
2008 if (self.withDoctest and |
2051 self.deferFunction(lambda: self.handleDoctests(node)) |
2009 not self._in_doctest() and |
2052 for stmt in node.body: |
2010 not isinstance(self.scope, FunctionScope)): |
2053 self.handleNode(stmt, node) |
2011 self.deferFunction(lambda: self.handleDoctests(node)) |
2054 self.popScope() |
2012 for stmt in node.body: |
|
2013 self.handleNode(stmt, node) |
|
2014 |
2055 self.addBinding(node, ClassDefinition(node.name, node)) |
2015 self.addBinding(node, ClassDefinition(node.name, node)) |
2056 |
2016 |
2057 def AUGASSIGN(self, node): |
2017 def AUGASSIGN(self, node): |
2058 self.handleNodeLoad(node.target, node) |
2018 self.handleNodeLoad(node.target, node) |
2059 self.handleNode(node.value, node) |
2019 self.handleNode(node.value, node) |
2223 def _match_target(self, node): |
2183 def _match_target(self, node): |
2224 self.handleNodeStore(node) |
2184 self.handleNodeStore(node) |
2225 self.handleChildren(node) |
2185 self.handleChildren(node) |
2226 |
2186 |
2227 MATCHAS = MATCHMAPPING = MATCHSTAR = _match_target |
2187 MATCHAS = MATCHMAPPING = MATCHSTAR = _match_target |
|
2188 |
|
2189 @contextlib.contextmanager |
|
2190 def _type_param_scope(self, node): |
|
2191 with contextlib.ExitStack() as ctx: |
|
2192 if sys.version_info >= (3, 12): |
|
2193 ctx.enter_context(self.in_scope(TypeScope)) |
|
2194 for param in node.type_params: |
|
2195 self.handleNode(param, node) |
|
2196 yield |
|
2197 |
|
2198 def TYPEVAR(self, node): |
|
2199 self.handleNodeStore(node) |
|
2200 self.handle_annotation_always_deferred(node.bound, node) |
|
2201 |
|
2202 def TYPEALIAS(self, node): |
|
2203 self.handleNode(node.name, node) |
|
2204 with self._type_param_scope(node): |
|
2205 self.handle_annotation_always_deferred(node.value, node) |