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