Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pyflakes/checker.py: 90%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1178 statements  

1""" 

2Main module. 

3 

4Implement the central Checker class. 

5Also, it models the Bindings and Scopes. 

6""" 

7import __future__ 

8import builtins 

9import ast 

10import collections 

11import contextlib 

12import doctest 

13import functools 

14import os 

15import re 

16import string 

17import sys 

18import warnings 

19 

20from pyflakes import messages 

21 

22PYPY = hasattr(sys, 'pypy_version_info') 

23 

24builtin_vars = dir(builtins) 

25 

26parse_format_string = string.Formatter().parse 

27 

28 

29def getAlternatives(n): 

30 if isinstance(n, ast.If): 

31 return [n.body] 

32 elif isinstance(n, ast.Try): 

33 return [n.body + n.orelse] + [[hdl] for hdl in n.handlers] 

34 elif sys.version_info >= (3, 10) and isinstance(n, ast.Match): 

35 return [mc.body for mc in n.cases] 

36 

37 

38FOR_TYPES = (ast.For, ast.AsyncFor) 

39 

40 

41def _is_singleton(node): # type: (ast.AST) -> bool 

42 return ( 

43 isinstance(node, ast.Constant) and 

44 isinstance(node.value, (bool, type(Ellipsis), type(None))) 

45 ) 

46 

47 

48def _is_tuple_constant(node): # type: (ast.AST) -> bool 

49 return ( 

50 isinstance(node, ast.Tuple) and 

51 all(_is_constant(elt) for elt in node.elts) 

52 ) 

53 

54 

55def _is_constant(node): 

56 return isinstance(node, ast.Constant) or _is_tuple_constant(node) 

57 

58 

59def _is_const_non_singleton(node): # type: (ast.AST) -> bool 

60 return _is_constant(node) and not _is_singleton(node) 

61 

62 

63def _is_name_or_attr(node, name): # type: (ast.AST, str) -> bool 

64 return ( 

65 (isinstance(node, ast.Name) and node.id == name) or 

66 (isinstance(node, ast.Attribute) and node.attr == name) 

67 ) 

68 

69 

70MAPPING_KEY_RE = re.compile(r'\(([^()]*)\)') 

71CONVERSION_FLAG_RE = re.compile('[#0+ -]*') 

72WIDTH_RE = re.compile(r'(?:\*|\d*)') 

73PRECISION_RE = re.compile(r'(?:\.(?:\*|\d*))?') 

74LENGTH_RE = re.compile('[hlL]?') 

75# https://docs.python.org/3/library/stdtypes.html#old-string-formatting 

76VALID_CONVERSIONS = frozenset('diouxXeEfFgGcrsa%') 

77 

78 

79def _must_match(regex, string, pos): 

80 match = regex.match(string, pos) 

81 assert match is not None 

82 return match 

83 

84 

85def parse_percent_format(s): 

86 """Parses the string component of a `'...' % ...` format call 

87 

88 Copied from https://github.com/asottile/pyupgrade at v1.20.1 

89 """ 

90 

91 def _parse_inner(): 

92 string_start = 0 

93 string_end = 0 

94 in_fmt = False 

95 

96 i = 0 

97 while i < len(s): 

98 if not in_fmt: 

99 try: 

100 i = s.index('%', i) 

101 except ValueError: # no more % fields! 

102 yield s[string_start:], None 

103 return 

104 else: 

105 string_end = i 

106 i += 1 

107 in_fmt = True 

108 else: 

109 key_match = MAPPING_KEY_RE.match(s, i) 

110 if key_match: 

111 key = key_match.group(1) 

112 i = key_match.end() 

113 else: 

114 key = None 

115 

116 conversion_flag_match = _must_match(CONVERSION_FLAG_RE, s, i) 

117 conversion_flag = conversion_flag_match.group() or None 

118 i = conversion_flag_match.end() 

119 

120 width_match = _must_match(WIDTH_RE, s, i) 

121 width = width_match.group() or None 

122 i = width_match.end() 

123 

124 precision_match = _must_match(PRECISION_RE, s, i) 

125 precision = precision_match.group() or None 

126 i = precision_match.end() 

127 

128 # length modifier is ignored 

129 i = _must_match(LENGTH_RE, s, i).end() 

130 

131 try: 

132 conversion = s[i] 

133 except IndexError: 

134 raise ValueError('end-of-string while parsing format') 

135 i += 1 

136 

137 fmt = (key, conversion_flag, width, precision, conversion) 

138 yield s[string_start:string_end], fmt 

139 

140 in_fmt = False 

141 string_start = i 

142 

143 if in_fmt: 

144 raise ValueError('end-of-string while parsing format') 

145 

146 return tuple(_parse_inner()) 

147 

148 

149class _FieldsOrder(dict): 

150 """Fix order of AST node fields.""" 

151 

152 def _get_fields(self, node_class): 

153 # handle iter before target, and generators before element 

154 fields = node_class._fields 

155 if 'iter' in fields: 

156 key_first = 'iter'.find 

157 elif 'generators' in fields: 

158 key_first = 'generators'.find 

159 else: 

160 key_first = 'value'.find 

161 return tuple(sorted(fields, key=key_first, reverse=True)) 

162 

163 def __missing__(self, node_class): 

164 self[node_class] = fields = self._get_fields(node_class) 

165 return fields 

166 

167 

168def iter_child_nodes(node, omit=None, _fields_order=_FieldsOrder()): 

169 """ 

170 Yield all direct child nodes of *node*, that is, all fields that 

171 are nodes and all items of fields that are lists of nodes. 

172 

173 :param node: AST node to be iterated upon 

174 :param omit: String or tuple of strings denoting the 

175 attributes of the node to be omitted from 

176 further parsing 

177 :param _fields_order: Order of AST node fields 

178 """ 

179 for name in _fields_order[node.__class__]: 

180 if omit and name in omit: 

181 continue 

182 field = getattr(node, name, None) 

183 if isinstance(field, ast.AST): 

184 yield field 

185 elif isinstance(field, list): 

186 for item in field: 

187 if isinstance(item, ast.AST): 

188 yield item 

189 

190 

191def convert_to_value(item): 

192 if isinstance(item, ast.Constant): 

193 return item.value 

194 elif isinstance(item, ast.Tuple): 

195 return tuple(convert_to_value(i) for i in item.elts) 

196 elif isinstance(item, ast.Name): 

197 return VariableKey(item=item) 

198 else: 

199 return UnhandledKeyType() 

200 

201 

202def is_notimplemented_name_node(node): 

203 return isinstance(node, ast.Name) and getNodeName(node) == 'NotImplemented' 

204 

205 

206class Binding: 

207 """ 

208 Represents the binding of a value to a name. 

209 

210 The checker uses this to keep track of which names have been bound and 

211 which names have not. See L{Assignment} for a special type of binding that 

212 is checked with stricter rules. 

213 

214 @ivar used: pair of (L{Scope}, node) indicating the scope and 

215 the node that this binding was last used. 

216 """ 

217 

218 def __init__(self, name, source): 

219 self.name = name 

220 self.source = source 

221 self.used = False 

222 

223 def __str__(self): 

224 return self.name 

225 

226 def __repr__(self): 

227 return '<{} object {!r} from line {!r} at 0x{:x}>'.format( 

228 self.__class__.__name__, 

229 self.name, 

230 self.source.lineno, 

231 id(self), 

232 ) 

233 

234 def redefines(self, other): 

235 return isinstance(other, Definition) and self.name == other.name 

236 

237 

238class Definition(Binding): 

239 """ 

240 A binding that defines a function or a class. 

241 """ 

242 def redefines(self, other): 

243 return ( 

244 super().redefines(other) or 

245 (isinstance(other, Assignment) and self.name == other.name) 

246 ) 

247 

248 

249class Builtin(Definition): 

250 """A definition created for all Python builtins.""" 

251 

252 def __init__(self, name): 

253 super().__init__(name, None) 

254 

255 def __repr__(self): 

256 return '<{} object {!r} at 0x{:x}>'.format( 

257 self.__class__.__name__, 

258 self.name, 

259 id(self) 

260 ) 

261 

262 

263class UnhandledKeyType: 

264 """ 

265 A dictionary key of a type that we cannot or do not check for duplicates. 

266 """ 

267 

268 

269class VariableKey: 

270 """ 

271 A dictionary key which is a variable. 

272 

273 @ivar item: The variable AST object. 

274 """ 

275 def __init__(self, item): 

276 self.name = item.id 

277 

278 def __eq__(self, compare): 

279 return ( 

280 compare.__class__ == self.__class__ and 

281 compare.name == self.name 

282 ) 

283 

284 def __hash__(self): 

285 return hash(self.name) 

286 

287 

288class Importation(Definition): 

289 """ 

290 A binding created by an import statement. 

291 

292 @ivar fullName: The complete name given to the import statement, 

293 possibly including multiple dotted components. 

294 @type fullName: C{str} 

295 """ 

296 

297 def __init__(self, name, source, full_name=None): 

298 self.fullName = full_name or name 

299 self.redefined = [] 

300 super().__init__(name, source) 

301 

302 def redefines(self, other): 

303 if isinstance(other, SubmoduleImportation): 

304 # See note in SubmoduleImportation about RedefinedWhileUnused 

305 return self.fullName == other.fullName 

306 return isinstance(other, Definition) and self.name == other.name 

307 

308 def _has_alias(self): 

309 """Return whether importation needs an as clause.""" 

310 return not self.fullName.split('.')[-1] == self.name 

311 

312 @property 

313 def source_statement(self): 

314 """Generate a source statement equivalent to the import.""" 

315 if self._has_alias(): 

316 return f'import {self.fullName} as {self.name}' 

317 else: 

318 return 'import %s' % self.fullName 

319 

320 def __str__(self): 

321 """Return import full name with alias.""" 

322 if self._has_alias(): 

323 return self.fullName + ' as ' + self.name 

324 else: 

325 return self.fullName 

326 

327 

328class SubmoduleImportation(Importation): 

329 """ 

330 A binding created by a submodule import statement. 

331 

332 A submodule import is a special case where the root module is implicitly 

333 imported, without an 'as' clause, and the submodule is also imported. 

334 Python does not restrict which attributes of the root module may be used. 

335 

336 This class is only used when the submodule import is without an 'as' clause. 

337 

338 pyflakes handles this case by registering the root module name in the scope, 

339 allowing any attribute of the root module to be accessed. 

340 

341 RedefinedWhileUnused is suppressed in `redefines` unless the submodule 

342 name is also the same, to avoid false positives. 

343 """ 

344 

345 def __init__(self, name, source): 

346 # A dot should only appear in the name when it is a submodule import 

347 assert '.' in name and (not source or isinstance(source, ast.Import)) 

348 package_name = name.split('.')[0] 

349 super().__init__(package_name, source) 

350 self.fullName = name 

351 

352 def redefines(self, other): 

353 if isinstance(other, Importation): 

354 return self.fullName == other.fullName 

355 return super().redefines(other) 

356 

357 def __str__(self): 

358 return self.fullName 

359 

360 @property 

361 def source_statement(self): 

362 return 'import ' + self.fullName 

363 

364 

365class ImportationFrom(Importation): 

366 

367 def __init__(self, name, source, module, real_name=None): 

368 self.module = module 

369 self.real_name = real_name or name 

370 

371 if module.endswith('.'): 

372 full_name = module + self.real_name 

373 else: 

374 full_name = module + '.' + self.real_name 

375 

376 super().__init__(name, source, full_name) 

377 

378 def __str__(self): 

379 """Return import full name with alias.""" 

380 if self.real_name != self.name: 

381 return self.fullName + ' as ' + self.name 

382 else: 

383 return self.fullName 

384 

385 @property 

386 def source_statement(self): 

387 if self.real_name != self.name: 

388 return f'from {self.module} import {self.real_name} as {self.name}' 

389 else: 

390 return f'from {self.module} import {self.name}' 

391 

392 

393class StarImportation(Importation): 

394 """A binding created by a 'from x import *' statement.""" 

395 

396 def __init__(self, name, source): 

397 super().__init__('*', source) 

398 # Each star importation needs a unique name, and 

399 # may not be the module name otherwise it will be deemed imported 

400 self.name = name + '.*' 

401 self.fullName = name 

402 

403 @property 

404 def source_statement(self): 

405 return 'from ' + self.fullName + ' import *' 

406 

407 def __str__(self): 

408 # When the module ends with a ., avoid the ambiguous '..*' 

409 if self.fullName.endswith('.'): 

410 return self.source_statement 

411 else: 

412 return self.name 

413 

414 

415class FutureImportation(ImportationFrom): 

416 """ 

417 A binding created by a from `__future__` import statement. 

418 

419 `__future__` imports are implicitly used. 

420 """ 

421 

422 def __init__(self, name, source, scope): 

423 super().__init__(name, source, '__future__') 

424 self.used = (scope, source) 

425 

426 

427class Argument(Binding): 

428 """ 

429 Represents binding a name as an argument. 

430 """ 

431 

432 

433class Assignment(Binding): 

434 """ 

435 Represents binding a name with an explicit assignment. 

436 

437 The checker will raise warnings for any Assignment that isn't used. Also, 

438 the checker does not consider assignments in tuple/list unpacking to be 

439 Assignments, rather it treats them as simple Bindings. 

440 """ 

441 

442 

443class NamedExprAssignment(Assignment): 

444 """ 

445 Represents binding a name with an assignment expression. 

446 """ 

447 

448 

449class Annotation(Binding): 

450 """ 

451 Represents binding a name to a type without an associated value. 

452 

453 As long as this name is not assigned a value in another binding, it is considered 

454 undefined for most purposes. One notable exception is using the name as a type 

455 annotation. 

456 """ 

457 

458 def redefines(self, other): 

459 """An Annotation doesn't define any name, so it cannot redefine one.""" 

460 return False 

461 

462 

463class FunctionDefinition(Definition): 

464 pass 

465 

466 

467class ClassDefinition(Definition): 

468 pass 

469 

470 

471class ExportBinding(Binding): 

472 """ 

473 A binding created by an C{__all__} assignment. If the names in the list 

474 can be determined statically, they will be treated as names for export and 

475 additional checking applied to them. 

476 

477 The only recognized C{__all__} assignment via list/tuple concatenation is in the 

478 following format: 

479 

480 __all__ = ['a'] + ['b'] + ['c'] 

481 

482 Names which are imported and not otherwise used but appear in the value of 

483 C{__all__} will not have an unused import warning reported for them. 

484 """ 

485 

486 def __init__(self, name, source, scope): 

487 if '__all__' in scope and isinstance(source, ast.AugAssign): 

488 self.names = list(scope['__all__'].names) 

489 else: 

490 self.names = [] 

491 

492 def _add_to_names(container): 

493 for node in container.elts: 

494 if isinstance(node, ast.Constant) and isinstance(node.value, str): 

495 self.names.append(node.value) 

496 

497 if isinstance(source.value, (ast.List, ast.Tuple)): 

498 _add_to_names(source.value) 

499 # If concatenating lists or tuples 

500 elif isinstance(source.value, ast.BinOp): 

501 currentValue = source.value 

502 while isinstance(currentValue.right, (ast.List, ast.Tuple)): 

503 left = currentValue.left 

504 right = currentValue.right 

505 _add_to_names(right) 

506 # If more lists are being added 

507 if isinstance(left, ast.BinOp): 

508 currentValue = left 

509 # If just two lists are being added 

510 elif isinstance(left, (ast.List, ast.Tuple)): 

511 _add_to_names(left) 

512 # All lists accounted for - done 

513 break 

514 # If not list concatenation 

515 else: 

516 break 

517 super().__init__(name, source) 

518 

519 

520class Scope(dict): 

521 importStarred = False # set to True when import * is found 

522 

523 def __repr__(self): 

524 scope_cls = self.__class__.__name__ 

525 return f'<{scope_cls} at 0x{id(self):x} {dict.__repr__(self)}>' 

526 

527 

528class ClassScope(Scope): 

529 def __init__(self): 

530 super().__init__() 

531 # {name: node} 

532 self.indirect_assignments = {} 

533 

534 

535class FunctionScope(Scope): 

536 """ 

537 I represent a name scope for a function. 

538 

539 @ivar globals: Names declared 'global' in this function. 

540 """ 

541 usesLocals = False 

542 alwaysUsed = {'__tracebackhide__', '__traceback_info__', 

543 '__traceback_supplement__', '__debuggerskip__'} 

544 

545 def __init__(self): 

546 super().__init__() 

547 # Simplify: manage the special locals as globals 

548 self.globals = self.alwaysUsed.copy() 

549 # {name: node} 

550 self.indirect_assignments = {} 

551 

552 def unused_assignments(self): 

553 """ 

554 Return a generator for the assignments which have not been used. 

555 """ 

556 for name, binding in self.items(): 

557 if (not binding.used and 

558 name != '_' and # see issue #202 

559 name not in self.globals and 

560 not self.usesLocals and 

561 isinstance(binding, Assignment)): 

562 yield name, binding 

563 

564 def unused_annotations(self): 

565 """ 

566 Return a generator for the annotations which have not been used. 

567 """ 

568 for name, binding in self.items(): 

569 if not binding.used and isinstance(binding, Annotation): 

570 yield name, binding 

571 

572 

573class TypeScope(Scope): 

574 pass 

575 

576 

577class GeneratorScope(Scope): 

578 pass 

579 

580 

581class ModuleScope(Scope): 

582 """Scope for a module.""" 

583 _futures_allowed = True 

584 _annotations_future_enabled = False 

585 

586 

587class DoctestScope(ModuleScope): 

588 """Scope for a doctest.""" 

589 

590 

591class DetectClassScopedMagic: 

592 names = dir() 

593 

594 

595# Globally defined names which are not attributes of the builtins module, or 

596# are only present on some platforms. 

597_MAGIC_GLOBALS = ['__file__', '__builtins__', '__annotations__', 'WindowsError'] 

598 

599 

600def getNodeName(node): 

601 # Returns node.id, or node.name, or None 

602 if hasattr(node, 'id'): # One of the many nodes with an id 

603 return node.id 

604 if hasattr(node, 'name'): # an ExceptHandler node 

605 return node.name 

606 if hasattr(node, 'rest'): # a MatchMapping node 

607 return node.rest 

608 

609 

610TYPING_MODULES = frozenset(('typing', 'typing_extensions')) 

611 

612 

613def _is_typing_helper(node, is_name_match_fn, scope_stack): 

614 """ 

615 Internal helper to determine whether or not something is a member of a 

616 typing module. This is used as part of working out whether we are within a 

617 type annotation context. 

618 

619 Note: you probably don't want to use this function directly. Instead see the 

620 utils below which wrap it (`_is_typing` and `_is_any_typing_member`). 

621 """ 

622 

623 def _bare_name_is_attr(name): 

624 for scope in reversed(scope_stack): 

625 if name in scope: 

626 return ( 

627 isinstance(scope[name], ImportationFrom) and 

628 scope[name].module in TYPING_MODULES and 

629 is_name_match_fn(scope[name].real_name) 

630 ) 

631 

632 return False 

633 

634 def _module_scope_is_typing(name): 

635 for scope in reversed(scope_stack): 

636 if name in scope: 

637 return ( 

638 isinstance(scope[name], Importation) and 

639 scope[name].fullName in TYPING_MODULES 

640 ) 

641 

642 return False 

643 

644 return ( 

645 ( 

646 isinstance(node, ast.Name) and 

647 _bare_name_is_attr(node.id) 

648 ) or ( 

649 isinstance(node, ast.Attribute) and 

650 isinstance(node.value, ast.Name) and 

651 _module_scope_is_typing(node.value.id) and 

652 is_name_match_fn(node.attr) 

653 ) 

654 ) 

655 

656 

657def _is_typing(node, typing_attr, scope_stack): 

658 """ 

659 Determine whether `node` represents the member of a typing module specified 

660 by `typing_attr`. 

661 

662 This is used as part of working out whether we are within a type annotation 

663 context. 

664 """ 

665 return _is_typing_helper(node, lambda x: x == typing_attr, scope_stack) 

666 

667 

668def _is_any_typing_member(node, scope_stack): 

669 """ 

670 Determine whether `node` represents any member of a typing module. 

671 

672 This is used as part of working out whether we are within a type annotation 

673 context. 

674 """ 

675 return _is_typing_helper(node, lambda x: True, scope_stack) 

676 

677 

678def is_typing_overload(value, scope_stack): 

679 return ( 

680 isinstance(value.source, (ast.FunctionDef, ast.AsyncFunctionDef)) and 

681 any( 

682 _is_typing(dec, 'overload', scope_stack) 

683 for dec in value.source.decorator_list 

684 ) 

685 ) 

686 

687 

688class AnnotationState: 

689 NONE = 0 

690 STRING = 1 

691 BARE = 2 

692 

693 

694def in_annotation(func): 

695 @functools.wraps(func) 

696 def in_annotation_func(self, *args, **kwargs): 

697 with self._enter_annotation(): 

698 return func(self, *args, **kwargs) 

699 return in_annotation_func 

700 

701 

702def in_string_annotation(func): 

703 @functools.wraps(func) 

704 def in_annotation_func(self, *args, **kwargs): 

705 with self._enter_annotation(AnnotationState.STRING): 

706 return func(self, *args, **kwargs) 

707 return in_annotation_func 

708 

709 

710class Checker: 

711 """I check the cleanliness and sanity of Python code.""" 

712 

713 _ast_node_scope = { 

714 ast.Module: ModuleScope, 

715 ast.ClassDef: ClassScope, 

716 ast.FunctionDef: FunctionScope, 

717 ast.AsyncFunctionDef: FunctionScope, 

718 ast.Lambda: FunctionScope, 

719 ast.ListComp: GeneratorScope, 

720 ast.SetComp: GeneratorScope, 

721 ast.GeneratorExp: GeneratorScope, 

722 ast.DictComp: GeneratorScope, 

723 } 

724 

725 nodeDepth = 0 

726 offset = None 

727 _in_annotation = AnnotationState.NONE 

728 

729 builtIns = set(builtin_vars).union(_MAGIC_GLOBALS) 

730 _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS') 

731 if _customBuiltIns: 

732 builtIns.update(_customBuiltIns.split(',')) 

733 del _customBuiltIns 

734 

735 def __init__(self, tree, filename='(none)', builtins=None, 

736 withDoctest='PYFLAKES_DOCTEST' in os.environ, file_tokens=()): 

737 self._nodeHandlers = {} 

738 self._deferred = collections.deque() 

739 self.deadScopes = [] 

740 self.messages = [] 

741 self.filename = filename 

742 if builtins: 

743 self.builtIns = self.builtIns.union(builtins) 

744 self.withDoctest = withDoctest 

745 self.exceptHandlers = [()] 

746 self.root = tree 

747 

748 self.scopeStack = [] 

749 try: 

750 scope_tp = Checker._ast_node_scope[type(tree)] 

751 except KeyError: 

752 raise RuntimeError('No scope implemented for the node %r' % tree) 

753 

754 with self.in_scope(scope_tp): 

755 for builtin in self.builtIns: 

756 self.addBinding(None, Builtin(builtin)) 

757 self.handleChildren(tree) 

758 self._run_deferred() 

759 

760 self.checkDeadScopes() 

761 

762 if file_tokens: 

763 warnings.warn( 

764 '`file_tokens` will be removed in a future version', 

765 stacklevel=2, 

766 ) 

767 

768 def deferFunction(self, callable): 

769 """ 

770 Schedule a function handler to be called just before completion. 

771 

772 This is used for handling function bodies, which must be deferred 

773 because code later in the file might modify the global scope. When 

774 `callable` is called, the scope at the time this is called will be 

775 restored, however it will contain any new bindings added to it. 

776 """ 

777 self._deferred.append((callable, self.scopeStack[:], self.offset)) 

778 

779 def _run_deferred(self): 

780 orig = (self.scopeStack, self.offset) 

781 

782 while self._deferred: 

783 handler, scope, offset = self._deferred.popleft() 

784 self.scopeStack, self.offset = scope, offset 

785 handler() 

786 

787 self.scopeStack, self.offset = orig 

788 

789 def _in_doctest(self): 

790 return (len(self.scopeStack) >= 2 and 

791 isinstance(self.scopeStack[1], DoctestScope)) 

792 

793 @property 

794 def futuresAllowed(self): 

795 if not all(isinstance(scope, ModuleScope) 

796 for scope in self.scopeStack): 

797 return False 

798 

799 return self.scope._futures_allowed 

800 

801 @futuresAllowed.setter 

802 def futuresAllowed(self, value): 

803 assert value is False 

804 if isinstance(self.scope, ModuleScope): 

805 self.scope._futures_allowed = False 

806 

807 @property 

808 def annotationsFutureEnabled(self): 

809 scope = self.scopeStack[0] 

810 if not isinstance(scope, ModuleScope): 

811 return False 

812 return scope._annotations_future_enabled 

813 

814 @annotationsFutureEnabled.setter 

815 def annotationsFutureEnabled(self, value): 

816 assert value is True 

817 assert isinstance(self.scope, ModuleScope) 

818 self.scope._annotations_future_enabled = True 

819 

820 @property 

821 def scope(self): 

822 return self.scopeStack[-1] 

823 

824 @contextlib.contextmanager 

825 def in_scope(self, cls): 

826 self.scopeStack.append(cls()) 

827 try: 

828 yield 

829 finally: 

830 self.deadScopes.append(self.scopeStack.pop()) 

831 

832 def checkDeadScopes(self): 

833 """ 

834 Look at scopes which have been fully examined and report names in them 

835 which were imported but unused. 

836 """ 

837 for scope in self.deadScopes: 

838 if isinstance(scope, (ClassScope, FunctionScope)): 

839 for name, node in scope.indirect_assignments.items(): 

840 self.report(messages.UnusedIndirectAssignment, node, name) 

841 

842 # imports in classes are public members 

843 if isinstance(scope, ClassScope): 

844 continue 

845 

846 if isinstance(scope, FunctionScope): 

847 for name, binding in scope.unused_assignments(): 

848 self.report(messages.UnusedVariable, binding.source, name) 

849 for name, binding in scope.unused_annotations(): 

850 self.report(messages.UnusedAnnotation, binding.source, name) 

851 

852 all_binding = scope.get('__all__') 

853 if all_binding and not isinstance(all_binding, ExportBinding): 

854 all_binding = None 

855 

856 if all_binding: 

857 all_names = set(all_binding.names) 

858 undefined = [ 

859 name for name in all_binding.names 

860 if name not in scope 

861 ] 

862 else: 

863 all_names = undefined = [] 

864 

865 if undefined: 

866 if not scope.importStarred and \ 

867 os.path.basename(self.filename) != '__init__.py': 

868 # Look for possible mistakes in the export list 

869 for name in undefined: 

870 self.report(messages.UndefinedExport, 

871 scope['__all__'].source, name) 

872 

873 # mark all import '*' as used by the undefined in __all__ 

874 if scope.importStarred: 

875 from_list = [] 

876 for binding in scope.values(): 

877 if isinstance(binding, StarImportation): 

878 binding.used = all_binding 

879 from_list.append(binding.fullName) 

880 # report * usage, with a list of possible sources 

881 from_list = ', '.join(sorted(from_list)) 

882 for name in undefined: 

883 self.report(messages.ImportStarUsage, 

884 scope['__all__'].source, name, from_list) 

885 

886 # Look for imported names that aren't used. 

887 for value in scope.values(): 

888 if isinstance(value, Importation): 

889 used = value.used or value.name in all_names 

890 if not used: 

891 messg = messages.UnusedImport 

892 self.report(messg, value.source, str(value)) 

893 for node in value.redefined: 

894 if isinstance(self.getParent(node), FOR_TYPES): 

895 messg = messages.ImportShadowedByLoopVar 

896 elif used: 

897 continue 

898 else: 

899 messg = messages.RedefinedWhileUnused 

900 self.report(messg, node, value.name, value.source) 

901 

902 def report(self, messageClass, *args, **kwargs): 

903 self.messages.append(messageClass(self.filename, *args, **kwargs)) 

904 

905 def getParent(self, node): 

906 # Lookup the first parent which is not Tuple, List or Starred 

907 while True: 

908 node = node._pyflakes_parent 

909 if not hasattr(node, 'elts') and not hasattr(node, 'ctx'): 

910 return node 

911 

912 def getCommonAncestor(self, lnode, rnode, stop): 

913 if ( 

914 stop in (lnode, rnode) or 

915 not ( 

916 hasattr(lnode, '_pyflakes_parent') and 

917 hasattr(rnode, '_pyflakes_parent') 

918 ) 

919 ): 

920 return None 

921 if lnode is rnode: 

922 return lnode 

923 

924 if (lnode._pyflakes_depth > rnode._pyflakes_depth): 

925 return self.getCommonAncestor(lnode._pyflakes_parent, rnode, stop) 

926 if (lnode._pyflakes_depth < rnode._pyflakes_depth): 

927 return self.getCommonAncestor(lnode, rnode._pyflakes_parent, stop) 

928 return self.getCommonAncestor( 

929 lnode._pyflakes_parent, 

930 rnode._pyflakes_parent, 

931 stop, 

932 ) 

933 

934 def descendantOf(self, node, ancestors, stop): 

935 for a in ancestors: 

936 if self.getCommonAncestor(node, a, stop): 

937 return True 

938 return False 

939 

940 def _getAncestor(self, node, ancestor_type): 

941 parent = node 

942 while True: 

943 if parent is self.root: 

944 return None 

945 parent = self.getParent(parent) 

946 if isinstance(parent, ancestor_type): 

947 return parent 

948 

949 def getScopeNode(self, node): 

950 return self._getAncestor(node, tuple(Checker._ast_node_scope.keys())) 

951 

952 def differentForks(self, lnode, rnode): 

953 """True, if lnode and rnode are located on different forks of IF/TRY""" 

954 ancestor = self.getCommonAncestor(lnode, rnode, self.root) 

955 parts = getAlternatives(ancestor) 

956 if parts: 

957 for items in parts: 

958 if self.descendantOf(lnode, items, ancestor) ^ \ 

959 self.descendantOf(rnode, items, ancestor): 

960 return True 

961 return False 

962 

963 def addBinding(self, node, value): 

964 """ 

965 Called when a binding is altered. 

966 

967 - `node` is the statement responsible for the change 

968 - `value` is the new value, a Binding instance 

969 """ 

970 # assert value.source in (node, node._pyflakes_parent): 

971 for scope in self.scopeStack[::-1]: 

972 if value.name in scope: 

973 break 

974 existing = scope.get(value.name) 

975 

976 if (existing and not isinstance(existing, Builtin) and 

977 not self.differentForks(node, existing.source)): 

978 

979 parent_stmt = self.getParent(value.source) 

980 if isinstance(existing, Importation) and isinstance(parent_stmt, FOR_TYPES): 

981 self.report(messages.ImportShadowedByLoopVar, 

982 node, value.name, existing.source) 

983 

984 elif scope is self.scope: 

985 if ( 

986 (not existing.used and value.redefines(existing)) and 

987 (value.name != '_' or isinstance(existing, Importation)) and 

988 not is_typing_overload(existing, self.scopeStack) 

989 ): 

990 self.report(messages.RedefinedWhileUnused, 

991 node, value.name, existing.source) 

992 

993 if isinstance(scope, (ClassScope, FunctionScope)): 

994 scope.indirect_assignments.pop(value.name, None) 

995 

996 elif isinstance(existing, Importation) and value.redefines(existing): 

997 existing.redefined.append(node) 

998 

999 if value.name in self.scope: 

1000 # then assume the rebound name is used as a global or within a loop 

1001 value.used = self.scope[value.name].used 

1002 

1003 # don't treat annotations as assignments if there is an existing value 

1004 # in scope 

1005 if value.name not in self.scope or not isinstance(value, Annotation): 

1006 if isinstance(value, NamedExprAssignment): 

1007 # PEP 572: use scope in which outermost generator is defined 

1008 scope = next( 

1009 scope 

1010 for scope in reversed(self.scopeStack) 

1011 if not isinstance(scope, GeneratorScope) 

1012 ) 

1013 if value.name in scope and isinstance(scope[value.name], Annotation): 

1014 # re-assignment to name that was previously only an annotation 

1015 scope[value.name] = value 

1016 else: 

1017 # it may be a re-assignment to an already existing name 

1018 scope.setdefault(value.name, value) 

1019 else: 

1020 self.scope[value.name] = value 

1021 

1022 def _unknown_handler(self, node): 

1023 # this environment variable configures whether to error on unknown 

1024 # ast types. 

1025 # 

1026 # this is silent by default but the error is enabled for the pyflakes 

1027 # testsuite. 

1028 # 

1029 # this allows new syntax to be added to python without *requiring* 

1030 # changes from the pyflakes side. but will still produce an error 

1031 # in the pyflakes testsuite (so more specific handling can be added if 

1032 # needed). 

1033 if os.environ.get('PYFLAKES_ERROR_UNKNOWN'): 

1034 raise NotImplementedError(f'Unexpected type: {type(node)}') 

1035 else: 

1036 self.handleChildren(node) 

1037 

1038 def getNodeHandler(self, node_class): 

1039 try: 

1040 return self._nodeHandlers[node_class] 

1041 except KeyError: 

1042 nodeType = node_class.__name__.upper() 

1043 self._nodeHandlers[node_class] = handler = getattr( 

1044 self, nodeType, self._unknown_handler, 

1045 ) 

1046 return handler 

1047 

1048 def handleNodeLoad(self, node, parent): 

1049 name = getNodeName(node) 

1050 if not name: 

1051 return 

1052 

1053 # only the following can access class scoped variables (since classes 

1054 # aren't really a scope) 

1055 # - direct accesses (not within a nested scope) 

1056 # - generators 

1057 # - type annotations (for generics, etc.) 

1058 can_access_class_vars = None 

1059 importStarred = None 

1060 

1061 # try enclosing function scopes and global scope 

1062 for scope in self.scopeStack[-1::-1]: 

1063 if isinstance(scope, ClassScope): 

1064 if name == '__class__': 

1065 return 

1066 elif can_access_class_vars is False: 

1067 # only generators used in a class scope can access the 

1068 # names of the class. this is skipped during the first 

1069 # iteration 

1070 continue 

1071 

1072 binding = scope.get(name, None) 

1073 if isinstance(binding, Annotation) and not self._in_postponed_annotation: 

1074 scope[name].used = (self.scope, node) 

1075 continue 

1076 

1077 if name == 'print' and isinstance(binding, Builtin): 

1078 if (isinstance(parent, ast.BinOp) and 

1079 isinstance(parent.op, ast.RShift)): 

1080 self.report(messages.InvalidPrintSyntax, node) 

1081 

1082 try: 

1083 scope[name].used = (self.scope, node) 

1084 

1085 # if the name of SubImportation is same as 

1086 # alias of other Importation and the alias 

1087 # is used, SubImportation also should be marked as used. 

1088 n = scope[name] 

1089 if isinstance(n, Importation) and n._has_alias(): 

1090 try: 

1091 scope[n.fullName].used = (self.scope, node) 

1092 except KeyError: 

1093 pass 

1094 except KeyError: 

1095 pass 

1096 else: 

1097 return 

1098 

1099 importStarred = importStarred or scope.importStarred 

1100 

1101 if can_access_class_vars is not False: 

1102 can_access_class_vars = isinstance( 

1103 scope, (TypeScope, GeneratorScope), 

1104 ) 

1105 

1106 if importStarred: 

1107 from_list = [] 

1108 

1109 for scope in self.scopeStack[-1::-1]: 

1110 for binding in scope.values(): 

1111 if isinstance(binding, StarImportation): 

1112 # mark '*' imports as used for each scope 

1113 binding.used = (self.scope, node) 

1114 from_list.append(binding.fullName) 

1115 

1116 # report * usage, with a list of possible sources 

1117 from_list = ', '.join(sorted(from_list)) 

1118 self.report(messages.ImportStarUsage, node, name, from_list) 

1119 return 

1120 

1121 if name == '__path__' and os.path.basename(self.filename) == '__init__.py': 

1122 # the special name __path__ is valid only in packages 

1123 return 

1124 

1125 if name in DetectClassScopedMagic.names and isinstance(self.scope, ClassScope): 

1126 return 

1127 

1128 # protected with a NameError handler? 

1129 if 'NameError' not in self.exceptHandlers[-1]: 

1130 self.report(messages.UndefinedName, node, name) 

1131 

1132 def handleNodeStore(self, node): 

1133 name = getNodeName(node) 

1134 if not name: 

1135 return 

1136 # if the name hasn't already been defined in the current scope 

1137 if isinstance(self.scope, FunctionScope) and name not in self.scope: 

1138 # for each function or module scope above us 

1139 for scope in self.scopeStack[:-1]: 

1140 if not isinstance(scope, (FunctionScope, ModuleScope)): 

1141 continue 

1142 # if the name was defined in that scope, and the name has 

1143 # been accessed already in the current scope, and hasn't 

1144 # been declared global 

1145 used = name in scope and scope[name].used 

1146 if used and used[0] is self.scope and name not in self.scope.globals: 

1147 # then it's probably a mistake 

1148 self.report(messages.UndefinedLocal, 

1149 scope[name].used[1], name, scope[name].source) 

1150 break 

1151 

1152 parent_stmt = self.getParent(node) 

1153 if isinstance(parent_stmt, ast.AnnAssign) and parent_stmt.value is None: 

1154 binding = Annotation(name, node) 

1155 elif isinstance(parent_stmt, (FOR_TYPES, ast.comprehension)) or ( 

1156 parent_stmt != node._pyflakes_parent and 

1157 not self.isLiteralTupleUnpacking(parent_stmt)): 

1158 binding = Binding(name, node) 

1159 elif ( 

1160 name == '__all__' and 

1161 isinstance(self.scope, ModuleScope) and 

1162 isinstance( 

1163 node._pyflakes_parent, 

1164 (ast.Assign, ast.AugAssign, ast.AnnAssign) 

1165 ) 

1166 ): 

1167 binding = ExportBinding(name, node._pyflakes_parent, self.scope) 

1168 elif isinstance(parent_stmt, ast.NamedExpr): 

1169 binding = NamedExprAssignment(name, node) 

1170 else: 

1171 binding = Assignment(name, node) 

1172 self.addBinding(node, binding) 

1173 

1174 def handleNodeDelete(self, node): 

1175 

1176 def on_conditional_branch(): 

1177 """ 

1178 Return `True` if node is part of a conditional body. 

1179 """ 

1180 current = getattr(node, '_pyflakes_parent', None) 

1181 while current: 

1182 if isinstance(current, (ast.If, ast.While, ast.IfExp)): 

1183 return True 

1184 current = getattr(current, '_pyflakes_parent', None) 

1185 return False 

1186 

1187 name = getNodeName(node) 

1188 if not name: 

1189 return 

1190 

1191 if on_conditional_branch(): 

1192 # We cannot predict if this conditional branch is going to 

1193 # be executed. 

1194 return 

1195 

1196 if isinstance(self.scope, (ClassScope, FunctionScope)): 

1197 self.scope.indirect_assignments.pop(name, None) 

1198 

1199 if isinstance(self.scope, FunctionScope) and name in self.scope.globals: 

1200 self.scope.globals.remove(name) 

1201 else: 

1202 try: 

1203 del self.scope[name] 

1204 except KeyError: 

1205 self.report(messages.UndefinedName, node, name) 

1206 

1207 @contextlib.contextmanager 

1208 def _enter_annotation(self, ann_type=AnnotationState.BARE): 

1209 orig, self._in_annotation = self._in_annotation, ann_type 

1210 try: 

1211 yield 

1212 finally: 

1213 self._in_annotation = orig 

1214 

1215 @property 

1216 def _in_postponed_annotation(self): 

1217 return ( 

1218 self._in_annotation == AnnotationState.STRING or 

1219 ( 

1220 self._in_annotation == AnnotationState.BARE and 

1221 (self.annotationsFutureEnabled or sys.version_info >= (3, 14)) 

1222 ) 

1223 ) 

1224 

1225 def handleChildren(self, tree, omit=None): 

1226 for node in iter_child_nodes(tree, omit=omit): 

1227 self.handleNode(node, tree) 

1228 

1229 def isLiteralTupleUnpacking(self, node): 

1230 if isinstance(node, ast.Assign): 

1231 for child in node.targets + [node.value]: 

1232 if not hasattr(child, 'elts'): 

1233 return False 

1234 return True 

1235 

1236 def isDocstring(self, node): 

1237 """ 

1238 Determine if the given node is a docstring, as long as it is at the 

1239 correct place in the node tree. 

1240 """ 

1241 return ( 

1242 isinstance(node, ast.Expr) and 

1243 isinstance(node.value, ast.Constant) and 

1244 isinstance(node.value.value, str) 

1245 ) 

1246 

1247 def getDocstring(self, node): 

1248 if ( 

1249 isinstance(node, ast.Expr) and 

1250 isinstance(node.value, ast.Constant) and 

1251 isinstance(node.value.value, str) 

1252 ): 

1253 return node.value.value, node.lineno - 1 

1254 else: 

1255 return None, None 

1256 

1257 def handleNode(self, node, parent): 

1258 if node is None: 

1259 return 

1260 if self.offset and getattr(node, 'lineno', None) is not None: 

1261 node.lineno += self.offset[0] 

1262 node.col_offset += self.offset[1] 

1263 if ( 

1264 self.futuresAllowed and 

1265 self.nodeDepth == 0 and 

1266 not isinstance(node, ast.ImportFrom) and 

1267 not self.isDocstring(node) 

1268 ): 

1269 self.futuresAllowed = False 

1270 self.nodeDepth += 1 

1271 node._pyflakes_depth = self.nodeDepth 

1272 node._pyflakes_parent = parent 

1273 try: 

1274 handler = self.getNodeHandler(node.__class__) 

1275 handler(node) 

1276 finally: 

1277 self.nodeDepth -= 1 

1278 

1279 _getDoctestExamples = doctest.DocTestParser().get_examples 

1280 

1281 def handleDoctests(self, node): 

1282 try: 

1283 (docstring, node_lineno) = self.getDocstring(node.body[0]) 

1284 examples = docstring and self._getDoctestExamples(docstring) 

1285 except (ValueError, IndexError): 

1286 # e.g. line 6 of the docstring for <string> has inconsistent 

1287 # leading whitespace: ... 

1288 return 

1289 if not examples: 

1290 return 

1291 

1292 # Place doctest in module scope 

1293 saved_stack = self.scopeStack 

1294 self.scopeStack = [self.scopeStack[0]] 

1295 node_offset = self.offset or (0, 0) 

1296 with self.in_scope(DoctestScope): 

1297 if '_' not in self.scopeStack[0]: 

1298 self.addBinding(None, Builtin('_')) 

1299 for example in examples: 

1300 try: 

1301 tree = ast.parse(example.source, "<doctest>") 

1302 except SyntaxError as e: 

1303 position = (node_lineno + example.lineno + e.lineno, 

1304 example.indent + 4 + (e.offset or 0)) 

1305 self.report(messages.DoctestSyntaxError, node, position) 

1306 else: 

1307 self.offset = (node_offset[0] + node_lineno + example.lineno, 

1308 node_offset[1] + example.indent + 4) 

1309 self.handleChildren(tree) 

1310 self.offset = node_offset 

1311 self.scopeStack = saved_stack 

1312 

1313 @in_string_annotation 

1314 def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err): 

1315 try: 

1316 tree = ast.parse(s) 

1317 except SyntaxError: 

1318 self.report(err, node, s) 

1319 return 

1320 

1321 body = tree.body 

1322 if len(body) != 1 or not isinstance(body[0], ast.Expr): 

1323 self.report(err, node, s) 

1324 return 

1325 

1326 parsed_annotation = tree.body[0].value 

1327 for descendant in ast.walk(parsed_annotation): 

1328 if ( 

1329 'lineno' in descendant._attributes and 

1330 'col_offset' in descendant._attributes 

1331 ): 

1332 descendant.lineno = ref_lineno 

1333 descendant.col_offset = ref_col_offset 

1334 

1335 self.handleNode(parsed_annotation, node) 

1336 

1337 def handle_annotation_always_deferred(self, annotation, parent): 

1338 fn = in_annotation(Checker.handleNode) 

1339 self.deferFunction(lambda: fn(self, annotation, parent)) 

1340 

1341 @in_annotation 

1342 def handleAnnotation(self, annotation, node): 

1343 if ( 

1344 isinstance(annotation, ast.Constant) and 

1345 isinstance(annotation.value, str) 

1346 ): 

1347 # Defer handling forward annotation. 

1348 self.deferFunction(functools.partial( 

1349 self.handleStringAnnotation, 

1350 annotation.value, 

1351 node, 

1352 annotation.lineno, 

1353 annotation.col_offset, 

1354 messages.ForwardAnnotationSyntaxError, 

1355 )) 

1356 elif self.annotationsFutureEnabled or sys.version_info >= (3, 14): 

1357 self.handle_annotation_always_deferred(annotation, node) 

1358 else: 

1359 self.handleNode(annotation, node) 

1360 

1361 def ignore(self, node): 

1362 pass 

1363 

1364 # "stmt" type nodes 

1365 DELETE = FOR = ASYNCFOR = WHILE = WITH = WITHITEM = ASYNCWITH = \ 

1366 EXPR = ASSIGN = handleChildren 

1367 

1368 PASS = ignore 

1369 

1370 # "expr" type nodes 

1371 BOOLOP = UNARYOP = SET = ATTRIBUTE = STARRED = NAMECONSTANT = \ 

1372 NAMEDEXPR = handleChildren 

1373 

1374 def SUBSCRIPT(self, node): 

1375 if _is_name_or_attr(node.value, 'Literal'): 

1376 with self._enter_annotation(AnnotationState.NONE): 

1377 self.handleChildren(node) 

1378 elif _is_name_or_attr(node.value, 'Annotated'): 

1379 self.handleNode(node.value, node) 

1380 

1381 # py39+ 

1382 if isinstance(node.slice, ast.Tuple): 

1383 slice_tuple = node.slice 

1384 # <py39 

1385 elif ( 

1386 isinstance(node.slice, ast.Index) and 

1387 isinstance(node.slice.value, ast.Tuple) 

1388 ): 

1389 slice_tuple = node.slice.value 

1390 else: 

1391 slice_tuple = None 

1392 

1393 # not a multi-arg `Annotated` 

1394 if slice_tuple is None or len(slice_tuple.elts) < 2: 

1395 self.handleNode(node.slice, node) 

1396 else: 

1397 # the first argument is the type 

1398 self.handleNode(slice_tuple.elts[0], node) 

1399 # the rest of the arguments are not 

1400 with self._enter_annotation(AnnotationState.NONE): 

1401 for arg in slice_tuple.elts[1:]: 

1402 self.handleNode(arg, node) 

1403 

1404 self.handleNode(node.ctx, node) 

1405 else: 

1406 if _is_any_typing_member(node.value, self.scopeStack): 

1407 with self._enter_annotation(): 

1408 self.handleChildren(node) 

1409 else: 

1410 self.handleChildren(node) 

1411 

1412 def _handle_string_dot_format(self, node): 

1413 try: 

1414 placeholders = tuple(parse_format_string(node.func.value.value)) 

1415 except ValueError as e: 

1416 self.report(messages.StringDotFormatInvalidFormat, node, e) 

1417 return 

1418 

1419 auto = None 

1420 next_auto = 0 

1421 

1422 placeholder_positional = set() 

1423 placeholder_named = set() 

1424 

1425 def _add_key(fmtkey): 

1426 """Returns True if there is an error which should early-exit""" 

1427 nonlocal auto, next_auto 

1428 

1429 if fmtkey is None: # end of string or `{` / `}` escapes 

1430 return False 

1431 

1432 # attributes / indices are allowed in `.format(...)` 

1433 fmtkey, _, _ = fmtkey.partition('.') 

1434 fmtkey, _, _ = fmtkey.partition('[') 

1435 

1436 try: 

1437 fmtkey = int(fmtkey) 

1438 except ValueError: 

1439 pass 

1440 else: # fmtkey was an integer 

1441 if auto is True: 

1442 self.report(messages.StringDotFormatMixingAutomatic, node) 

1443 return True 

1444 else: 

1445 auto = False 

1446 

1447 if fmtkey == '': 

1448 if auto is False: 

1449 self.report(messages.StringDotFormatMixingAutomatic, node) 

1450 return True 

1451 else: 

1452 auto = True 

1453 

1454 fmtkey = next_auto 

1455 next_auto += 1 

1456 

1457 if isinstance(fmtkey, int): 

1458 placeholder_positional.add(fmtkey) 

1459 else: 

1460 placeholder_named.add(fmtkey) 

1461 

1462 return False 

1463 

1464 for _, fmtkey, spec, _ in placeholders: 

1465 if _add_key(fmtkey): 

1466 return 

1467 

1468 # spec can also contain format specifiers 

1469 if spec is not None: 

1470 try: 

1471 spec_placeholders = tuple(parse_format_string(spec)) 

1472 except ValueError as e: 

1473 self.report(messages.StringDotFormatInvalidFormat, node, e) 

1474 return 

1475 

1476 for _, spec_fmtkey, spec_spec, _ in spec_placeholders: 

1477 # can't recurse again 

1478 if spec_spec is not None and '{' in spec_spec: 

1479 self.report( 

1480 messages.StringDotFormatInvalidFormat, 

1481 node, 

1482 'Max string recursion exceeded', 

1483 ) 

1484 return 

1485 if _add_key(spec_fmtkey): 

1486 return 

1487 

1488 # bail early if there is *args or **kwargs 

1489 if ( 

1490 # *args 

1491 any(isinstance(arg, ast.Starred) for arg in node.args) or 

1492 # **kwargs 

1493 any(kwd.arg is None for kwd in node.keywords) 

1494 ): 

1495 return 

1496 

1497 substitution_positional = set(range(len(node.args))) 

1498 substitution_named = {kwd.arg for kwd in node.keywords} 

1499 

1500 extra_positional = substitution_positional - placeholder_positional 

1501 extra_named = substitution_named - placeholder_named 

1502 

1503 missing_arguments = ( 

1504 (placeholder_positional | placeholder_named) - 

1505 (substitution_positional | substitution_named) 

1506 ) 

1507 

1508 if extra_positional: 

1509 self.report( 

1510 messages.StringDotFormatExtraPositionalArguments, 

1511 node, 

1512 ', '.join(sorted(str(x) for x in extra_positional)), 

1513 ) 

1514 if extra_named: 

1515 self.report( 

1516 messages.StringDotFormatExtraNamedArguments, 

1517 node, 

1518 ', '.join(sorted(extra_named)), 

1519 ) 

1520 if missing_arguments: 

1521 self.report( 

1522 messages.StringDotFormatMissingArgument, 

1523 node, 

1524 ', '.join(sorted(str(x) for x in missing_arguments)), 

1525 ) 

1526 

1527 def CALL(self, node): 

1528 if ( 

1529 isinstance(node.func, ast.Attribute) and 

1530 isinstance(node.func.value, ast.Constant) and 

1531 isinstance(node.func.value.value, str) and 

1532 node.func.attr == 'format' 

1533 ): 

1534 self._handle_string_dot_format(node) 

1535 

1536 omit = [] 

1537 annotated = [] 

1538 not_annotated = [] 

1539 

1540 if ( 

1541 _is_typing(node.func, 'cast', self.scopeStack) and 

1542 len(node.args) >= 1 

1543 ): 

1544 with self._enter_annotation(): 

1545 self.handleNode(node.args[0], node) 

1546 

1547 elif _is_typing(node.func, 'TypeVar', self.scopeStack): 

1548 

1549 # TypeVar("T", "int", "str") 

1550 omit += ["args"] 

1551 annotated += [arg for arg in node.args[1:]] 

1552 

1553 # TypeVar("T", bound="str") 

1554 omit += ["keywords"] 

1555 annotated += [k.value for k in node.keywords if k.arg == "bound"] 

1556 not_annotated += [ 

1557 (k, ["value"] if k.arg == "bound" else None) 

1558 for k in node.keywords 

1559 ] 

1560 

1561 elif _is_typing(node.func, "TypedDict", self.scopeStack): 

1562 # TypedDict("a", {"a": int}) 

1563 if len(node.args) > 1 and isinstance(node.args[1], ast.Dict): 

1564 omit += ["args"] 

1565 annotated += node.args[1].values 

1566 not_annotated += [ 

1567 (arg, ["values"] if i == 1 else None) 

1568 for i, arg in enumerate(node.args) 

1569 ] 

1570 

1571 # TypedDict("a", a=int) 

1572 omit += ["keywords"] 

1573 annotated += [k.value for k in node.keywords] 

1574 not_annotated += [(k, ["value"]) for k in node.keywords] 

1575 

1576 elif _is_typing(node.func, "NamedTuple", self.scopeStack): 

1577 # NamedTuple("a", [("a", int)]) 

1578 if ( 

1579 len(node.args) > 1 and 

1580 isinstance(node.args[1], (ast.Tuple, ast.List)) and 

1581 all(isinstance(x, (ast.Tuple, ast.List)) and 

1582 len(x.elts) == 2 for x in node.args[1].elts) 

1583 ): 

1584 omit += ["args"] 

1585 annotated += [elt.elts[1] for elt in node.args[1].elts] 

1586 not_annotated += [(elt.elts[0], None) for elt in node.args[1].elts] 

1587 not_annotated += [ 

1588 (arg, ["elts"] if i == 1 else None) 

1589 for i, arg in enumerate(node.args) 

1590 ] 

1591 not_annotated += [(elt, "elts") for elt in node.args[1].elts] 

1592 

1593 # NamedTuple("a", a=int) 

1594 omit += ["keywords"] 

1595 annotated += [k.value for k in node.keywords] 

1596 not_annotated += [(k, ["value"]) for k in node.keywords] 

1597 

1598 if omit: 

1599 with self._enter_annotation(AnnotationState.NONE): 

1600 for na_node, na_omit in not_annotated: 

1601 self.handleChildren(na_node, omit=na_omit) 

1602 self.handleChildren(node, omit=omit) 

1603 

1604 with self._enter_annotation(): 

1605 for annotated_node in annotated: 

1606 self.handleNode(annotated_node, node) 

1607 else: 

1608 self.handleChildren(node) 

1609 

1610 def _handle_percent_format(self, node): 

1611 try: 

1612 placeholders = parse_percent_format(node.left.value) 

1613 except ValueError: 

1614 self.report( 

1615 messages.PercentFormatInvalidFormat, 

1616 node, 

1617 'incomplete format', 

1618 ) 

1619 return 

1620 

1621 named = set() 

1622 positional_count = 0 

1623 positional = None 

1624 for _, placeholder in placeholders: 

1625 if placeholder is None: 

1626 continue 

1627 name, _, width, precision, conversion = placeholder 

1628 

1629 if conversion == '%': 

1630 continue 

1631 

1632 if conversion not in VALID_CONVERSIONS: 

1633 self.report( 

1634 messages.PercentFormatUnsupportedFormatCharacter, 

1635 node, 

1636 conversion, 

1637 ) 

1638 

1639 if positional is None and conversion: 

1640 positional = name is None 

1641 

1642 for part in (width, precision): 

1643 if part is not None and '*' in part: 

1644 if not positional: 

1645 self.report( 

1646 messages.PercentFormatStarRequiresSequence, 

1647 node, 

1648 ) 

1649 else: 

1650 positional_count += 1 

1651 

1652 if positional and name is not None: 

1653 self.report( 

1654 messages.PercentFormatMixedPositionalAndNamed, 

1655 node, 

1656 ) 

1657 return 

1658 elif not positional and name is None: 

1659 self.report( 

1660 messages.PercentFormatMixedPositionalAndNamed, 

1661 node, 

1662 ) 

1663 return 

1664 

1665 if positional: 

1666 positional_count += 1 

1667 else: 

1668 named.add(name) 

1669 

1670 if ( 

1671 isinstance(node.right, (ast.List, ast.Tuple)) and 

1672 # does not have any *splats (py35+ feature) 

1673 not any( 

1674 isinstance(elt, ast.Starred) 

1675 for elt in node.right.elts 

1676 ) 

1677 ): 

1678 substitution_count = len(node.right.elts) 

1679 if positional and positional_count != substitution_count: 

1680 self.report( 

1681 messages.PercentFormatPositionalCountMismatch, 

1682 node, 

1683 positional_count, 

1684 substitution_count, 

1685 ) 

1686 elif not positional: 

1687 self.report(messages.PercentFormatExpectedMapping, node) 

1688 

1689 if ( 

1690 isinstance(node.right, ast.Dict) and 

1691 all( 

1692 isinstance(k, ast.Constant) and isinstance(k.value, str) 

1693 for k in node.right.keys 

1694 ) 

1695 ): 

1696 if positional and positional_count > 1: 

1697 self.report(messages.PercentFormatExpectedSequence, node) 

1698 return 

1699 

1700 substitution_keys = {k.value for k in node.right.keys} 

1701 extra_keys = substitution_keys - named 

1702 missing_keys = named - substitution_keys 

1703 if not positional and extra_keys: 

1704 self.report( 

1705 messages.PercentFormatExtraNamedArguments, 

1706 node, 

1707 ', '.join(sorted(extra_keys)), 

1708 ) 

1709 if not positional and missing_keys: 

1710 self.report( 

1711 messages.PercentFormatMissingArgument, 

1712 node, 

1713 ', '.join(sorted(missing_keys)), 

1714 ) 

1715 

1716 def BINOP(self, node): 

1717 if ( 

1718 isinstance(node.op, ast.Mod) and 

1719 isinstance(node.left, ast.Constant) and 

1720 isinstance(node.left.value, str) 

1721 ): 

1722 self._handle_percent_format(node) 

1723 self.handleChildren(node) 

1724 

1725 def CONSTANT(self, node): 

1726 if isinstance(node.value, str) and self._in_annotation: 

1727 fn = functools.partial( 

1728 self.handleStringAnnotation, 

1729 node.value, 

1730 node, 

1731 node.lineno, 

1732 node.col_offset, 

1733 messages.ForwardAnnotationSyntaxError, 

1734 ) 

1735 self.deferFunction(fn) 

1736 

1737 # "slice" type nodes 

1738 SLICE = EXTSLICE = INDEX = handleChildren 

1739 

1740 # expression contexts are node instances too, though being constants 

1741 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore 

1742 

1743 # same for operators 

1744 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \ 

1745 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \ 

1746 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = \ 

1747 MATMULT = ignore 

1748 

1749 def RAISE(self, node): 

1750 self.handleChildren(node) 

1751 

1752 arg = node.exc 

1753 

1754 if isinstance(arg, ast.Call): 

1755 if is_notimplemented_name_node(arg.func): 

1756 # Handle "raise NotImplemented(...)" 

1757 self.report(messages.RaiseNotImplemented, node) 

1758 elif is_notimplemented_name_node(arg): 

1759 # Handle "raise NotImplemented" 

1760 self.report(messages.RaiseNotImplemented, node) 

1761 

1762 # additional node types 

1763 COMPREHENSION = KEYWORD = FORMATTEDVALUE = handleChildren 

1764 

1765 _in_fstring = False 

1766 

1767 def JOINEDSTR(self, node): 

1768 if ( 

1769 # the conversion / etc. flags are parsed as f-strings without 

1770 # placeholders 

1771 not self._in_fstring and 

1772 not any(isinstance(x, ast.FormattedValue) for x in node.values) 

1773 ): 

1774 self.report(messages.FStringMissingPlaceholders, node) 

1775 

1776 self._in_fstring, orig = True, self._in_fstring 

1777 try: 

1778 self.handleChildren(node) 

1779 finally: 

1780 self._in_fstring = orig 

1781 

1782 def TEMPLATESTR(self, node): 

1783 if not any(isinstance(x, ast.Interpolation) for x in node.values): 

1784 self.report(messages.TStringMissingPlaceholders, node) 

1785 

1786 # similar to f-strings, conversion / etc. flags are parsed as f-strings 

1787 # without placeholders 

1788 self._in_fstring, orig = True, self._in_fstring 

1789 try: 

1790 self.handleChildren(node) 

1791 finally: 

1792 self._in_fstring = orig 

1793 

1794 INTERPOLATION = handleChildren 

1795 

1796 def DICT(self, node): 

1797 # Complain if there are duplicate keys with different values 

1798 # If they have the same value it's not going to cause potentially 

1799 # unexpected behaviour so we'll not complain. 

1800 keys = [ 

1801 convert_to_value(key) for key in node.keys 

1802 ] 

1803 

1804 key_counts = collections.Counter(keys) 

1805 duplicate_keys = [ 

1806 key for key, count in key_counts.items() 

1807 if count > 1 

1808 ] 

1809 

1810 for key in duplicate_keys: 

1811 key_indices = [i for i, i_key in enumerate(keys) if i_key == key] 

1812 

1813 values = collections.Counter( 

1814 convert_to_value(node.values[index]) 

1815 for index in key_indices 

1816 ) 

1817 if any(count == 1 for value, count in values.items()): 

1818 for key_index in key_indices: 

1819 key_node = node.keys[key_index] 

1820 if isinstance(key, VariableKey): 

1821 self.report(messages.MultiValueRepeatedKeyVariable, 

1822 key_node, 

1823 key.name) 

1824 else: 

1825 self.report( 

1826 messages.MultiValueRepeatedKeyLiteral, 

1827 key_node, 

1828 key, 

1829 ) 

1830 self.handleChildren(node) 

1831 

1832 def IF(self, node): 

1833 if isinstance(node.test, ast.Tuple) and node.test.elts != []: 

1834 self.report(messages.IfTuple, node) 

1835 self.handleChildren(node) 

1836 

1837 IFEXP = IF 

1838 

1839 def ASSERT(self, node): 

1840 if isinstance(node.test, ast.Tuple) and node.test.elts != []: 

1841 self.report(messages.AssertTuple, node) 

1842 self.handleChildren(node) 

1843 

1844 def GLOBAL(self, node): 

1845 """ 

1846 Keep track of globals declarations. 

1847 """ 

1848 global_scope_index = 1 if self._in_doctest() else 0 

1849 global_scope = self.scopeStack[global_scope_index] 

1850 

1851 # Ignore 'global' statement in global scope. 

1852 if self.scope is not global_scope: 

1853 

1854 # One 'global' statement can bind multiple (comma-delimited) names. 

1855 for node_name in node.names: 

1856 node_value = Assignment(node_name, node) 

1857 

1858 # Remove UndefinedName messages already reported for this name. 

1859 # TODO: if the global is not used in this scope, it does not 

1860 # become a globally defined name. See test_unused_global. 

1861 self.messages = [ 

1862 m for m in self.messages if not 

1863 isinstance(m, messages.UndefinedName) or 

1864 m.message_args[0] != node_name] 

1865 

1866 # Bind name to global scope if it doesn't exist already. 

1867 global_scope.setdefault(node_name, node_value) 

1868 

1869 # Bind name to non-global scopes, but as already "used". 

1870 node_value.used = (global_scope, node) 

1871 for scope in self.scopeStack[global_scope_index + 1:]: 

1872 scope[node_name] = node_value 

1873 

1874 self.scope.indirect_assignments[node_name] = node 

1875 

1876 NONLOCAL = GLOBAL 

1877 

1878 def GENERATOREXP(self, node): 

1879 with self.in_scope(GeneratorScope): 

1880 self.handleChildren(node) 

1881 

1882 LISTCOMP = DICTCOMP = SETCOMP = GENERATOREXP 

1883 

1884 def NAME(self, node): 

1885 """ 

1886 Handle occurrence of Name (which can be a load/store/delete access.) 

1887 """ 

1888 # Locate the name in locals / function / globals scopes. 

1889 if isinstance(node.ctx, ast.Load): 

1890 self.handleNodeLoad(node, self.getParent(node)) 

1891 if (node.id == 'locals' and isinstance(self.scope, FunctionScope) and 

1892 isinstance(node._pyflakes_parent, ast.Call)): 

1893 # we are doing locals() call in current scope 

1894 self.scope.usesLocals = True 

1895 elif isinstance(node.ctx, ast.Store): 

1896 self.handleNodeStore(node) 

1897 elif isinstance(node.ctx, ast.Del): 

1898 self.handleNodeDelete(node) 

1899 else: 

1900 # Unknown context 

1901 raise RuntimeError(f"Got impossible expression context: {node.ctx!r}") 

1902 

1903 def CONTINUE(self, node): 

1904 # Walk the tree up until we see a loop (OK), a function or class 

1905 # definition (not OK), for 'continue', a finally block (not OK), or 

1906 # the top module scope (not OK) 

1907 n = node 

1908 while hasattr(n, '_pyflakes_parent'): 

1909 n, n_child = n._pyflakes_parent, n 

1910 if isinstance(n, (ast.While, ast.For, ast.AsyncFor)): 

1911 # Doesn't apply unless it's in the loop itself 

1912 if n_child not in n.orelse: 

1913 return 

1914 if isinstance(n, (ast.FunctionDef, ast.ClassDef)): 

1915 break 

1916 if isinstance(node, ast.Continue): 

1917 self.report(messages.ContinueOutsideLoop, node) 

1918 else: # ast.Break 

1919 self.report(messages.BreakOutsideLoop, node) 

1920 

1921 BREAK = CONTINUE 

1922 

1923 def RETURN(self, node): 

1924 if isinstance(self.scope, (ClassScope, ModuleScope)): 

1925 self.report(messages.ReturnOutsideFunction, node) 

1926 return 

1927 

1928 self.handleNode(node.value, node) 

1929 

1930 def YIELD(self, node): 

1931 if isinstance(self.scope, (ClassScope, ModuleScope)): 

1932 self.report(messages.YieldOutsideFunction, node) 

1933 return 

1934 

1935 self.handleNode(node.value, node) 

1936 

1937 AWAIT = YIELDFROM = YIELD 

1938 

1939 def FUNCTIONDEF(self, node): 

1940 for deco in node.decorator_list: 

1941 self.handleNode(deco, node) 

1942 

1943 with self._type_param_scope(node): 

1944 self.LAMBDA(node) 

1945 

1946 self.addBinding(node, FunctionDefinition(node.name, node)) 

1947 # doctest does not process doctest within a doctest, 

1948 # or in nested functions. 

1949 if (self.withDoctest and 

1950 not self._in_doctest() and 

1951 not isinstance(self.scope, FunctionScope)): 

1952 self.deferFunction(lambda: self.handleDoctests(node)) 

1953 

1954 ASYNCFUNCTIONDEF = FUNCTIONDEF 

1955 

1956 def LAMBDA(self, node): 

1957 args = [] 

1958 annotations = [] 

1959 

1960 for arg in node.args.posonlyargs: 

1961 args.append(arg.arg) 

1962 annotations.append(arg.annotation) 

1963 for arg in node.args.args + node.args.kwonlyargs: 

1964 args.append(arg.arg) 

1965 annotations.append(arg.annotation) 

1966 defaults = node.args.defaults + node.args.kw_defaults 

1967 

1968 has_annotations = not isinstance(node, ast.Lambda) 

1969 

1970 for arg_name in ('vararg', 'kwarg'): 

1971 wildcard = getattr(node.args, arg_name) 

1972 if not wildcard: 

1973 continue 

1974 args.append(wildcard.arg) 

1975 if has_annotations: 

1976 annotations.append(wildcard.annotation) 

1977 

1978 if has_annotations: 

1979 annotations.append(node.returns) 

1980 

1981 if len(set(args)) < len(args): 

1982 for (idx, arg) in enumerate(args): 

1983 if arg in args[:idx]: 

1984 self.report(messages.DuplicateArgument, node, arg) 

1985 

1986 for annotation in annotations: 

1987 self.handleAnnotation(annotation, node) 

1988 

1989 for default in defaults: 

1990 self.handleNode(default, node) 

1991 

1992 def runFunction(): 

1993 with self.in_scope(FunctionScope): 

1994 self.handleChildren( 

1995 node, 

1996 omit=('decorator_list', 'returns', 'type_params'), 

1997 ) 

1998 

1999 self.deferFunction(runFunction) 

2000 

2001 def ARGUMENTS(self, node): 

2002 self.handleChildren(node, omit=('defaults', 'kw_defaults')) 

2003 

2004 def ARG(self, node): 

2005 self.addBinding(node, Argument(node.arg, self.getScopeNode(node))) 

2006 

2007 def CLASSDEF(self, node): 

2008 """ 

2009 Check names used in a class definition, including its decorators, base 

2010 classes, and the body of its definition. Additionally, add its name to 

2011 the current scope. 

2012 """ 

2013 for deco in node.decorator_list: 

2014 self.handleNode(deco, node) 

2015 

2016 with self._type_param_scope(node): 

2017 for baseNode in node.bases: 

2018 self.handleNode(baseNode, node) 

2019 for keywordNode in node.keywords: 

2020 self.handleNode(keywordNode, node) 

2021 with self.in_scope(ClassScope): 

2022 # doctest does not process doctest within a doctest 

2023 # classes within classes are processed. 

2024 if (self.withDoctest and 

2025 not self._in_doctest() and 

2026 not isinstance(self.scope, FunctionScope)): 

2027 self.deferFunction(lambda: self.handleDoctests(node)) 

2028 for stmt in node.body: 

2029 self.handleNode(stmt, node) 

2030 

2031 self.addBinding(node, ClassDefinition(node.name, node)) 

2032 

2033 def AUGASSIGN(self, node): 

2034 self.handleNodeLoad(node.target, node) 

2035 self.handleNode(node.value, node) 

2036 self.handleNode(node.target, node) 

2037 

2038 def TUPLE(self, node): 

2039 if isinstance(node.ctx, ast.Store): 

2040 # Python 3 advanced tuple unpacking: a, *b, c = d. 

2041 # Only one starred expression is allowed, and no more than 1<<8 

2042 # assignments are allowed before a stared expression. There is 

2043 # also a limit of 1<<24 expressions after the starred expression, 

2044 # which is impossible to test due to memory restrictions, but we 

2045 # add it here anyway 

2046 has_starred = False 

2047 star_loc = -1 

2048 for i, n in enumerate(node.elts): 

2049 if isinstance(n, ast.Starred): 

2050 if has_starred: 

2051 self.report(messages.TwoStarredExpressions, node) 

2052 # The SyntaxError doesn't distinguish two from more 

2053 # than two. 

2054 break 

2055 has_starred = True 

2056 star_loc = i 

2057 if star_loc >= 1 << 8 or len(node.elts) - star_loc - 1 >= 1 << 24: 

2058 self.report(messages.TooManyExpressionsInStarredAssignment, node) 

2059 self.handleChildren(node) 

2060 

2061 LIST = TUPLE 

2062 

2063 def IMPORT(self, node): 

2064 for alias in node.names: 

2065 if '.' in alias.name and not alias.asname: 

2066 importation = SubmoduleImportation(alias.name, node) 

2067 else: 

2068 name = alias.asname or alias.name 

2069 importation = Importation(name, node, alias.name) 

2070 self.addBinding(node, importation) 

2071 

2072 def IMPORTFROM(self, node): 

2073 if node.module == '__future__': 

2074 if not self.futuresAllowed: 

2075 self.report(messages.LateFutureImport, node) 

2076 else: 

2077 self.futuresAllowed = False 

2078 

2079 module = ('.' * node.level) + (node.module or '') 

2080 

2081 for alias in node.names: 

2082 name = alias.asname or alias.name 

2083 if node.module == '__future__': 

2084 importation = FutureImportation(name, node, self.scope) 

2085 if alias.name not in __future__.all_feature_names: 

2086 self.report(messages.FutureFeatureNotDefined, 

2087 node, alias.name) 

2088 if alias.name == 'annotations': 

2089 self.annotationsFutureEnabled = True 

2090 elif alias.name == '*': 

2091 if not isinstance(self.scope, ModuleScope): 

2092 self.report(messages.ImportStarNotPermitted, 

2093 node, module) 

2094 continue 

2095 

2096 self.scope.importStarred = True 

2097 self.report(messages.ImportStarUsed, node, module) 

2098 importation = StarImportation(module, node) 

2099 else: 

2100 importation = ImportationFrom(name, node, 

2101 module, alias.name) 

2102 self.addBinding(node, importation) 

2103 

2104 def TRY(self, node): 

2105 handler_names = [] 

2106 # List the exception handlers 

2107 for i, handler in enumerate(node.handlers): 

2108 if isinstance(handler.type, ast.Tuple): 

2109 for exc_type in handler.type.elts: 

2110 handler_names.append(getNodeName(exc_type)) 

2111 elif handler.type: 

2112 handler_names.append(getNodeName(handler.type)) 

2113 

2114 if handler.type is None and i < len(node.handlers) - 1: 

2115 self.report(messages.DefaultExceptNotLast, handler) 

2116 # Memorize the except handlers and process the body 

2117 self.exceptHandlers.append(handler_names) 

2118 for child in node.body: 

2119 self.handleNode(child, node) 

2120 self.exceptHandlers.pop() 

2121 # Process the other nodes: "except:", "else:", "finally:" 

2122 self.handleChildren(node, omit='body') 

2123 

2124 TRYSTAR = TRY 

2125 

2126 def EXCEPTHANDLER(self, node): 

2127 if node.name is None: 

2128 self.handleChildren(node) 

2129 return 

2130 

2131 # If the name already exists in the scope, modify state of existing 

2132 # binding. 

2133 if node.name in self.scope: 

2134 self.handleNodeStore(node) 

2135 

2136 # 3.x: the name of the exception, which is not a Name node, but a 

2137 # simple string, creates a local that is only bound within the scope of 

2138 # the except: block. As such, temporarily remove the existing binding 

2139 # to more accurately determine if the name is used in the except: 

2140 # block. 

2141 

2142 try: 

2143 prev_definition = self.scope.pop(node.name) 

2144 except KeyError: 

2145 prev_definition = None 

2146 

2147 self.handleNodeStore(node) 

2148 self.handleChildren(node) 

2149 

2150 # See discussion on https://github.com/PyCQA/pyflakes/pull/59 

2151 

2152 # We're removing the local name since it's being unbound after leaving 

2153 # the except: block and it's always unbound if the except: block is 

2154 # never entered. This will cause an "undefined name" error raised if 

2155 # the checked code tries to use the name afterwards. 

2156 # 

2157 # Unless it's been removed already. Then do nothing. 

2158 

2159 try: 

2160 binding = self.scope.pop(node.name) 

2161 except KeyError: 

2162 pass 

2163 else: 

2164 if not binding.used: 

2165 self.report(messages.UnusedVariable, node, node.name) 

2166 

2167 # Restore. 

2168 if prev_definition: 

2169 self.scope[node.name] = prev_definition 

2170 

2171 def ANNASSIGN(self, node): 

2172 self.handleAnnotation(node.annotation, node) 

2173 # If the assignment has value, handle the *value* now. 

2174 if node.value: 

2175 # If the annotation is `TypeAlias`, handle the *value* as an annotation. 

2176 if _is_typing(node.annotation, 'TypeAlias', self.scopeStack): 

2177 self.handleAnnotation(node.value, node) 

2178 else: 

2179 self.handleNode(node.value, node) 

2180 self.handleNode(node.target, node) 

2181 

2182 def COMPARE(self, node): 

2183 left = node.left 

2184 for op, right in zip(node.ops, node.comparators): 

2185 if ( 

2186 isinstance(op, (ast.Is, ast.IsNot)) and ( 

2187 _is_const_non_singleton(left) or 

2188 _is_const_non_singleton(right) 

2189 ) 

2190 ): 

2191 self.report(messages.IsLiteral, node) 

2192 left = right 

2193 

2194 self.handleChildren(node) 

2195 

2196 MATCH = MATCH_CASE = MATCHCLASS = MATCHOR = MATCHSEQUENCE = handleChildren 

2197 MATCHSINGLETON = MATCHVALUE = handleChildren 

2198 

2199 def _match_target(self, node): 

2200 self.handleNodeStore(node) 

2201 self.handleChildren(node) 

2202 

2203 MATCHAS = MATCHMAPPING = MATCHSTAR = _match_target 

2204 

2205 @contextlib.contextmanager 

2206 def _type_param_scope(self, node): 

2207 with contextlib.ExitStack() as ctx: 

2208 if sys.version_info >= (3, 12): 

2209 ctx.enter_context(self.in_scope(TypeScope)) 

2210 for param in node.type_params: 

2211 self.handleNode(param, node) 

2212 yield 

2213 

2214 def TYPEVAR(self, node): 

2215 self.handleNodeStore(node) 

2216 self.handle_annotation_always_deferred(node.bound, node) 

2217 

2218 PARAMSPEC = TYPEVARTUPLE = handleNodeStore 

2219 

2220 def TYPEALIAS(self, node): 

2221 self.handleNode(node.name, node) 

2222 with self._type_param_scope(node): 

2223 self.handle_annotation_always_deferred(node.value, node)