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

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

1164 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 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 PARAMSPEC = TYPEVARTUPLE = handleNodeStore 

2196 

2197 def TYPEALIAS(self, node): 

2198 self.handleNode(node.name, node) 

2199 with self._type_param_scope(node): 

2200 self.handle_annotation_always_deferred(node.value, node)