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