Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pyflakes/checker.py: 90%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Main module.
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
20from pyflakes import messages
22PYPY = hasattr(sys, 'pypy_version_info')
24builtin_vars = dir(builtins)
26parse_format_string = string.Formatter().parse
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]
38FOR_TYPES = (ast.For, ast.AsyncFor)
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 )
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 )
55def _is_constant(node):
56 return isinstance(node, ast.Constant) or _is_tuple_constant(node)
59def _is_const_non_singleton(node): # type: (ast.AST) -> bool
60 return _is_constant(node) and not _is_singleton(node)
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 )
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%')
79def _must_match(regex, string, pos):
80 match = regex.match(string, pos)
81 assert match is not None
82 return match
85def parse_percent_format(s):
86 """Parses the string component of a `'...' % ...` format call
88 Copied from https://github.com/asottile/pyupgrade at v1.20.1
89 """
91 def _parse_inner():
92 string_start = 0
93 string_end = 0
94 in_fmt = False
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
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()
120 width_match = _must_match(WIDTH_RE, s, i)
121 width = width_match.group() or None
122 i = width_match.end()
124 precision_match = _must_match(PRECISION_RE, s, i)
125 precision = precision_match.group() or None
126 i = precision_match.end()
128 # length modifier is ignored
129 i = _must_match(LENGTH_RE, s, i).end()
131 try:
132 conversion = s[i]
133 except IndexError:
134 raise ValueError('end-of-string while parsing format')
135 i += 1
137 fmt = (key, conversion_flag, width, precision, conversion)
138 yield s[string_start:string_end], fmt
140 in_fmt = False
141 string_start = i
143 if in_fmt:
144 raise ValueError('end-of-string while parsing format')
146 return tuple(_parse_inner())
149class _FieldsOrder(dict):
150 """Fix order of AST node fields."""
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))
163 def __missing__(self, node_class):
164 self[node_class] = fields = self._get_fields(node_class)
165 return fields
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.
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
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()
202def is_notimplemented_name_node(node):
203 return isinstance(node, ast.Name) and getNodeName(node) == 'NotImplemented'
206class Binding:
207 """
208 Represents the binding of a value to a name.
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.
214 @ivar used: pair of (L{Scope}, node) indicating the scope and
215 the node that this binding was last used.
216 """
218 def __init__(self, name, source):
219 self.name = name
220 self.source = source
221 self.used = False
223 def __str__(self):
224 return self.name
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 )
234 def redefines(self, other):
235 return isinstance(other, Definition) and self.name == other.name
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 )
249class Builtin(Definition):
250 """A definition created for all Python builtins."""
252 def __init__(self, name):
253 super().__init__(name, None)
255 def __repr__(self):
256 return '<{} object {!r} at 0x{:x}>'.format(
257 self.__class__.__name__,
258 self.name,
259 id(self)
260 )
263class UnhandledKeyType:
264 """
265 A dictionary key of a type that we cannot or do not check for duplicates.
266 """
269class VariableKey:
270 """
271 A dictionary key which is a variable.
273 @ivar item: The variable AST object.
274 """
275 def __init__(self, item):
276 self.name = item.id
278 def __eq__(self, compare):
279 return (
280 compare.__class__ == self.__class__ and
281 compare.name == self.name
282 )
284 def __hash__(self):
285 return hash(self.name)
288class Importation(Definition):
289 """
290 A binding created by an import statement.
292 @ivar fullName: The complete name given to the import statement,
293 possibly including multiple dotted components.
294 @type fullName: C{str}
295 """
297 def __init__(self, name, source, full_name=None):
298 self.fullName = full_name or name
299 self.redefined = []
300 super().__init__(name, source)
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
308 def _has_alias(self):
309 """Return whether importation needs an as clause."""
310 return not self.fullName.split('.')[-1] == self.name
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
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
328class SubmoduleImportation(Importation):
329 """
330 A binding created by a submodule import statement.
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.
336 This class is only used when the submodule import is without an 'as' clause.
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.
341 RedefinedWhileUnused is suppressed in `redefines` unless the submodule
342 name is also the same, to avoid false positives.
343 """
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
352 def redefines(self, other):
353 if isinstance(other, Importation):
354 return self.fullName == other.fullName
355 return super().redefines(other)
357 def __str__(self):
358 return self.fullName
360 @property
361 def source_statement(self):
362 return 'import ' + self.fullName
365class ImportationFrom(Importation):
367 def __init__(self, name, source, module, real_name=None):
368 self.module = module
369 self.real_name = real_name or name
371 if module.endswith('.'):
372 full_name = module + self.real_name
373 else:
374 full_name = module + '.' + self.real_name
376 super().__init__(name, source, full_name)
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
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}'
393class StarImportation(Importation):
394 """A binding created by a 'from x import *' statement."""
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
403 @property
404 def source_statement(self):
405 return 'from ' + self.fullName + ' import *'
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
415class FutureImportation(ImportationFrom):
416 """
417 A binding created by a from `__future__` import statement.
419 `__future__` imports are implicitly used.
420 """
422 def __init__(self, name, source, scope):
423 super().__init__(name, source, '__future__')
424 self.used = (scope, source)
427class Argument(Binding):
428 """
429 Represents binding a name as an argument.
430 """
433class Assignment(Binding):
434 """
435 Represents binding a name with an explicit assignment.
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 """
443class NamedExprAssignment(Assignment):
444 """
445 Represents binding a name with an assignment expression.
446 """
449class Annotation(Binding):
450 """
451 Represents binding a name to a type without an associated value.
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 """
458 def redefines(self, other):
459 """An Annotation doesn't define any name, so it cannot redefine one."""
460 return False
463class FunctionDefinition(Definition):
464 pass
467class ClassDefinition(Definition):
468 pass
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.
477 The only recognized C{__all__} assignment via list/tuple concatenation is in the
478 following format:
480 __all__ = ['a'] + ['b'] + ['c']
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 """
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 = []
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)
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)
520class Scope(dict):
521 importStarred = False # set to True when import * is found
523 def __repr__(self):
524 scope_cls = self.__class__.__name__
525 return f'<{scope_cls} at 0x{id(self):x} {dict.__repr__(self)}>'
528class ClassScope(Scope):
529 def __init__(self):
530 super().__init__()
531 # {name: node}
532 self.indirect_assignments = {}
535class FunctionScope(Scope):
536 """
537 I represent a name scope for a function.
539 @ivar globals: Names declared 'global' in this function.
540 """
541 usesLocals = False
542 alwaysUsed = {'__tracebackhide__', '__traceback_info__',
543 '__traceback_supplement__', '__debuggerskip__'}
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 = {}
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
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
573class TypeScope(Scope):
574 pass
577class GeneratorScope(Scope):
578 pass
581class ModuleScope(Scope):
582 """Scope for a module."""
583 _futures_allowed = True
584 _annotations_future_enabled = False
587class DoctestScope(ModuleScope):
588 """Scope for a doctest."""
591class DetectClassScopedMagic:
592 names = dir()
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']
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
610TYPING_MODULES = frozenset(('typing', 'typing_extensions'))
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.
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 """
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 )
632 return False
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 )
642 return False
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 )
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`.
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)
668def _is_any_typing_member(node, scope_stack):
669 """
670 Determine whether `node` represents any member of a typing module.
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)
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 )
688class AnnotationState:
689 NONE = 0
690 STRING = 1
691 BARE = 2
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
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
710class Checker:
711 """I check the cleanliness and sanity of Python code."""
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 }
725 nodeDepth = 0
726 offset = None
727 _in_annotation = AnnotationState.NONE
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
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
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)
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()
760 self.checkDeadScopes()
762 if file_tokens:
763 warnings.warn(
764 '`file_tokens` will be removed in a future version',
765 stacklevel=2,
766 )
768 def deferFunction(self, callable):
769 """
770 Schedule a function handler to be called just before completion.
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))
779 def _run_deferred(self):
780 orig = (self.scopeStack, self.offset)
782 while self._deferred:
783 handler, scope, offset = self._deferred.popleft()
784 self.scopeStack, self.offset = scope, offset
785 handler()
787 self.scopeStack, self.offset = orig
789 def _in_doctest(self):
790 return (len(self.scopeStack) >= 2 and
791 isinstance(self.scopeStack[1], DoctestScope))
793 @property
794 def futuresAllowed(self):
795 if not all(isinstance(scope, ModuleScope)
796 for scope in self.scopeStack):
797 return False
799 return self.scope._futures_allowed
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
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
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
820 @property
821 def scope(self):
822 return self.scopeStack[-1]
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())
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)
842 # imports in classes are public members
843 if isinstance(scope, ClassScope):
844 continue
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)
852 all_binding = scope.get('__all__')
853 if all_binding and not isinstance(all_binding, ExportBinding):
854 all_binding = None
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 = []
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)
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)
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)
902 def report(self, messageClass, *args, **kwargs):
903 self.messages.append(messageClass(self.filename, *args, **kwargs))
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
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
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 )
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
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
949 def getScopeNode(self, node):
950 return self._getAncestor(node, tuple(Checker._ast_node_scope.keys()))
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
963 def addBinding(self, node, value):
964 """
965 Called when a binding is altered.
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)
976 if (existing and not isinstance(existing, Builtin) and
977 not self.differentForks(node, existing.source)):
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)
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)
993 if isinstance(scope, (ClassScope, FunctionScope)):
994 scope.indirect_assignments.pop(value.name, None)
996 elif isinstance(existing, Importation) and value.redefines(existing):
997 existing.redefined.append(node)
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
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
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)
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
1048 def handleNodeLoad(self, node, parent):
1049 name = getNodeName(node)
1050 if not name:
1051 return
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
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
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
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)
1082 try:
1083 scope[name].used = (self.scope, node)
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
1099 importStarred = importStarred or scope.importStarred
1101 if can_access_class_vars is not False:
1102 can_access_class_vars = isinstance(
1103 scope, (TypeScope, GeneratorScope),
1104 )
1106 if importStarred:
1107 from_list = []
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)
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
1121 if name == '__path__' and os.path.basename(self.filename) == '__init__.py':
1122 # the special name __path__ is valid only in packages
1123 return
1125 if name in DetectClassScopedMagic.names and isinstance(self.scope, ClassScope):
1126 return
1128 # protected with a NameError handler?
1129 if 'NameError' not in self.exceptHandlers[-1]:
1130 self.report(messages.UndefinedName, node, name)
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
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)
1174 def handleNodeDelete(self, node):
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
1187 name = getNodeName(node)
1188 if not name:
1189 return
1191 if on_conditional_branch():
1192 # We cannot predict if this conditional branch is going to
1193 # be executed.
1194 return
1196 if isinstance(self.scope, (ClassScope, FunctionScope)):
1197 self.scope.indirect_assignments.pop(name, None)
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)
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
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 )
1225 def handleChildren(self, tree, omit=None):
1226 for node in iter_child_nodes(tree, omit=omit):
1227 self.handleNode(node, tree)
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
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 )
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
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
1279 _getDoctestExamples = doctest.DocTestParser().get_examples
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
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
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
1321 body = tree.body
1322 if len(body) != 1 or not isinstance(body[0], ast.Expr):
1323 self.report(err, node, s)
1324 return
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
1335 self.handleNode(parsed_annotation, node)
1337 def handle_annotation_always_deferred(self, annotation, parent):
1338 fn = in_annotation(Checker.handleNode)
1339 self.deferFunction(lambda: fn(self, annotation, parent))
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)
1361 def ignore(self, node):
1362 pass
1364 # "stmt" type nodes
1365 DELETE = FOR = ASYNCFOR = WHILE = WITH = WITHITEM = ASYNCWITH = \
1366 EXPR = ASSIGN = handleChildren
1368 PASS = ignore
1370 # "expr" type nodes
1371 BOOLOP = UNARYOP = SET = ATTRIBUTE = STARRED = NAMECONSTANT = \
1372 NAMEDEXPR = handleChildren
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)
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
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)
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)
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
1419 auto = None
1420 next_auto = 0
1422 placeholder_positional = set()
1423 placeholder_named = set()
1425 def _add_key(fmtkey):
1426 """Returns True if there is an error which should early-exit"""
1427 nonlocal auto, next_auto
1429 if fmtkey is None: # end of string or `{` / `}` escapes
1430 return False
1432 # attributes / indices are allowed in `.format(...)`
1433 fmtkey, _, _ = fmtkey.partition('.')
1434 fmtkey, _, _ = fmtkey.partition('[')
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
1447 if fmtkey == '':
1448 if auto is False:
1449 self.report(messages.StringDotFormatMixingAutomatic, node)
1450 return True
1451 else:
1452 auto = True
1454 fmtkey = next_auto
1455 next_auto += 1
1457 if isinstance(fmtkey, int):
1458 placeholder_positional.add(fmtkey)
1459 else:
1460 placeholder_named.add(fmtkey)
1462 return False
1464 for _, fmtkey, spec, _ in placeholders:
1465 if _add_key(fmtkey):
1466 return
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
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
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
1497 substitution_positional = set(range(len(node.args)))
1498 substitution_named = {kwd.arg for kwd in node.keywords}
1500 extra_positional = substitution_positional - placeholder_positional
1501 extra_named = substitution_named - placeholder_named
1503 missing_arguments = (
1504 (placeholder_positional | placeholder_named) -
1505 (substitution_positional | substitution_named)
1506 )
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 )
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)
1536 omit = []
1537 annotated = []
1538 not_annotated = []
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)
1547 elif _is_typing(node.func, 'TypeVar', self.scopeStack):
1549 # TypeVar("T", "int", "str")
1550 omit += ["args"]
1551 annotated += [arg for arg in node.args[1:]]
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 ]
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 ]
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]
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]
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]
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)
1604 with self._enter_annotation():
1605 for annotated_node in annotated:
1606 self.handleNode(annotated_node, node)
1607 else:
1608 self.handleChildren(node)
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
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
1629 if conversion == '%':
1630 continue
1632 if conversion not in VALID_CONVERSIONS:
1633 self.report(
1634 messages.PercentFormatUnsupportedFormatCharacter,
1635 node,
1636 conversion,
1637 )
1639 if positional is None and conversion:
1640 positional = name is None
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
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
1665 if positional:
1666 positional_count += 1
1667 else:
1668 named.add(name)
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)
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
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 )
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)
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)
1737 # "slice" type nodes
1738 SLICE = EXTSLICE = INDEX = handleChildren
1740 # expression contexts are node instances too, though being constants
1741 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
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
1749 def RAISE(self, node):
1750 self.handleChildren(node)
1752 arg = node.exc
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)
1762 # additional node types
1763 COMPREHENSION = KEYWORD = FORMATTEDVALUE = handleChildren
1765 _in_fstring = False
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)
1776 self._in_fstring, orig = True, self._in_fstring
1777 try:
1778 self.handleChildren(node)
1779 finally:
1780 self._in_fstring = orig
1782 def TEMPLATESTR(self, node):
1783 if not any(isinstance(x, ast.Interpolation) for x in node.values):
1784 self.report(messages.TStringMissingPlaceholders, node)
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
1794 INTERPOLATION = handleChildren
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 ]
1804 key_counts = collections.Counter(keys)
1805 duplicate_keys = [
1806 key for key, count in key_counts.items()
1807 if count > 1
1808 ]
1810 for key in duplicate_keys:
1811 key_indices = [i for i, i_key in enumerate(keys) if i_key == key]
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)
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)
1837 IFEXP = IF
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)
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]
1851 # Ignore 'global' statement in global scope.
1852 if self.scope is not global_scope:
1854 # One 'global' statement can bind multiple (comma-delimited) names.
1855 for node_name in node.names:
1856 node_value = Assignment(node_name, node)
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]
1866 # Bind name to global scope if it doesn't exist already.
1867 global_scope.setdefault(node_name, node_value)
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
1874 self.scope.indirect_assignments[node_name] = node
1876 NONLOCAL = GLOBAL
1878 def GENERATOREXP(self, node):
1879 with self.in_scope(GeneratorScope):
1880 self.handleChildren(node)
1882 LISTCOMP = DICTCOMP = SETCOMP = GENERATOREXP
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}")
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)
1921 BREAK = CONTINUE
1923 def RETURN(self, node):
1924 if isinstance(self.scope, (ClassScope, ModuleScope)):
1925 self.report(messages.ReturnOutsideFunction, node)
1926 return
1928 self.handleNode(node.value, node)
1930 def YIELD(self, node):
1931 if isinstance(self.scope, (ClassScope, ModuleScope)):
1932 self.report(messages.YieldOutsideFunction, node)
1933 return
1935 self.handleNode(node.value, node)
1937 AWAIT = YIELDFROM = YIELD
1939 def FUNCTIONDEF(self, node):
1940 for deco in node.decorator_list:
1941 self.handleNode(deco, node)
1943 with self._type_param_scope(node):
1944 self.LAMBDA(node)
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))
1954 ASYNCFUNCTIONDEF = FUNCTIONDEF
1956 def LAMBDA(self, node):
1957 args = []
1958 annotations = []
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
1968 has_annotations = not isinstance(node, ast.Lambda)
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)
1978 if has_annotations:
1979 annotations.append(node.returns)
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)
1986 for annotation in annotations:
1987 self.handleAnnotation(annotation, node)
1989 for default in defaults:
1990 self.handleNode(default, node)
1992 def runFunction():
1993 with self.in_scope(FunctionScope):
1994 self.handleChildren(
1995 node,
1996 omit=('decorator_list', 'returns', 'type_params'),
1997 )
1999 self.deferFunction(runFunction)
2001 def ARGUMENTS(self, node):
2002 self.handleChildren(node, omit=('defaults', 'kw_defaults'))
2004 def ARG(self, node):
2005 self.addBinding(node, Argument(node.arg, self.getScopeNode(node)))
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)
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)
2031 self.addBinding(node, ClassDefinition(node.name, node))
2033 def AUGASSIGN(self, node):
2034 self.handleNodeLoad(node.target, node)
2035 self.handleNode(node.value, node)
2036 self.handleNode(node.target, node)
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)
2061 LIST = TUPLE
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)
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
2079 module = ('.' * node.level) + (node.module or '')
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
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)
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))
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')
2124 TRYSTAR = TRY
2126 def EXCEPTHANDLER(self, node):
2127 if node.name is None:
2128 self.handleChildren(node)
2129 return
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)
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.
2142 try:
2143 prev_definition = self.scope.pop(node.name)
2144 except KeyError:
2145 prev_definition = None
2147 self.handleNodeStore(node)
2148 self.handleChildren(node)
2150 # See discussion on https://github.com/PyCQA/pyflakes/pull/59
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.
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)
2167 # Restore.
2168 if prev_definition:
2169 self.scope[node.name] = prev_definition
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)
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
2194 self.handleChildren(node)
2196 MATCH = MATCH_CASE = MATCHCLASS = MATCHOR = MATCHSEQUENCE = handleChildren
2197 MATCHSINGLETON = MATCHVALUE = handleChildren
2199 def _match_target(self, node):
2200 self.handleNodeStore(node)
2201 self.handleChildren(node)
2203 MATCHAS = MATCHMAPPING = MATCHSTAR = _match_target
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
2214 def TYPEVAR(self, node):
2215 self.handleNodeStore(node)
2216 self.handle_annotation_always_deferred(node.bound, node)
2218 PARAMSPEC = TYPEVARTUPLE = handleNodeStore
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)