1"""
2Main module.
3
4Implement the central Checker class.
5Also, it models the Bindings and Scopes.
6"""
7import __future__
8import builtins
9import ast
10import collections
11import contextlib
12import doctest
13import functools
14import os
15import re
16import string
17import sys
18import warnings
19
20from pyflakes import messages
21
22PYPY = hasattr(sys, 'pypy_version_info')
23
24builtin_vars = dir(builtins)
25
26parse_format_string = string.Formatter().parse
27
28
29def getAlternatives(n):
30 if isinstance(n, ast.If):
31 return [n.body]
32 elif isinstance(n, ast.Try):
33 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers]
34 elif sys.version_info >= (3, 10) and isinstance(n, ast.Match):
35 return [mc.body for mc in n.cases]
36
37
38FOR_TYPES = (ast.For, ast.AsyncFor)
39
40
41def _is_singleton(node): # type: (ast.AST) -> bool
42 return (
43 isinstance(node, ast.Constant) and
44 isinstance(node.value, (bool, type(Ellipsis), type(None)))
45 )
46
47
48def _is_tuple_constant(node): # type: (ast.AST) -> bool
49 return (
50 isinstance(node, ast.Tuple) and
51 all(_is_constant(elt) for elt in node.elts)
52 )
53
54
55def _is_constant(node):
56 return isinstance(node, ast.Constant) or _is_tuple_constant(node)
57
58
59def _is_const_non_singleton(node): # type: (ast.AST) -> bool
60 return _is_constant(node) and not _is_singleton(node)
61
62
63def _is_name_or_attr(node, name): # type: (ast.AST, str) -> bool
64 return (
65 (isinstance(node, ast.Name) and node.id == name) or
66 (isinstance(node, ast.Attribute) and node.attr == name)
67 )
68
69
70MAPPING_KEY_RE = re.compile(r'\(([^()]*)\)')
71CONVERSION_FLAG_RE = re.compile('[#0+ -]*')
72WIDTH_RE = re.compile(r'(?:\*|\d*)')
73PRECISION_RE = re.compile(r'(?:\.(?:\*|\d*))?')
74LENGTH_RE = re.compile('[hlL]?')
75# https://docs.python.org/3/library/stdtypes.html#old-string-formatting
76VALID_CONVERSIONS = frozenset('diouxXeEfFgGcrsa%')
77
78
79def _must_match(regex, string, pos):
80 match = regex.match(string, pos)
81 assert match is not None
82 return match
83
84
85def parse_percent_format(s):
86 """Parses the string component of a `'...' % ...` format call
87
88 Copied from https://github.com/asottile/pyupgrade at v1.20.1
89 """
90
91 def _parse_inner():
92 string_start = 0
93 string_end = 0
94 in_fmt = False
95
96 i = 0
97 while i < len(s):
98 if not in_fmt:
99 try:
100 i = s.index('%', i)
101 except ValueError: # no more % fields!
102 yield s[string_start:], None
103 return
104 else:
105 string_end = i
106 i += 1
107 in_fmt = True
108 else:
109 key_match = MAPPING_KEY_RE.match(s, i)
110 if key_match:
111 key = key_match.group(1)
112 i = key_match.end()
113 else:
114 key = None
115
116 conversion_flag_match = _must_match(CONVERSION_FLAG_RE, s, i)
117 conversion_flag = conversion_flag_match.group() or None
118 i = conversion_flag_match.end()
119
120 width_match = _must_match(WIDTH_RE, s, i)
121 width = width_match.group() or None
122 i = width_match.end()
123
124 precision_match = _must_match(PRECISION_RE, s, i)
125 precision = precision_match.group() or None
126 i = precision_match.end()
127
128 # length modifier is ignored
129 i = _must_match(LENGTH_RE, s, i).end()
130
131 try:
132 conversion = s[i]
133 except IndexError:
134 raise ValueError('end-of-string while parsing format')
135 i += 1
136
137 fmt = (key, conversion_flag, width, precision, conversion)
138 yield s[string_start:string_end], fmt
139
140 in_fmt = False
141 string_start = i
142
143 if in_fmt:
144 raise ValueError('end-of-string while parsing format')
145
146 return tuple(_parse_inner())
147
148
149class _FieldsOrder(dict):
150 """Fix order of AST node fields."""
151
152 def _get_fields(self, node_class):
153 # handle iter before target, and generators before element
154 fields = node_class._fields
155 if 'iter' in fields:
156 key_first = 'iter'.find
157 elif 'generators' in fields:
158 key_first = 'generators'.find
159 else:
160 key_first = 'value'.find
161 return tuple(sorted(fields, key=key_first, reverse=True))
162
163 def __missing__(self, node_class):
164 self[node_class] = fields = self._get_fields(node_class)
165 return fields
166
167
168def iter_child_nodes(node, omit=None, _fields_order=_FieldsOrder()):
169 """
170 Yield all direct child nodes of *node*, that is, all fields that
171 are nodes and all items of fields that are lists of nodes.
172
173 :param node: AST node to be iterated upon
174 :param omit: String or tuple of strings denoting the
175 attributes of the node to be omitted from
176 further parsing
177 :param _fields_order: Order of AST node fields
178 """
179 for name in _fields_order[node.__class__]:
180 if omit and name in omit:
181 continue
182 field = getattr(node, name, None)
183 if isinstance(field, ast.AST):
184 yield field
185 elif isinstance(field, list):
186 for item in field:
187 if isinstance(item, ast.AST):
188 yield item
189
190
191def convert_to_value(item):
192 if isinstance(item, ast.Constant):
193 return item.value
194 elif isinstance(item, ast.Tuple):
195 return tuple(convert_to_value(i) for i in item.elts)
196 elif isinstance(item, ast.Name):
197 return VariableKey(item=item)
198 else:
199 return UnhandledKeyType()
200
201
202def is_notimplemented_name_node(node):
203 return isinstance(node, ast.Name) and getNodeName(node) == 'NotImplemented'
204
205
206class Binding:
207 """
208 Represents the binding of a value to a name.
209
210 The checker uses this to keep track of which names have been bound and
211 which names have not. See L{Assignment} for a special type of binding that
212 is checked with stricter rules.
213
214 @ivar used: pair of (L{Scope}, node) indicating the scope and
215 the node that this binding was last used.
216 """
217
218 def __init__(self, name, source):
219 self.name = name
220 self.source = source
221 self.used = False
222
223 def __str__(self):
224 return self.name
225
226 def __repr__(self):
227 return '<{} object {!r} from line {!r} at 0x{:x}>'.format(
228 self.__class__.__name__,
229 self.name,
230 self.source.lineno,
231 id(self),
232 )
233
234 def redefines(self, other):
235 return isinstance(other, Definition) and self.name == other.name
236
237
238class Definition(Binding):
239 """
240 A binding that defines a function or a class.
241 """
242 def redefines(self, other):
243 return (
244 super().redefines(other) or
245 (isinstance(other, Assignment) and self.name == other.name)
246 )
247
248
249class Builtin(Definition):
250 """A definition created for all Python builtins."""
251
252 def __init__(self, name):
253 super().__init__(name, None)
254
255 def __repr__(self):
256 return '<{} object {!r} at 0x{:x}>'.format(
257 self.__class__.__name__,
258 self.name,
259 id(self)
260 )
261
262
263class UnhandledKeyType:
264 """
265 A dictionary key of a type that we cannot or do not check for duplicates.
266 """
267
268
269class VariableKey:
270 """
271 A dictionary key which is a variable.
272
273 @ivar item: The variable AST object.
274 """
275 def __init__(self, item):
276 self.name = item.id
277
278 def __eq__(self, compare):
279 return (
280 compare.__class__ == self.__class__ and
281 compare.name == self.name
282 )
283
284 def __hash__(self):
285 return hash(self.name)
286
287
288class Importation(Definition):
289 """
290 A binding created by an import statement.
291
292 @ivar fullName: The complete name given to the import statement,
293 possibly including multiple dotted components.
294 @type fullName: C{str}
295 """
296
297 def __init__(self, name, source, full_name=None):
298 self.fullName = full_name or name
299 self.redefined = []
300 super().__init__(name, source)
301
302 def redefines(self, other):
303 if isinstance(other, SubmoduleImportation):
304 # See note in SubmoduleImportation about RedefinedWhileUnused
305 return self.fullName == other.fullName
306 return isinstance(other, Definition) and self.name == other.name
307
308 def _has_alias(self):
309 """Return whether importation needs an as clause."""
310 return not self.fullName.split('.')[-1] == self.name
311
312 @property
313 def source_statement(self):
314 """Generate a source statement equivalent to the import."""
315 if self._has_alias():
316 return f'import {self.fullName} as {self.name}'
317 else:
318 return 'import %s' % self.fullName
319
320 def __str__(self):
321 """Return import full name with alias."""
322 if self._has_alias():
323 return self.fullName + ' as ' + self.name
324 else:
325 return self.fullName
326
327
328class SubmoduleImportation(Importation):
329 """
330 A binding created by a submodule import statement.
331
332 A submodule import is a special case where the root module is implicitly
333 imported, without an 'as' clause, and the submodule is also imported.
334 Python does not restrict which attributes of the root module may be used.
335
336 This class is only used when the submodule import is without an 'as' clause.
337
338 pyflakes handles this case by registering the root module name in the scope,
339 allowing any attribute of the root module to be accessed.
340
341 RedefinedWhileUnused is suppressed in `redefines` unless the submodule
342 name is also the same, to avoid false positives.
343 """
344
345 def __init__(self, name, source):
346 # A dot should only appear in the name when it is a submodule import
347 assert '.' in name and (not source or isinstance(source, ast.Import))
348 package_name = name.split('.')[0]
349 super().__init__(package_name, source)
350 self.fullName = name
351
352 def redefines(self, other):
353 if isinstance(other, Importation):
354 return self.fullName == other.fullName
355 return super().redefines(other)
356
357 def __str__(self):
358 return self.fullName
359
360 @property
361 def source_statement(self):
362 return 'import ' + self.fullName
363
364
365class ImportationFrom(Importation):
366
367 def __init__(self, name, source, module, real_name=None):
368 self.module = module
369 self.real_name = real_name or name
370
371 if module.endswith('.'):
372 full_name = module + self.real_name
373 else:
374 full_name = module + '.' + self.real_name
375
376 super().__init__(name, source, full_name)
377
378 def __str__(self):
379 """Return import full name with alias."""
380 if self.real_name != self.name:
381 return self.fullName + ' as ' + self.name
382 else:
383 return self.fullName
384
385 @property
386 def source_statement(self):
387 if self.real_name != self.name:
388 return f'from {self.module} import {self.real_name} as {self.name}'
389 else:
390 return f'from {self.module} import {self.name}'
391
392
393class StarImportation(Importation):
394 """A binding created by a 'from x import *' statement."""
395
396 def __init__(self, name, source):
397 super().__init__('*', source)
398 # Each star importation needs a unique name, and
399 # may not be the module name otherwise it will be deemed imported
400 self.name = name + '.*'
401 self.fullName = name
402
403 @property
404 def source_statement(self):
405 return 'from ' + self.fullName + ' import *'
406
407 def __str__(self):
408 # When the module ends with a ., avoid the ambiguous '..*'
409 if self.fullName.endswith('.'):
410 return self.source_statement
411 else:
412 return self.name
413
414
415class FutureImportation(ImportationFrom):
416 """
417 A binding created by a from `__future__` import statement.
418
419 `__future__` imports are implicitly used.
420 """
421
422 def __init__(self, name, source, scope):
423 super().__init__(name, source, '__future__')
424 self.used = (scope, source)
425
426
427class Argument(Binding):
428 """
429 Represents binding a name as an argument.
430 """
431
432
433class Assignment(Binding):
434 """
435 Represents binding a name with an explicit assignment.
436
437 The checker will raise warnings for any Assignment that isn't used. Also,
438 the checker does not consider assignments in tuple/list unpacking to be
439 Assignments, rather it treats them as simple Bindings.
440 """
441
442
443class NamedExprAssignment(Assignment):
444 """
445 Represents binding a name with an assignment expression.
446 """
447
448
449class Annotation(Binding):
450 """
451 Represents binding a name to a type without an associated value.
452
453 As long as this name is not assigned a value in another binding, it is considered
454 undefined for most purposes. One notable exception is using the name as a type
455 annotation.
456 """
457
458 def redefines(self, other):
459 """An Annotation doesn't define any name, so it cannot redefine one."""
460 return False
461
462
463class FunctionDefinition(Definition):
464 pass
465
466
467class ClassDefinition(Definition):
468 pass
469
470
471class ExportBinding(Binding):
472 """
473 A binding created by an C{__all__} assignment. If the names in the list
474 can be determined statically, they will be treated as names for export and
475 additional checking applied to them.
476
477 The only recognized C{__all__} assignment via list/tuple concatenation is in the
478 following format:
479
480 __all__ = ['a'] + ['b'] + ['c']
481
482 Names which are imported and not otherwise used but appear in the value of
483 C{__all__} will not have an unused import warning reported for them.
484 """
485
486 def __init__(self, name, source, scope):
487 if '__all__' in scope and isinstance(source, ast.AugAssign):
488 self.names = list(scope['__all__'].names)
489 else:
490 self.names = []
491
492 def _add_to_names(container):
493 for node in container.elts:
494 if isinstance(node, ast.Constant) and isinstance(node.value, str):
495 self.names.append(node.value)
496
497 if isinstance(source.value, (ast.List, ast.Tuple)):
498 _add_to_names(source.value)
499 # If concatenating lists or tuples
500 elif isinstance(source.value, ast.BinOp):
501 currentValue = source.value
502 while isinstance(currentValue.right, (ast.List, ast.Tuple)):
503 left = currentValue.left
504 right = currentValue.right
505 _add_to_names(right)
506 # If more lists are being added
507 if isinstance(left, ast.BinOp):
508 currentValue = left
509 # If just two lists are being added
510 elif isinstance(left, (ast.List, ast.Tuple)):
511 _add_to_names(left)
512 # All lists accounted for - done
513 break
514 # If not list concatenation
515 else:
516 break
517 super().__init__(name, source)
518
519
520class Scope(dict):
521 importStarred = False # set to True when import * is found
522
523 def __repr__(self):
524 scope_cls = self.__class__.__name__
525 return f'<{scope_cls} at 0x{id(self):x} {dict.__repr__(self)}>'
526
527
528class ClassScope(Scope):
529 def __init__(self):
530 super().__init__()
531 # {name: node}
532 self.indirect_assignments = {}
533
534
535class FunctionScope(Scope):
536 """
537 I represent a name scope for a function.
538
539 @ivar globals: Names declared 'global' in this function.
540 """
541 usesLocals = False
542 alwaysUsed = {'__tracebackhide__', '__traceback_info__',
543 '__traceback_supplement__', '__debuggerskip__'}
544
545 def __init__(self):
546 super().__init__()
547 # Simplify: manage the special locals as globals
548 self.globals = self.alwaysUsed.copy()
549 # {name: node}
550 self.indirect_assignments = {}
551
552 def unused_assignments(self):
553 """
554 Return a generator for the assignments which have not been used.
555 """
556 for name, binding in self.items():
557 if (not binding.used and
558 name != '_' and # see issue #202
559 name not in self.globals and
560 not self.usesLocals and
561 isinstance(binding, Assignment)):
562 yield name, binding
563
564 def unused_annotations(self):
565 """
566 Return a generator for the annotations which have not been used.
567 """
568 for name, binding in self.items():
569 if not binding.used and isinstance(binding, Annotation):
570 yield name, binding
571
572
573class TypeScope(Scope):
574 pass
575
576
577class GeneratorScope(Scope):
578 pass
579
580
581class ModuleScope(Scope):
582 """Scope for a module."""
583 _futures_allowed = True
584 _annotations_future_enabled = False
585
586
587class DoctestScope(ModuleScope):
588 """Scope for a doctest."""
589
590
591class DetectClassScopedMagic:
592 names = dir()
593
594
595# Globally defined names which are not attributes of the builtins module, or
596# are only present on some platforms.
597_MAGIC_GLOBALS = ['__file__', '__builtins__', '__annotations__', 'WindowsError']
598
599
600def getNodeName(node):
601 # Returns node.id, or node.name, or None
602 if hasattr(node, 'id'): # One of the many nodes with an id
603 return node.id
604 if hasattr(node, 'name'): # an ExceptHandler node
605 return node.name
606 if hasattr(node, 'rest'): # a MatchMapping node
607 return node.rest
608
609
610TYPING_MODULES = frozenset(('typing', 'typing_extensions'))
611
612
613def _is_typing_helper(node, is_name_match_fn, scope_stack):
614 """
615 Internal helper to determine whether or not something is a member of a
616 typing module. This is used as part of working out whether we are within a
617 type annotation context.
618
619 Note: you probably don't want to use this function directly. Instead see the
620 utils below which wrap it (`_is_typing` and `_is_any_typing_member`).
621 """
622
623 def _bare_name_is_attr(name):
624 for scope in reversed(scope_stack):
625 if name in scope:
626 return (
627 isinstance(scope[name], ImportationFrom) and
628 scope[name].module in TYPING_MODULES and
629 is_name_match_fn(scope[name].real_name)
630 )
631
632 return False
633
634 def _module_scope_is_typing(name):
635 for scope in reversed(scope_stack):
636 if name in scope:
637 return (
638 isinstance(scope[name], Importation) and
639 scope[name].fullName in TYPING_MODULES
640 )
641
642 return False
643
644 return (
645 (
646 isinstance(node, ast.Name) and
647 _bare_name_is_attr(node.id)
648 ) or (
649 isinstance(node, ast.Attribute) and
650 isinstance(node.value, ast.Name) and
651 _module_scope_is_typing(node.value.id) and
652 is_name_match_fn(node.attr)
653 )
654 )
655
656
657def _is_typing(node, typing_attr, scope_stack):
658 """
659 Determine whether `node` represents the member of a typing module specified
660 by `typing_attr`.
661
662 This is used as part of working out whether we are within a type annotation
663 context.
664 """
665 return _is_typing_helper(node, lambda x: x == typing_attr, scope_stack)
666
667
668def _is_any_typing_member(node, scope_stack):
669 """
670 Determine whether `node` represents any member of a typing module.
671
672 This is used as part of working out whether we are within a type annotation
673 context.
674 """
675 return _is_typing_helper(node, lambda x: True, scope_stack)
676
677
678def is_typing_overload(value, scope_stack):
679 return (
680 isinstance(value.source, (ast.FunctionDef, ast.AsyncFunctionDef)) and
681 any(
682 _is_typing(dec, 'overload', scope_stack)
683 for dec in value.source.decorator_list
684 )
685 )
686
687
688class AnnotationState:
689 NONE = 0
690 STRING = 1
691 BARE = 2
692
693
694def in_annotation(func):
695 @functools.wraps(func)
696 def in_annotation_func(self, *args, **kwargs):
697 with self._enter_annotation():
698 return func(self, *args, **kwargs)
699 return in_annotation_func
700
701
702def in_string_annotation(func):
703 @functools.wraps(func)
704 def in_annotation_func(self, *args, **kwargs):
705 with self._enter_annotation(AnnotationState.STRING):
706 return func(self, *args, **kwargs)
707 return in_annotation_func
708
709
710class Checker:
711 """I check the cleanliness and sanity of Python code."""
712
713 _ast_node_scope = {
714 ast.Module: ModuleScope,
715 ast.ClassDef: ClassScope,
716 ast.FunctionDef: FunctionScope,
717 ast.AsyncFunctionDef: FunctionScope,
718 ast.Lambda: FunctionScope,
719 ast.ListComp: GeneratorScope,
720 ast.SetComp: GeneratorScope,
721 ast.GeneratorExp: GeneratorScope,
722 ast.DictComp: GeneratorScope,
723 }
724
725 nodeDepth = 0
726 offset = None
727 _in_annotation = AnnotationState.NONE
728
729 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
730 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS')
731 if _customBuiltIns:
732 builtIns.update(_customBuiltIns.split(','))
733 del _customBuiltIns
734
735 def __init__(self, tree, filename='(none)', builtins=None,
736 withDoctest='PYFLAKES_DOCTEST' in os.environ, file_tokens=()):
737 self._nodeHandlers = {}
738 self._deferred = collections.deque()
739 self.deadScopes = []
740 self.messages = []
741 self.filename = filename
742 if builtins:
743 self.builtIns = self.builtIns.union(builtins)
744 self.withDoctest = withDoctest
745 self.exceptHandlers = [()]
746 self.root = tree
747
748 self.scopeStack = []
749 try:
750 scope_tp = Checker._ast_node_scope[type(tree)]
751 except KeyError:
752 raise RuntimeError('No scope implemented for the node %r' % tree)
753
754 with self.in_scope(scope_tp):
755 for builtin in self.builtIns:
756 self.addBinding(None, Builtin(builtin))
757 self.handleChildren(tree)
758 self._run_deferred()
759
760 self.checkDeadScopes()
761
762 if file_tokens:
763 warnings.warn(
764 '`file_tokens` will be removed in a future version',
765 stacklevel=2,
766 )
767
768 def deferFunction(self, callable):
769 """
770 Schedule a function handler to be called just before completion.
771
772 This is used for handling function bodies, which must be deferred
773 because code later in the file might modify the global scope. When
774 `callable` is called, the scope at the time this is called will be
775 restored, however it will contain any new bindings added to it.
776 """
777 self._deferred.append((callable, self.scopeStack[:], self.offset))
778
779 def _run_deferred(self):
780 orig = (self.scopeStack, self.offset)
781
782 while self._deferred:
783 handler, scope, offset = self._deferred.popleft()
784 self.scopeStack, self.offset = scope, offset
785 handler()
786
787 self.scopeStack, self.offset = orig
788
789 def _in_doctest(self):
790 return (len(self.scopeStack) >= 2 and
791 isinstance(self.scopeStack[1], DoctestScope))
792
793 @property
794 def futuresAllowed(self):
795 if not all(isinstance(scope, ModuleScope)
796 for scope in self.scopeStack):
797 return False
798
799 return self.scope._futures_allowed
800
801 @futuresAllowed.setter
802 def futuresAllowed(self, value):
803 assert value is False
804 if isinstance(self.scope, ModuleScope):
805 self.scope._futures_allowed = False
806
807 @property
808 def annotationsFutureEnabled(self):
809 scope = self.scopeStack[0]
810 if not isinstance(scope, ModuleScope):
811 return False
812 return scope._annotations_future_enabled
813
814 @annotationsFutureEnabled.setter
815 def annotationsFutureEnabled(self, value):
816 assert value is True
817 assert isinstance(self.scope, ModuleScope)
818 self.scope._annotations_future_enabled = True
819
820 @property
821 def scope(self):
822 return self.scopeStack[-1]
823
824 @contextlib.contextmanager
825 def in_scope(self, cls):
826 self.scopeStack.append(cls())
827 try:
828 yield
829 finally:
830 self.deadScopes.append(self.scopeStack.pop())
831
832 def checkDeadScopes(self):
833 """
834 Look at scopes which have been fully examined and report names in them
835 which were imported but unused.
836 """
837 for scope in self.deadScopes:
838 if isinstance(scope, (ClassScope, FunctionScope)):
839 for name, node in scope.indirect_assignments.items():
840 self.report(messages.UnusedIndirectAssignment, node, name)
841
842 # imports in classes are public members
843 if isinstance(scope, ClassScope):
844 continue
845
846 if isinstance(scope, FunctionScope):
847 for name, binding in scope.unused_assignments():
848 self.report(messages.UnusedVariable, binding.source, name)
849 for name, binding in scope.unused_annotations():
850 self.report(messages.UnusedAnnotation, binding.source, name)
851
852 all_binding = scope.get('__all__')
853 if all_binding and not isinstance(all_binding, ExportBinding):
854 all_binding = None
855
856 if all_binding:
857 all_names = set(all_binding.names)
858 undefined = [
859 name for name in all_binding.names
860 if name not in scope
861 ]
862 else:
863 all_names = undefined = []
864
865 if undefined:
866 if not scope.importStarred and \
867 os.path.basename(self.filename) != '__init__.py':
868 # Look for possible mistakes in the export list
869 for name in undefined:
870 self.report(messages.UndefinedExport,
871 scope['__all__'].source, name)
872
873 # mark all import '*' as used by the undefined in __all__
874 if scope.importStarred:
875 from_list = []
876 for binding in scope.values():
877 if isinstance(binding, StarImportation):
878 binding.used = all_binding
879 from_list.append(binding.fullName)
880 # report * usage, with a list of possible sources
881 from_list = ', '.join(sorted(from_list))
882 for name in undefined:
883 self.report(messages.ImportStarUsage,
884 scope['__all__'].source, name, from_list)
885
886 # Look for imported names that aren't used.
887 for value in scope.values():
888 if isinstance(value, Importation):
889 used = value.used or value.name in all_names
890 if not used:
891 messg = messages.UnusedImport
892 self.report(messg, value.source, str(value))
893 for node in value.redefined:
894 if isinstance(self.getParent(node), FOR_TYPES):
895 messg = messages.ImportShadowedByLoopVar
896 elif used:
897 continue
898 else:
899 messg = messages.RedefinedWhileUnused
900 self.report(messg, node, value.name, value.source)
901
902 def report(self, messageClass, *args, **kwargs):
903 self.messages.append(messageClass(self.filename, *args, **kwargs))
904
905 def getParent(self, node):
906 # Lookup the first parent which is not Tuple, List or Starred
907 while True:
908 node = node._pyflakes_parent
909 if not hasattr(node, 'elts') and not hasattr(node, 'ctx'):
910 return node
911
912 def getCommonAncestor(self, lnode, rnode, stop):
913 if (
914 stop in (lnode, rnode) or
915 not (
916 hasattr(lnode, '_pyflakes_parent') and
917 hasattr(rnode, '_pyflakes_parent')
918 )
919 ):
920 return None
921 if lnode is rnode:
922 return lnode
923
924 if (lnode._pyflakes_depth > rnode._pyflakes_depth):
925 return self.getCommonAncestor(lnode._pyflakes_parent, rnode, stop)
926 if (lnode._pyflakes_depth < rnode._pyflakes_depth):
927 return self.getCommonAncestor(lnode, rnode._pyflakes_parent, stop)
928 return self.getCommonAncestor(
929 lnode._pyflakes_parent,
930 rnode._pyflakes_parent,
931 stop,
932 )
933
934 def descendantOf(self, node, ancestors, stop):
935 for a in ancestors:
936 if self.getCommonAncestor(node, a, stop):
937 return True
938 return False
939
940 def _getAncestor(self, node, ancestor_type):
941 parent = node
942 while True:
943 if parent is self.root:
944 return None
945 parent = self.getParent(parent)
946 if isinstance(parent, ancestor_type):
947 return parent
948
949 def getScopeNode(self, node):
950 return self._getAncestor(node, tuple(Checker._ast_node_scope.keys()))
951
952 def differentForks(self, lnode, rnode):
953 """True, if lnode and rnode are located on different forks of IF/TRY"""
954 ancestor = self.getCommonAncestor(lnode, rnode, self.root)
955 parts = getAlternatives(ancestor)
956 if parts:
957 for items in parts:
958 if self.descendantOf(lnode, items, ancestor) ^ \
959 self.descendantOf(rnode, items, ancestor):
960 return True
961 return False
962
963 def addBinding(self, node, value):
964 """
965 Called when a binding is altered.
966
967 - `node` is the statement responsible for the change
968 - `value` is the new value, a Binding instance
969 """
970 # assert value.source in (node, node._pyflakes_parent):
971 for scope in self.scopeStack[::-1]:
972 if value.name in scope:
973 break
974 existing = scope.get(value.name)
975
976 if (existing and not isinstance(existing, Builtin) and
977 not self.differentForks(node, existing.source)):
978
979 parent_stmt = self.getParent(value.source)
980 if isinstance(existing, Importation) and isinstance(parent_stmt, FOR_TYPES):
981 self.report(messages.ImportShadowedByLoopVar,
982 node, value.name, existing.source)
983
984 elif scope is self.scope:
985 if (
986 (not existing.used and value.redefines(existing)) and
987 (value.name != '_' or isinstance(existing, Importation)) and
988 not is_typing_overload(existing, self.scopeStack)
989 ):
990 self.report(messages.RedefinedWhileUnused,
991 node, value.name, existing.source)
992
993 if isinstance(scope, (ClassScope, FunctionScope)):
994 scope.indirect_assignments.pop(value.name, None)
995
996 elif isinstance(existing, Importation) and value.redefines(existing):
997 existing.redefined.append(node)
998
999 if value.name in self.scope:
1000 # then assume the rebound name is used as a global or within a loop
1001 value.used = self.scope[value.name].used
1002
1003 # don't treat annotations as assignments if there is an existing value
1004 # in scope
1005 if value.name not in self.scope or not isinstance(value, Annotation):
1006 if isinstance(value, NamedExprAssignment):
1007 # PEP 572: use scope in which outermost generator is defined
1008 scope = next(
1009 scope
1010 for scope in reversed(self.scopeStack)
1011 if not isinstance(scope, GeneratorScope)
1012 )
1013 if value.name in scope and isinstance(scope[value.name], Annotation):
1014 # re-assignment to name that was previously only an annotation
1015 scope[value.name] = value
1016 else:
1017 # it may be a re-assignment to an already existing name
1018 scope.setdefault(value.name, value)
1019 else:
1020 self.scope[value.name] = value
1021
1022 def _unknown_handler(self, node):
1023 # this environment variable configures whether to error on unknown
1024 # ast types.
1025 #
1026 # this is silent by default but the error is enabled for the pyflakes
1027 # testsuite.
1028 #
1029 # this allows new syntax to be added to python without *requiring*
1030 # changes from the pyflakes side. but will still produce an error
1031 # in the pyflakes testsuite (so more specific handling can be added if
1032 # needed).
1033 if os.environ.get('PYFLAKES_ERROR_UNKNOWN'):
1034 raise NotImplementedError(f'Unexpected type: {type(node)}')
1035 else:
1036 self.handleChildren(node)
1037
1038 def getNodeHandler(self, node_class):
1039 try:
1040 return self._nodeHandlers[node_class]
1041 except KeyError:
1042 nodeType = node_class.__name__.upper()
1043 self._nodeHandlers[node_class] = handler = getattr(
1044 self, nodeType, self._unknown_handler,
1045 )
1046 return handler
1047
1048 def handleNodeLoad(self, node, parent):
1049 name = getNodeName(node)
1050 if not name:
1051 return
1052
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
1059 importStarred = None
1060
1061 # try enclosing function scopes and global scope
1062 for scope in self.scopeStack[-1::-1]:
1063 if isinstance(scope, ClassScope):
1064 if name == '__class__':
1065 return
1066 elif can_access_class_vars is False:
1067 # only generators used in a class scope can access the
1068 # names of the class. this is skipped during the first
1069 # iteration
1070 continue
1071
1072 binding = scope.get(name, None)
1073 if isinstance(binding, Annotation) and not self._in_postponed_annotation:
1074 scope[name].used = (self.scope, node)
1075 continue
1076
1077 if name == 'print' and isinstance(binding, Builtin):
1078 if (isinstance(parent, ast.BinOp) and
1079 isinstance(parent.op, ast.RShift)):
1080 self.report(messages.InvalidPrintSyntax, node)
1081
1082 try:
1083 scope[name].used = (self.scope, node)
1084
1085 # if the name of SubImportation is same as
1086 # alias of other Importation and the alias
1087 # is used, SubImportation also should be marked as used.
1088 n = scope[name]
1089 if isinstance(n, Importation) and n._has_alias():
1090 try:
1091 scope[n.fullName].used = (self.scope, node)
1092 except KeyError:
1093 pass
1094 except KeyError:
1095 pass
1096 else:
1097 return
1098
1099 importStarred = importStarred or scope.importStarred
1100
1101 if can_access_class_vars is not False:
1102 can_access_class_vars = isinstance(
1103 scope, (TypeScope, GeneratorScope),
1104 )
1105
1106 if importStarred:
1107 from_list = []
1108
1109 for scope in self.scopeStack[-1::-1]:
1110 for binding in scope.values():
1111 if isinstance(binding, StarImportation):
1112 # mark '*' imports as used for each scope
1113 binding.used = (self.scope, node)
1114 from_list.append(binding.fullName)
1115
1116 # report * usage, with a list of possible sources
1117 from_list = ', '.join(sorted(from_list))
1118 self.report(messages.ImportStarUsage, node, name, from_list)
1119 return
1120
1121 if name == '__path__' and os.path.basename(self.filename) == '__init__.py':
1122 # the special name __path__ is valid only in packages
1123 return
1124
1125 if name in DetectClassScopedMagic.names and isinstance(self.scope, ClassScope):
1126 return
1127
1128 # protected with a NameError handler?
1129 if 'NameError' not in self.exceptHandlers[-1]:
1130 self.report(messages.UndefinedName, node, name)
1131
1132 def handleNodeStore(self, node):
1133 name = getNodeName(node)
1134 if not name:
1135 return
1136 # if the name hasn't already been defined in the current scope
1137 if isinstance(self.scope, FunctionScope) and name not in self.scope:
1138 # for each function or module scope above us
1139 for scope in self.scopeStack[:-1]:
1140 if not isinstance(scope, (FunctionScope, ModuleScope)):
1141 continue
1142 # if the name was defined in that scope, and the name has
1143 # been accessed already in the current scope, and hasn't
1144 # been declared global
1145 used = name in scope and scope[name].used
1146 if used and used[0] is self.scope and name not in self.scope.globals:
1147 # then it's probably a mistake
1148 self.report(messages.UndefinedLocal,
1149 scope[name].used[1], name, scope[name].source)
1150 break
1151
1152 parent_stmt = self.getParent(node)
1153 if isinstance(parent_stmt, ast.AnnAssign) and parent_stmt.value is None:
1154 binding = Annotation(name, node)
1155 elif isinstance(parent_stmt, (FOR_TYPES, ast.comprehension)) or (
1156 parent_stmt != node._pyflakes_parent and
1157 not self.isLiteralTupleUnpacking(parent_stmt)):
1158 binding = Binding(name, node)
1159 elif (
1160 name == '__all__' and
1161 isinstance(self.scope, ModuleScope) and
1162 isinstance(
1163 node._pyflakes_parent,
1164 (ast.Assign, ast.AugAssign, ast.AnnAssign)
1165 )
1166 ):
1167 binding = ExportBinding(name, node._pyflakes_parent, self.scope)
1168 elif isinstance(parent_stmt, ast.NamedExpr):
1169 binding = NamedExprAssignment(name, node)
1170 else:
1171 binding = Assignment(name, node)
1172 self.addBinding(node, binding)
1173
1174 def handleNodeDelete(self, node):
1175
1176 def on_conditional_branch():
1177 """
1178 Return `True` if node is part of a conditional body.
1179 """
1180 current = getattr(node, '_pyflakes_parent', None)
1181 while current:
1182 if isinstance(current, (ast.If, ast.While, ast.IfExp)):
1183 return True
1184 current = getattr(current, '_pyflakes_parent', None)
1185 return False
1186
1187 name = getNodeName(node)
1188 if not name:
1189 return
1190
1191 if on_conditional_branch():
1192 # We cannot predict if this conditional branch is going to
1193 # be executed.
1194 return
1195
1196 if isinstance(self.scope, (ClassScope, FunctionScope)):
1197 self.scope.indirect_assignments.pop(name, None)
1198
1199 if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
1200 self.scope.globals.remove(name)
1201 else:
1202 try:
1203 del self.scope[name]
1204 except KeyError:
1205 self.report(messages.UndefinedName, node, name)
1206
1207 @contextlib.contextmanager
1208 def _enter_annotation(self, ann_type=AnnotationState.BARE):
1209 orig, self._in_annotation = self._in_annotation, ann_type
1210 try:
1211 yield
1212 finally:
1213 self._in_annotation = orig
1214
1215 @property
1216 def _in_postponed_annotation(self):
1217 return (
1218 self._in_annotation == AnnotationState.STRING or
1219 (
1220 self._in_annotation == AnnotationState.BARE and
1221 (self.annotationsFutureEnabled or sys.version_info >= (3, 14))
1222 )
1223 )
1224
1225 def handleChildren(self, tree, omit=None):
1226 for node in iter_child_nodes(tree, omit=omit):
1227 self.handleNode(node, tree)
1228
1229 def isLiteralTupleUnpacking(self, node):
1230 if isinstance(node, ast.Assign):
1231 for child in node.targets + [node.value]:
1232 if not hasattr(child, 'elts'):
1233 return False
1234 return True
1235
1236 def isDocstring(self, node):
1237 """
1238 Determine if the given node is a docstring, as long as it is at the
1239 correct place in the node tree.
1240 """
1241 return (
1242 isinstance(node, ast.Expr) and
1243 isinstance(node.value, ast.Constant) and
1244 isinstance(node.value.value, str)
1245 )
1246
1247 def getDocstring(self, node):
1248 if (
1249 isinstance(node, ast.Expr) and
1250 isinstance(node.value, ast.Constant) and
1251 isinstance(node.value.value, str)
1252 ):
1253 return node.value.value, node.lineno - 1
1254 else:
1255 return None, None
1256
1257 def handleNode(self, node, parent):
1258 if node is None:
1259 return
1260 if self.offset and getattr(node, 'lineno', None) is not None:
1261 node.lineno += self.offset[0]
1262 node.col_offset += self.offset[1]
1263 if (
1264 self.futuresAllowed and
1265 self.nodeDepth == 0 and
1266 not isinstance(node, ast.ImportFrom) and
1267 not self.isDocstring(node)
1268 ):
1269 self.futuresAllowed = False
1270 self.nodeDepth += 1
1271 node._pyflakes_depth = self.nodeDepth
1272 node._pyflakes_parent = parent
1273 try:
1274 handler = self.getNodeHandler(node.__class__)
1275 handler(node)
1276 finally:
1277 self.nodeDepth -= 1
1278
1279 _getDoctestExamples = doctest.DocTestParser().get_examples
1280
1281 def handleDoctests(self, node):
1282 try:
1283 (docstring, node_lineno) = self.getDocstring(node.body[0])
1284 examples = docstring and self._getDoctestExamples(docstring)
1285 except (ValueError, IndexError):
1286 # e.g. line 6 of the docstring for <string> has inconsistent
1287 # leading whitespace: ...
1288 return
1289 if not examples:
1290 return
1291
1292 # Place doctest in module scope
1293 saved_stack = self.scopeStack
1294 self.scopeStack = [self.scopeStack[0]]
1295 node_offset = self.offset or (0, 0)
1296 with self.in_scope(DoctestScope):
1297 if '_' not in self.scopeStack[0]:
1298 self.addBinding(None, Builtin('_'))
1299 for example in examples:
1300 try:
1301 tree = ast.parse(example.source, "<doctest>")
1302 except SyntaxError as e:
1303 position = (node_lineno + example.lineno + e.lineno,
1304 example.indent + 4 + (e.offset or 0))
1305 self.report(messages.DoctestSyntaxError, node, position)
1306 else:
1307 self.offset = (node_offset[0] + node_lineno + example.lineno,
1308 node_offset[1] + example.indent + 4)
1309 self.handleChildren(tree)
1310 self.offset = node_offset
1311 self.scopeStack = saved_stack
1312
1313 @in_string_annotation
1314 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err):
1315 try:
1316 tree = ast.parse(s)
1317 except SyntaxError:
1318 self.report(err, node, s)
1319 return
1320
1321 body = tree.body
1322 if len(body) != 1 or not isinstance(body[0], ast.Expr):
1323 self.report(err, node, s)
1324 return
1325
1326 parsed_annotation = tree.body[0].value
1327 for descendant in ast.walk(parsed_annotation):
1328 if (
1329 'lineno' in descendant._attributes and
1330 'col_offset' in descendant._attributes
1331 ):
1332 descendant.lineno = ref_lineno
1333 descendant.col_offset = ref_col_offset
1334
1335 self.handleNode(parsed_annotation, node)
1336
1337 def handle_annotation_always_deferred(self, annotation, parent):
1338 fn = in_annotation(Checker.handleNode)
1339 self.deferFunction(lambda: fn(self, annotation, parent))
1340
1341 @in_annotation
1342 def handleAnnotation(self, annotation, node):
1343 if (
1344 isinstance(annotation, ast.Constant) and
1345 isinstance(annotation.value, str)
1346 ):
1347 # Defer handling forward annotation.
1348 self.deferFunction(functools.partial(
1349 self.handleStringAnnotation,
1350 annotation.value,
1351 node,
1352 annotation.lineno,
1353 annotation.col_offset,
1354 messages.ForwardAnnotationSyntaxError,
1355 ))
1356 elif self.annotationsFutureEnabled or sys.version_info >= (3, 14):
1357 self.handle_annotation_always_deferred(annotation, node)
1358 else:
1359 self.handleNode(annotation, node)
1360
1361 def ignore(self, node):
1362 pass
1363
1364 # "stmt" type nodes
1365 DELETE = FOR = ASYNCFOR = WHILE = WITH = WITHITEM = ASYNCWITH = \
1366 EXPR = ASSIGN = handleChildren
1367
1368 PASS = ignore
1369
1370 # "expr" type nodes
1371 BOOLOP = UNARYOP = SET = ATTRIBUTE = STARRED = NAMECONSTANT = \
1372 NAMEDEXPR = handleChildren
1373
1374 def SUBSCRIPT(self, node):
1375 if _is_name_or_attr(node.value, 'Literal'):
1376 with self._enter_annotation(AnnotationState.NONE):
1377 self.handleChildren(node)
1378 elif _is_name_or_attr(node.value, 'Annotated'):
1379 self.handleNode(node.value, node)
1380
1381 # py39+
1382 if isinstance(node.slice, ast.Tuple):
1383 slice_tuple = node.slice
1384 # <py39
1385 elif (
1386 isinstance(node.slice, ast.Index) and
1387 isinstance(node.slice.value, ast.Tuple)
1388 ):
1389 slice_tuple = node.slice.value
1390 else:
1391 slice_tuple = None
1392
1393 # not a multi-arg `Annotated`
1394 if slice_tuple is None or len(slice_tuple.elts) < 2:
1395 self.handleNode(node.slice, node)
1396 else:
1397 # the first argument is the type
1398 self.handleNode(slice_tuple.elts[0], node)
1399 # the rest of the arguments are not
1400 with self._enter_annotation(AnnotationState.NONE):
1401 for arg in slice_tuple.elts[1:]:
1402 self.handleNode(arg, node)
1403
1404 self.handleNode(node.ctx, node)
1405 else:
1406 if _is_any_typing_member(node.value, self.scopeStack):
1407 with self._enter_annotation():
1408 self.handleChildren(node)
1409 else:
1410 self.handleChildren(node)
1411
1412 def _handle_string_dot_format(self, node):
1413 try:
1414 placeholders = tuple(parse_format_string(node.func.value.value))
1415 except ValueError as e:
1416 self.report(messages.StringDotFormatInvalidFormat, node, e)
1417 return
1418
1419 auto = None
1420 next_auto = 0
1421
1422 placeholder_positional = set()
1423 placeholder_named = set()
1424
1425 def _add_key(fmtkey):
1426 """Returns True if there is an error which should early-exit"""
1427 nonlocal auto, next_auto
1428
1429 if fmtkey is None: # end of string or `{` / `}` escapes
1430 return False
1431
1432 # attributes / indices are allowed in `.format(...)`
1433 fmtkey, _, _ = fmtkey.partition('.')
1434 fmtkey, _, _ = fmtkey.partition('[')
1435
1436 try:
1437 fmtkey = int(fmtkey)
1438 except ValueError:
1439 pass
1440 else: # fmtkey was an integer
1441 if auto is True:
1442 self.report(messages.StringDotFormatMixingAutomatic, node)
1443 return True
1444 else:
1445 auto = False
1446
1447 if fmtkey == '':
1448 if auto is False:
1449 self.report(messages.StringDotFormatMixingAutomatic, node)
1450 return True
1451 else:
1452 auto = True
1453
1454 fmtkey = next_auto
1455 next_auto += 1
1456
1457 if isinstance(fmtkey, int):
1458 placeholder_positional.add(fmtkey)
1459 else:
1460 placeholder_named.add(fmtkey)
1461
1462 return False
1463
1464 for _, fmtkey, spec, _ in placeholders:
1465 if _add_key(fmtkey):
1466 return
1467
1468 # spec can also contain format specifiers
1469 if spec is not None:
1470 try:
1471 spec_placeholders = tuple(parse_format_string(spec))
1472 except ValueError as e:
1473 self.report(messages.StringDotFormatInvalidFormat, node, e)
1474 return
1475
1476 for _, spec_fmtkey, spec_spec, _ in spec_placeholders:
1477 # can't recurse again
1478 if spec_spec is not None and '{' in spec_spec:
1479 self.report(
1480 messages.StringDotFormatInvalidFormat,
1481 node,
1482 'Max string recursion exceeded',
1483 )
1484 return
1485 if _add_key(spec_fmtkey):
1486 return
1487
1488 # bail early if there is *args or **kwargs
1489 if (
1490 # *args
1491 any(isinstance(arg, ast.Starred) for arg in node.args) or
1492 # **kwargs
1493 any(kwd.arg is None for kwd in node.keywords)
1494 ):
1495 return
1496
1497 substitution_positional = set(range(len(node.args)))
1498 substitution_named = {kwd.arg for kwd in node.keywords}
1499
1500 extra_positional = substitution_positional - placeholder_positional
1501 extra_named = substitution_named - placeholder_named
1502
1503 missing_arguments = (
1504 (placeholder_positional | placeholder_named) -
1505 (substitution_positional | substitution_named)
1506 )
1507
1508 if extra_positional:
1509 self.report(
1510 messages.StringDotFormatExtraPositionalArguments,
1511 node,
1512 ', '.join(sorted(str(x) for x in extra_positional)),
1513 )
1514 if extra_named:
1515 self.report(
1516 messages.StringDotFormatExtraNamedArguments,
1517 node,
1518 ', '.join(sorted(extra_named)),
1519 )
1520 if missing_arguments:
1521 self.report(
1522 messages.StringDotFormatMissingArgument,
1523 node,
1524 ', '.join(sorted(str(x) for x in missing_arguments)),
1525 )
1526
1527 def CALL(self, node):
1528 if (
1529 isinstance(node.func, ast.Attribute) and
1530 isinstance(node.func.value, ast.Constant) and
1531 isinstance(node.func.value.value, str) and
1532 node.func.attr == 'format'
1533 ):
1534 self._handle_string_dot_format(node)
1535
1536 omit = []
1537 annotated = []
1538 not_annotated = []
1539
1540 if (
1541 _is_typing(node.func, 'cast', self.scopeStack) and
1542 len(node.args) >= 1
1543 ):
1544 with self._enter_annotation():
1545 self.handleNode(node.args[0], node)
1546
1547 elif _is_typing(node.func, 'TypeVar', self.scopeStack):
1548
1549 # TypeVar("T", "int", "str")
1550 omit += ["args"]
1551 annotated += [arg for arg in node.args[1:]]
1552
1553 # TypeVar("T", bound="str")
1554 omit += ["keywords"]
1555 annotated += [k.value for k in node.keywords if k.arg == "bound"]
1556 not_annotated += [
1557 (k, ["value"] if k.arg == "bound" else None)
1558 for k in node.keywords
1559 ]
1560
1561 elif _is_typing(node.func, "TypedDict", self.scopeStack):
1562 # TypedDict("a", {"a": int})
1563 if len(node.args) > 1 and isinstance(node.args[1], ast.Dict):
1564 omit += ["args"]
1565 annotated += node.args[1].values
1566 not_annotated += [
1567 (arg, ["values"] if i == 1 else None)
1568 for i, arg in enumerate(node.args)
1569 ]
1570
1571 # TypedDict("a", a=int)
1572 omit += ["keywords"]
1573 annotated += [k.value for k in node.keywords]
1574 not_annotated += [(k, ["value"]) for k in node.keywords]
1575
1576 elif _is_typing(node.func, "NamedTuple", self.scopeStack):
1577 # NamedTuple("a", [("a", int)])
1578 if (
1579 len(node.args) > 1 and
1580 isinstance(node.args[1], (ast.Tuple, ast.List)) and
1581 all(isinstance(x, (ast.Tuple, ast.List)) and
1582 len(x.elts) == 2 for x in node.args[1].elts)
1583 ):
1584 omit += ["args"]
1585 annotated += [elt.elts[1] for elt in node.args[1].elts]
1586 not_annotated += [(elt.elts[0], None) for elt in node.args[1].elts]
1587 not_annotated += [
1588 (arg, ["elts"] if i == 1 else None)
1589 for i, arg in enumerate(node.args)
1590 ]
1591 not_annotated += [(elt, "elts") for elt in node.args[1].elts]
1592
1593 # NamedTuple("a", a=int)
1594 omit += ["keywords"]
1595 annotated += [k.value for k in node.keywords]
1596 not_annotated += [(k, ["value"]) for k in node.keywords]
1597
1598 if omit:
1599 with self._enter_annotation(AnnotationState.NONE):
1600 for na_node, na_omit in not_annotated:
1601 self.handleChildren(na_node, omit=na_omit)
1602 self.handleChildren(node, omit=omit)
1603
1604 with self._enter_annotation():
1605 for annotated_node in annotated:
1606 self.handleNode(annotated_node, node)
1607 else:
1608 self.handleChildren(node)
1609
1610 def _handle_percent_format(self, node):
1611 try:
1612 placeholders = parse_percent_format(node.left.value)
1613 except ValueError:
1614 self.report(
1615 messages.PercentFormatInvalidFormat,
1616 node,
1617 'incomplete format',
1618 )
1619 return
1620
1621 named = set()
1622 positional_count = 0
1623 positional = None
1624 for _, placeholder in placeholders:
1625 if placeholder is None:
1626 continue
1627 name, _, width, precision, conversion = placeholder
1628
1629 if conversion == '%':
1630 continue
1631
1632 if conversion not in VALID_CONVERSIONS:
1633 self.report(
1634 messages.PercentFormatUnsupportedFormatCharacter,
1635 node,
1636 conversion,
1637 )
1638
1639 if positional is None and conversion:
1640 positional = name is None
1641
1642 for part in (width, precision):
1643 if part is not None and '*' in part:
1644 if not positional:
1645 self.report(
1646 messages.PercentFormatStarRequiresSequence,
1647 node,
1648 )
1649 else:
1650 positional_count += 1
1651
1652 if positional and name is not None:
1653 self.report(
1654 messages.PercentFormatMixedPositionalAndNamed,
1655 node,
1656 )
1657 return
1658 elif not positional and name is None:
1659 self.report(
1660 messages.PercentFormatMixedPositionalAndNamed,
1661 node,
1662 )
1663 return
1664
1665 if positional:
1666 positional_count += 1
1667 else:
1668 named.add(name)
1669
1670 if (
1671 isinstance(node.right, (ast.List, ast.Tuple)) and
1672 # does not have any *splats (py35+ feature)
1673 not any(
1674 isinstance(elt, ast.Starred)
1675 for elt in node.right.elts
1676 )
1677 ):
1678 substitution_count = len(node.right.elts)
1679 if positional and positional_count != substitution_count:
1680 self.report(
1681 messages.PercentFormatPositionalCountMismatch,
1682 node,
1683 positional_count,
1684 substitution_count,
1685 )
1686 elif not positional:
1687 self.report(messages.PercentFormatExpectedMapping, node)
1688
1689 if (
1690 isinstance(node.right, ast.Dict) and
1691 all(
1692 isinstance(k, ast.Constant) and isinstance(k.value, str)
1693 for k in node.right.keys
1694 )
1695 ):
1696 if positional and positional_count > 1:
1697 self.report(messages.PercentFormatExpectedSequence, node)
1698 return
1699
1700 substitution_keys = {k.value for k in node.right.keys}
1701 extra_keys = substitution_keys - named
1702 missing_keys = named - substitution_keys
1703 if not positional and extra_keys:
1704 self.report(
1705 messages.PercentFormatExtraNamedArguments,
1706 node,
1707 ', '.join(sorted(extra_keys)),
1708 )
1709 if not positional and missing_keys:
1710 self.report(
1711 messages.PercentFormatMissingArgument,
1712 node,
1713 ', '.join(sorted(missing_keys)),
1714 )
1715
1716 def BINOP(self, node):
1717 if (
1718 isinstance(node.op, ast.Mod) and
1719 isinstance(node.left, ast.Constant) and
1720 isinstance(node.left.value, str)
1721 ):
1722 self._handle_percent_format(node)
1723 self.handleChildren(node)
1724
1725 def CONSTANT(self, node):
1726 if isinstance(node.value, str) and self._in_annotation:
1727 fn = functools.partial(
1728 self.handleStringAnnotation,
1729 node.value,
1730 node,
1731 node.lineno,
1732 node.col_offset,
1733 messages.ForwardAnnotationSyntaxError,
1734 )
1735 self.deferFunction(fn)
1736
1737 # "slice" type nodes
1738 SLICE = EXTSLICE = INDEX = handleChildren
1739
1740 # expression contexts are node instances too, though being constants
1741 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
1742
1743 # same for operators
1744 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
1745 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
1746 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = \
1747 MATMULT = ignore
1748
1749 def RAISE(self, node):
1750 self.handleChildren(node)
1751
1752 arg = node.exc
1753
1754 if isinstance(arg, ast.Call):
1755 if is_notimplemented_name_node(arg.func):
1756 # Handle "raise NotImplemented(...)"
1757 self.report(messages.RaiseNotImplemented, node)
1758 elif is_notimplemented_name_node(arg):
1759 # Handle "raise NotImplemented"
1760 self.report(messages.RaiseNotImplemented, node)
1761
1762 # additional node types
1763 COMPREHENSION = KEYWORD = FORMATTEDVALUE = handleChildren
1764
1765 _in_fstring = False
1766
1767 def JOINEDSTR(self, node):
1768 if (
1769 # the conversion / etc. flags are parsed as f-strings without
1770 # placeholders
1771 not self._in_fstring and
1772 not any(isinstance(x, ast.FormattedValue) for x in node.values)
1773 ):
1774 self.report(messages.FStringMissingPlaceholders, node)
1775
1776 self._in_fstring, orig = True, self._in_fstring
1777 try:
1778 self.handleChildren(node)
1779 finally:
1780 self._in_fstring = orig
1781
1782 def TEMPLATESTR(self, node):
1783 if not any(isinstance(x, ast.Interpolation) for x in node.values):
1784 self.report(messages.TStringMissingPlaceholders, node)
1785
1786 # similar to f-strings, conversion / etc. flags are parsed as f-strings
1787 # without placeholders
1788 self._in_fstring, orig = True, self._in_fstring
1789 try:
1790 self.handleChildren(node)
1791 finally:
1792 self._in_fstring = orig
1793
1794 INTERPOLATION = handleChildren
1795
1796 def DICT(self, node):
1797 # Complain if there are duplicate keys with different values
1798 # If they have the same value it's not going to cause potentially
1799 # unexpected behaviour so we'll not complain.
1800 keys = [
1801 convert_to_value(key) for key in node.keys
1802 ]
1803
1804 key_counts = collections.Counter(keys)
1805 duplicate_keys = [
1806 key for key, count in key_counts.items()
1807 if count > 1
1808 ]
1809
1810 for key in duplicate_keys:
1811 key_indices = [i for i, i_key in enumerate(keys) if i_key == key]
1812
1813 values = collections.Counter(
1814 convert_to_value(node.values[index])
1815 for index in key_indices
1816 )
1817 if any(count == 1 for value, count in values.items()):
1818 for key_index in key_indices:
1819 key_node = node.keys[key_index]
1820 if isinstance(key, VariableKey):
1821 self.report(messages.MultiValueRepeatedKeyVariable,
1822 key_node,
1823 key.name)
1824 else:
1825 self.report(
1826 messages.MultiValueRepeatedKeyLiteral,
1827 key_node,
1828 key,
1829 )
1830 self.handleChildren(node)
1831
1832 def IF(self, node):
1833 if isinstance(node.test, ast.Tuple) and node.test.elts != []:
1834 self.report(messages.IfTuple, node)
1835 self.handleChildren(node)
1836
1837 IFEXP = IF
1838
1839 def ASSERT(self, node):
1840 if isinstance(node.test, ast.Tuple) and node.test.elts != []:
1841 self.report(messages.AssertTuple, node)
1842 self.handleChildren(node)
1843
1844 def GLOBAL(self, node):
1845 """
1846 Keep track of globals declarations.
1847 """
1848 global_scope_index = 1 if self._in_doctest() else 0
1849 global_scope = self.scopeStack[global_scope_index]
1850
1851 # Ignore 'global' statement in global scope.
1852 if self.scope is not global_scope:
1853
1854 # One 'global' statement can bind multiple (comma-delimited) names.
1855 for node_name in node.names:
1856 node_value = Assignment(node_name, node)
1857
1858 # Remove UndefinedName messages already reported for this name.
1859 # TODO: if the global is not used in this scope, it does not
1860 # become a globally defined name. See test_unused_global.
1861 self.messages = [
1862 m for m in self.messages if not
1863 isinstance(m, messages.UndefinedName) or
1864 m.message_args[0] != node_name]
1865
1866 # Bind name to global scope if it doesn't exist already.
1867 global_scope.setdefault(node_name, node_value)
1868
1869 # Bind name to non-global scopes, but as already "used".
1870 node_value.used = (global_scope, node)
1871 for scope in self.scopeStack[global_scope_index + 1:]:
1872 scope[node_name] = node_value
1873
1874 self.scope.indirect_assignments[node_name] = node
1875
1876 NONLOCAL = GLOBAL
1877
1878 def GENERATOREXP(self, node):
1879 with self.in_scope(GeneratorScope):
1880 self.handleChildren(node)
1881
1882 LISTCOMP = DICTCOMP = SETCOMP = GENERATOREXP
1883
1884 def NAME(self, node):
1885 """
1886 Handle occurrence of Name (which can be a load/store/delete access.)
1887 """
1888 # Locate the name in locals / function / globals scopes.
1889 if isinstance(node.ctx, ast.Load):
1890 self.handleNodeLoad(node, self.getParent(node))
1891 if (node.id == 'locals' and isinstance(self.scope, FunctionScope) and
1892 isinstance(node._pyflakes_parent, ast.Call)):
1893 # we are doing locals() call in current scope
1894 self.scope.usesLocals = True
1895 elif isinstance(node.ctx, ast.Store):
1896 self.handleNodeStore(node)
1897 elif isinstance(node.ctx, ast.Del):
1898 self.handleNodeDelete(node)
1899 else:
1900 # Unknown context
1901 raise RuntimeError(f"Got impossible expression context: {node.ctx!r}")
1902
1903 def CONTINUE(self, node):
1904 # Walk the tree up until we see a loop (OK), a function or class
1905 # definition (not OK), for 'continue', a finally block (not OK), or
1906 # the top module scope (not OK)
1907 n = node
1908 while hasattr(n, '_pyflakes_parent'):
1909 n, n_child = n._pyflakes_parent, n
1910 if isinstance(n, (ast.While, ast.For, ast.AsyncFor)):
1911 # Doesn't apply unless it's in the loop itself
1912 if n_child not in n.orelse:
1913 return
1914 if isinstance(n, (ast.FunctionDef, ast.ClassDef)):
1915 break
1916 if isinstance(node, ast.Continue):
1917 self.report(messages.ContinueOutsideLoop, node)
1918 else: # ast.Break
1919 self.report(messages.BreakOutsideLoop, node)
1920
1921 BREAK = CONTINUE
1922
1923 def RETURN(self, node):
1924 if isinstance(self.scope, (ClassScope, ModuleScope)):
1925 self.report(messages.ReturnOutsideFunction, node)
1926 return
1927
1928 self.handleNode(node.value, node)
1929
1930 def YIELD(self, node):
1931 if isinstance(self.scope, (ClassScope, ModuleScope)):
1932 self.report(messages.YieldOutsideFunction, node)
1933 return
1934
1935 self.handleNode(node.value, node)
1936
1937 AWAIT = YIELDFROM = YIELD
1938
1939 def FUNCTIONDEF(self, node):
1940 for deco in node.decorator_list:
1941 self.handleNode(deco, node)
1942
1943 with self._type_param_scope(node):
1944 self.LAMBDA(node)
1945
1946 self.addBinding(node, FunctionDefinition(node.name, node))
1947 # doctest does not process doctest within a doctest,
1948 # or in nested functions.
1949 if (self.withDoctest and
1950 not self._in_doctest() and
1951 not isinstance(self.scope, FunctionScope)):
1952 self.deferFunction(lambda: self.handleDoctests(node))
1953
1954 ASYNCFUNCTIONDEF = FUNCTIONDEF
1955
1956 def LAMBDA(self, node):
1957 args = []
1958 annotations = []
1959
1960 for arg in node.args.posonlyargs:
1961 args.append(arg.arg)
1962 annotations.append(arg.annotation)
1963 for arg in node.args.args + node.args.kwonlyargs:
1964 args.append(arg.arg)
1965 annotations.append(arg.annotation)
1966 defaults = node.args.defaults + node.args.kw_defaults
1967
1968 has_annotations = not isinstance(node, ast.Lambda)
1969
1970 for arg_name in ('vararg', 'kwarg'):
1971 wildcard = getattr(node.args, arg_name)
1972 if not wildcard:
1973 continue
1974 args.append(wildcard.arg)
1975 if has_annotations:
1976 annotations.append(wildcard.annotation)
1977
1978 if has_annotations:
1979 annotations.append(node.returns)
1980
1981 if len(set(args)) < len(args):
1982 for (idx, arg) in enumerate(args):
1983 if arg in args[:idx]:
1984 self.report(messages.DuplicateArgument, node, arg)
1985
1986 for annotation in annotations:
1987 self.handleAnnotation(annotation, node)
1988
1989 for default in defaults:
1990 self.handleNode(default, node)
1991
1992 def runFunction():
1993 with self.in_scope(FunctionScope):
1994 self.handleChildren(
1995 node,
1996 omit=('decorator_list', 'returns', 'type_params'),
1997 )
1998
1999 self.deferFunction(runFunction)
2000
2001 def ARGUMENTS(self, node):
2002 self.handleChildren(node, omit=('defaults', 'kw_defaults'))
2003
2004 def ARG(self, node):
2005 self.addBinding(node, Argument(node.arg, self.getScopeNode(node)))
2006
2007 def CLASSDEF(self, node):
2008 """
2009 Check names used in a class definition, including its decorators, base
2010 classes, and the body of its definition. Additionally, add its name to
2011 the current scope.
2012 """
2013 for deco in node.decorator_list:
2014 self.handleNode(deco, node)
2015
2016 with self._type_param_scope(node):
2017 for baseNode in node.bases:
2018 self.handleNode(baseNode, node)
2019 for keywordNode in node.keywords:
2020 self.handleNode(keywordNode, node)
2021 with self.in_scope(ClassScope):
2022 # doctest does not process doctest within a doctest
2023 # classes within classes are processed.
2024 if (self.withDoctest and
2025 not self._in_doctest() and
2026 not isinstance(self.scope, FunctionScope)):
2027 self.deferFunction(lambda: self.handleDoctests(node))
2028 for stmt in node.body:
2029 self.handleNode(stmt, node)
2030
2031 self.addBinding(node, ClassDefinition(node.name, node))
2032
2033 def AUGASSIGN(self, node):
2034 self.handleNodeLoad(node.target, node)
2035 self.handleNode(node.value, node)
2036 self.handleNode(node.target, node)
2037
2038 def TUPLE(self, node):
2039 if isinstance(node.ctx, ast.Store):
2040 # Python 3 advanced tuple unpacking: a, *b, c = d.
2041 # Only one starred expression is allowed, and no more than 1<<8
2042 # assignments are allowed before a stared expression. There is
2043 # also a limit of 1<<24 expressions after the starred expression,
2044 # which is impossible to test due to memory restrictions, but we
2045 # add it here anyway
2046 has_starred = False
2047 star_loc = -1
2048 for i, n in enumerate(node.elts):
2049 if isinstance(n, ast.Starred):
2050 if has_starred:
2051 self.report(messages.TwoStarredExpressions, node)
2052 # The SyntaxError doesn't distinguish two from more
2053 # than two.
2054 break
2055 has_starred = True
2056 star_loc = i
2057 if star_loc >= 1 << 8 or len(node.elts) - star_loc - 1 >= 1 << 24:
2058 self.report(messages.TooManyExpressionsInStarredAssignment, node)
2059 self.handleChildren(node)
2060
2061 LIST = TUPLE
2062
2063 def IMPORT(self, node):
2064 for alias in node.names:
2065 if '.' in alias.name and not alias.asname:
2066 importation = SubmoduleImportation(alias.name, node)
2067 else:
2068 name = alias.asname or alias.name
2069 importation = Importation(name, node, alias.name)
2070 self.addBinding(node, importation)
2071
2072 def IMPORTFROM(self, node):
2073 if node.module == '__future__':
2074 if not self.futuresAllowed:
2075 self.report(messages.LateFutureImport, node)
2076 else:
2077 self.futuresAllowed = False
2078
2079 module = ('.' * node.level) + (node.module or '')
2080
2081 for alias in node.names:
2082 name = alias.asname or alias.name
2083 if node.module == '__future__':
2084 importation = FutureImportation(name, node, self.scope)
2085 if alias.name not in __future__.all_feature_names:
2086 self.report(messages.FutureFeatureNotDefined,
2087 node, alias.name)
2088 if alias.name == 'annotations':
2089 self.annotationsFutureEnabled = True
2090 elif alias.name == '*':
2091 if not isinstance(self.scope, ModuleScope):
2092 self.report(messages.ImportStarNotPermitted,
2093 node, module)
2094 continue
2095
2096 self.scope.importStarred = True
2097 self.report(messages.ImportStarUsed, node, module)
2098 importation = StarImportation(module, node)
2099 else:
2100 importation = ImportationFrom(name, node,
2101 module, alias.name)
2102 self.addBinding(node, importation)
2103
2104 def TRY(self, node):
2105 handler_names = []
2106 # List the exception handlers
2107 for i, handler in enumerate(node.handlers):
2108 if isinstance(handler.type, ast.Tuple):
2109 for exc_type in handler.type.elts:
2110 handler_names.append(getNodeName(exc_type))
2111 elif handler.type:
2112 handler_names.append(getNodeName(handler.type))
2113
2114 if handler.type is None and i < len(node.handlers) - 1:
2115 self.report(messages.DefaultExceptNotLast, handler)
2116 # Memorize the except handlers and process the body
2117 self.exceptHandlers.append(handler_names)
2118 for child in node.body:
2119 self.handleNode(child, node)
2120 self.exceptHandlers.pop()
2121 # Process the other nodes: "except:", "else:", "finally:"
2122 self.handleChildren(node, omit='body')
2123
2124 TRYSTAR = TRY
2125
2126 def EXCEPTHANDLER(self, node):
2127 if node.name is None:
2128 self.handleChildren(node)
2129 return
2130
2131 # If the name already exists in the scope, modify state of existing
2132 # binding.
2133 if node.name in self.scope:
2134 self.handleNodeStore(node)
2135
2136 # 3.x: the name of the exception, which is not a Name node, but a
2137 # simple string, creates a local that is only bound within the scope of
2138 # the except: block. As such, temporarily remove the existing binding
2139 # to more accurately determine if the name is used in the except:
2140 # block.
2141
2142 try:
2143 prev_definition = self.scope.pop(node.name)
2144 except KeyError:
2145 prev_definition = None
2146
2147 self.handleNodeStore(node)
2148 self.handleChildren(node)
2149
2150 # See discussion on https://github.com/PyCQA/pyflakes/pull/59
2151
2152 # We're removing the local name since it's being unbound after leaving
2153 # the except: block and it's always unbound if the except: block is
2154 # never entered. This will cause an "undefined name" error raised if
2155 # the checked code tries to use the name afterwards.
2156 #
2157 # Unless it's been removed already. Then do nothing.
2158
2159 try:
2160 binding = self.scope.pop(node.name)
2161 except KeyError:
2162 pass
2163 else:
2164 if not binding.used:
2165 self.report(messages.UnusedVariable, node, node.name)
2166
2167 # Restore.
2168 if prev_definition:
2169 self.scope[node.name] = prev_definition
2170
2171 def ANNASSIGN(self, node):
2172 self.handleAnnotation(node.annotation, node)
2173 # If the assignment has value, handle the *value* now.
2174 if node.value:
2175 # If the annotation is `TypeAlias`, handle the *value* as an annotation.
2176 if _is_typing(node.annotation, 'TypeAlias', self.scopeStack):
2177 self.handleAnnotation(node.value, node)
2178 else:
2179 self.handleNode(node.value, node)
2180 self.handleNode(node.target, node)
2181
2182 def COMPARE(self, node):
2183 left = node.left
2184 for op, right in zip(node.ops, node.comparators):
2185 if (
2186 isinstance(op, (ast.Is, ast.IsNot)) and (
2187 _is_const_non_singleton(left) or
2188 _is_const_non_singleton(right)
2189 )
2190 ):
2191 self.report(messages.IsLiteral, node)
2192 left = right
2193
2194 self.handleChildren(node)
2195
2196 MATCH = MATCH_CASE = MATCHCLASS = MATCHOR = MATCHSEQUENCE = handleChildren
2197 MATCHSINGLETON = MATCHVALUE = handleChildren
2198
2199 def _match_target(self, node):
2200 self.handleNodeStore(node)
2201 self.handleChildren(node)
2202
2203 MATCHAS = MATCHMAPPING = MATCHSTAR = _match_target
2204
2205 @contextlib.contextmanager
2206 def _type_param_scope(self, node):
2207 with contextlib.ExitStack() as ctx:
2208 if sys.version_info >= (3, 12):
2209 ctx.enter_context(self.in_scope(TypeScope))
2210 for param in node.type_params:
2211 self.handleNode(param, node)
2212 yield
2213
2214 def TYPEVAR(self, node):
2215 self.handleNodeStore(node)
2216 self.handle_annotation_always_deferred(node.bound, node)
2217
2218 PARAMSPEC = TYPEVARTUPLE = handleNodeStore
2219
2220 def TYPEALIAS(self, node):
2221 self.handleNode(node.name, node)
2222 with self._type_param_scope(node):
2223 self.handle_annotation_always_deferred(node.value, node)