Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py: 52%

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

1272 statements  

1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html 

2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE 

3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt 

4 

5""" 

6This module contains the classes for "scoped" node, i.e. which are opening a 

7new local scope in the language definition : Module, ClassDef, FunctionDef (and 

8Lambda, GeneratorExp, DictComp and SetComp to some extent). 

9""" 

10 

11from __future__ import annotations 

12 

13import io 

14import itertools 

15import os 

16import warnings 

17from collections.abc import Generator, Iterable, Iterator, Sequence 

18from functools import cached_property, lru_cache 

19from typing import TYPE_CHECKING, Any, ClassVar, Literal, NoReturn, TypeVar 

20 

21from astroid import bases, protocols, util 

22from astroid.context import ( 

23 CallContext, 

24 InferenceContext, 

25 bind_context_to_node, 

26 copy_context, 

27) 

28from astroid.exceptions import ( 

29 AstroidBuildingError, 

30 AstroidTypeError, 

31 AttributeInferenceError, 

32 DuplicateBasesError, 

33 InconsistentMroError, 

34 InferenceError, 

35 MroError, 

36 ParentMissingError, 

37 StatementMissing, 

38 TooManyLevelsError, 

39) 

40from astroid.interpreter.dunder_lookup import lookup 

41from astroid.interpreter.objectmodel import ClassModel, FunctionModel, ModuleModel 

42from astroid.manager import AstroidManager 

43from astroid.nodes import _base_nodes, node_classes 

44from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG 

45from astroid.nodes.scoped_nodes.utils import builtin_lookup 

46from astroid.nodes.utils import Position 

47from astroid.typing import ( 

48 InferBinaryOp, 

49 InferenceErrorInfo, 

50 InferenceResult, 

51 SuccessfulInferenceResult, 

52) 

53 

54if TYPE_CHECKING: 

55 from astroid import nodes, objects 

56 from astroid.nodes import Arguments, Const, NodeNG 

57 from astroid.nodes._base_nodes import LookupMixIn 

58 

59 

60ITER_METHODS = ("__iter__", "__getitem__") 

61EXCEPTION_BASE_CLASSES = frozenset({"Exception", "BaseException"}) 

62BUILTIN_DESCRIPTORS = frozenset( 

63 {"classmethod", "staticmethod", "builtins.classmethod", "builtins.staticmethod"} 

64) 

65 

66_T = TypeVar("_T") 

67 

68 

69def _c3_merge(sequences, cls, context): 

70 """Merges MROs in *sequences* to a single MRO using the C3 algorithm. 

71 

72 Adapted from http://www.python.org/download/releases/2.3/mro/. 

73 

74 """ 

75 result = [] 

76 while True: 

77 sequences = [s for s in sequences if s] # purge empty sequences 

78 if not sequences: 

79 return result 

80 for s1 in sequences: # find merge candidates among seq heads 

81 candidate = s1[0] 

82 for s2 in sequences: 

83 if candidate in s2[1:]: 

84 candidate = None 

85 break # reject the current head, it appears later 

86 else: 

87 break 

88 if not candidate: 

89 # Show all the remaining bases, which were considered as 

90 # candidates for the next mro sequence. 

91 raise InconsistentMroError( 

92 message="Cannot create a consistent method resolution order " 

93 "for MROs {mros} of class {cls!r}.", 

94 mros=sequences, 

95 cls=cls, 

96 context=context, 

97 ) 

98 

99 result.append(candidate) 

100 # remove the chosen candidate 

101 for seq in sequences: 

102 if seq[0] == candidate: 

103 del seq[0] 

104 return None 

105 

106 

107def clean_typing_generic_mro(sequences: list[list[ClassDef]]) -> None: 

108 """A class can inherit from typing.Generic directly, as base, 

109 and as base of bases. The merged MRO must however only contain the last entry. 

110 To prepare for _c3_merge, remove some typing.Generic entries from 

111 sequences if multiple are present. 

112 

113 This method will check if Generic is in inferred_bases and also 

114 part of bases_mro. If true, remove it from inferred_bases 

115 as well as its entry the bases_mro. 

116 

117 Format sequences: [[self]] + bases_mro + [inferred_bases] 

118 """ 

119 bases_mro = sequences[1:-1] 

120 inferred_bases = sequences[-1] 

121 # Check if Generic is part of inferred_bases 

122 for i, base in enumerate(inferred_bases): 

123 if base.qname() == "typing.Generic": 

124 position_in_inferred_bases = i 

125 break 

126 else: 

127 return 

128 # Check if also part of bases_mro 

129 # Ignore entry for typing.Generic 

130 for i, seq in enumerate(bases_mro): 

131 if i == position_in_inferred_bases: 

132 continue 

133 if any(base.qname() == "typing.Generic" for base in seq): 

134 break 

135 else: 

136 return 

137 # Found multiple Generics in mro, remove entry from inferred_bases 

138 # and the corresponding one from bases_mro 

139 inferred_bases.pop(position_in_inferred_bases) 

140 bases_mro.pop(position_in_inferred_bases) 

141 

142 

143def clean_duplicates_mro( 

144 sequences: list[list[ClassDef]], 

145 cls: ClassDef, 

146 context: InferenceContext | None, 

147) -> list[list[ClassDef]]: 

148 for sequence in sequences: 

149 seen = set() 

150 for node in sequence: 

151 lineno_and_qname = (node.lineno, node.qname()) 

152 if lineno_and_qname in seen: 

153 raise DuplicateBasesError( 

154 message="Duplicates found in MROs {mros} for {cls!r}.", 

155 mros=sequences, 

156 cls=cls, 

157 context=context, 

158 ) 

159 seen.add(lineno_and_qname) 

160 return sequences 

161 

162 

163def function_to_method(n, klass): 

164 if isinstance(n, FunctionDef): 

165 if n.type == "classmethod": 

166 return bases.BoundMethod(n, klass) 

167 if n.type == "property": 

168 return n 

169 if n.type != "staticmethod": 

170 return bases.UnboundMethod(n) 

171 return n 

172 

173 

174def _infer_last( 

175 arg: SuccessfulInferenceResult, context: InferenceContext 

176) -> InferenceResult: 

177 res = util.Uninferable 

178 for b in arg.infer(context=context.clone()): 

179 res = b 

180 return res 

181 

182 

183class Module(LocalsDictNodeNG): 

184 """Class representing an :class:`ast.Module` node. 

185 

186 >>> import astroid 

187 >>> node = astroid.extract_node('import astroid') 

188 >>> node 

189 <Import l.1 at 0x7f23b2e4e5c0> 

190 >>> node.parent 

191 <Module l.0 at 0x7f23b2e4eda0> 

192 """ 

193 

194 _astroid_fields = ("doc_node", "body") 

195 

196 doc_node: Const | None 

197 """The doc node associated with this node.""" 

198 

199 # attributes below are set by the builder module or by raw factories 

200 

201 file_bytes: str | bytes | None = None 

202 """The string/bytes that this ast was built from.""" 

203 

204 file_encoding: str | None = None 

205 """The encoding of the source file. 

206 

207 This is used to get unicode out of a source file. 

208 Python 2 only. 

209 """ 

210 

211 special_attributes = ModuleModel() 

212 """The names of special attributes that this module has.""" 

213 

214 # names of module attributes available through the global scope 

215 scope_attrs: ClassVar[set[str]] = { 

216 "__name__", 

217 "__doc__", 

218 "__file__", 

219 "__path__", 

220 "__package__", 

221 } 

222 """The names of module attributes available through the global scope.""" 

223 

224 _other_fields = ( 

225 "name", 

226 "file", 

227 "path", 

228 "package", 

229 "pure_python", 

230 "future_imports", 

231 ) 

232 _other_other_fields = ("locals", "globals") 

233 

234 def __init__( 

235 self, 

236 name: str, 

237 file: str | None = None, 

238 path: Sequence[str] | None = None, 

239 package: bool = False, 

240 pure_python: bool = True, 

241 ) -> None: 

242 self.name = name 

243 """The name of the module.""" 

244 

245 self.file = file 

246 """The path to the file that this ast has been extracted from. 

247 

248 This will be ``None`` when the representation has been built from a 

249 built-in module. 

250 """ 

251 

252 self.path = path 

253 

254 self.package = package 

255 """Whether the node represents a package or a module.""" 

256 

257 self.pure_python = pure_python 

258 """Whether the ast was built from source.""" 

259 

260 self.globals: dict[str, list[InferenceResult]] 

261 """A map of the name of a global variable to the node defining the global.""" 

262 

263 self.locals = self.globals = {} 

264 """A map of the name of a local variable to the node defining the local.""" 

265 

266 self.body: list[node_classes.NodeNG] = [] 

267 """The contents of the module.""" 

268 

269 self.future_imports: set[str] = set() 

270 """The imports from ``__future__``.""" 

271 

272 super().__init__( 

273 lineno=0, parent=None, col_offset=0, end_lineno=None, end_col_offset=None 

274 ) 

275 

276 # pylint: enable=redefined-builtin 

277 

278 def postinit( 

279 self, body: list[node_classes.NodeNG], *, doc_node: Const | None = None 

280 ): 

281 self.body = body 

282 self.doc_node = doc_node 

283 

284 def _get_stream(self): 

285 if self.file_bytes is not None: 

286 return io.BytesIO(self.file_bytes) 

287 if self.file is not None: 

288 # pylint: disable=consider-using-with 

289 stream = open(self.file, "rb") 

290 return stream 

291 return None 

292 

293 def stream(self): 

294 """Get a stream to the underlying file or bytes. 

295 

296 :type: file or io.BytesIO or None 

297 """ 

298 return self._get_stream() 

299 

300 def block_range(self, lineno: int) -> tuple[int, int]: 

301 """Get a range from where this node starts to where this node ends. 

302 

303 :param lineno: Unused. 

304 

305 :returns: The range of line numbers that this node belongs to. 

306 """ 

307 return self.fromlineno, self.tolineno 

308 

309 def scope_lookup( 

310 self, node: LookupMixIn, name: str, offset: int = 0 

311 ) -> tuple[LocalsDictNodeNG, list[node_classes.NodeNG]]: 

312 """Lookup where the given variable is assigned. 

313 

314 :param node: The node to look for assignments up to. 

315 Any assignments after the given node are ignored. 

316 

317 :param name: The name of the variable to find assignments for. 

318 

319 :param offset: The line offset to filter statements up to. 

320 

321 :returns: This scope node and the list of assignments associated to the 

322 given name according to the scope where it has been found (locals, 

323 globals or builtin). 

324 """ 

325 if name in self.scope_attrs and name not in self.locals: 

326 try: 

327 return self, self.getattr(name) 

328 except AttributeInferenceError: 

329 return self, [] 

330 return self._scope_lookup(node, name, offset) 

331 

332 def pytype(self) -> Literal["builtins.module"]: 

333 """Get the name of the type that this node represents. 

334 

335 :returns: The name of the type. 

336 """ 

337 return "builtins.module" 

338 

339 def display_type(self) -> str: 

340 """A human readable type of this node. 

341 

342 :returns: The type of this node. 

343 :rtype: str 

344 """ 

345 return "Module" 

346 

347 def getattr( 

348 self, name, context: InferenceContext | None = None, ignore_locals=False 

349 ): 

350 if not name: 

351 raise AttributeInferenceError(target=self, attribute=name, context=context) 

352 

353 result = [] 

354 name_in_locals = name in self.locals 

355 

356 if name in self.special_attributes and not ignore_locals and not name_in_locals: 

357 result = [self.special_attributes.lookup(name)] 

358 if name == "__name__": 

359 main_const = node_classes.const_factory("__main__") 

360 main_const.parent = AstroidManager().builtins_module 

361 result.append(main_const) 

362 elif not ignore_locals and name_in_locals: 

363 result = self.locals[name] 

364 elif self.package: 

365 try: 

366 result = [self.import_module(name, relative_only=True)] 

367 except (AstroidBuildingError, SyntaxError) as exc: 

368 raise AttributeInferenceError( 

369 target=self, attribute=name, context=context 

370 ) from exc 

371 result = [n for n in result if not isinstance(n, node_classes.DelName)] 

372 if result: 

373 return result 

374 raise AttributeInferenceError(target=self, attribute=name, context=context) 

375 

376 def igetattr( 

377 self, name: str, context: InferenceContext | None = None 

378 ) -> Iterator[InferenceResult]: 

379 """Infer the possible values of the given variable. 

380 

381 :param name: The name of the variable to infer. 

382 

383 :returns: The inferred possible values. 

384 """ 

385 # set lookup name since this is necessary to infer on import nodes for 

386 # instance 

387 context = copy_context(context) 

388 context.lookupname = name 

389 try: 

390 return bases._infer_stmts(self.getattr(name, context), context, frame=self) 

391 except AttributeInferenceError as error: 

392 raise InferenceError( 

393 str(error), target=self, attribute=name, context=context 

394 ) from error 

395 

396 def fully_defined(self) -> bool: 

397 """Check if this module has been build from a .py file. 

398 

399 If so, the module contains a complete representation, 

400 including the code. 

401 

402 :returns: Whether the module has been built from a .py file. 

403 """ 

404 return self.file is not None and self.file.endswith(".py") 

405 

406 def statement(self, *, future: Literal[None, True] = None) -> NoReturn: 

407 """The first parent node, including self, marked as statement node. 

408 

409 When called on a :class:`Module` this raises a StatementMissing. 

410 """ 

411 if future is not None: 

412 warnings.warn( 

413 "The future arg will be removed in astroid 4.0.", 

414 DeprecationWarning, 

415 stacklevel=2, 

416 ) 

417 raise StatementMissing(target=self) 

418 

419 def previous_sibling(self): 

420 """The previous sibling statement. 

421 

422 :returns: The previous sibling statement node. 

423 :rtype: NodeNG or None 

424 """ 

425 

426 def next_sibling(self): 

427 """The next sibling statement node. 

428 

429 :returns: The next sibling statement node. 

430 :rtype: NodeNG or None 

431 """ 

432 

433 _absolute_import_activated = True 

434 

435 def absolute_import_activated(self) -> bool: 

436 """Whether :pep:`328` absolute import behaviour has been enabled. 

437 

438 :returns: Whether :pep:`328` has been enabled. 

439 """ 

440 return self._absolute_import_activated 

441 

442 def import_module( 

443 self, 

444 modname: str, 

445 relative_only: bool = False, 

446 level: int | None = None, 

447 use_cache: bool = True, 

448 ) -> Module: 

449 """Get the ast for a given module as if imported from this module. 

450 

451 :param modname: The name of the module to "import". 

452 

453 :param relative_only: Whether to only consider relative imports. 

454 

455 :param level: The level of relative import. 

456 

457 :param use_cache: Whether to use the astroid_cache of modules. 

458 

459 :returns: The imported module ast. 

460 """ 

461 if relative_only and level is None: 

462 level = 0 

463 absmodname = self.relative_to_absolute_name(modname, level) 

464 

465 try: 

466 return AstroidManager().ast_from_module_name( 

467 absmodname, use_cache=use_cache 

468 ) 

469 except AstroidBuildingError: 

470 # we only want to import a sub module or package of this module, 

471 # skip here 

472 if relative_only: 

473 raise 

474 # Don't repeat the same operation, e.g. for missing modules 

475 # like "_winapi" or "nt" on POSIX systems. 

476 if modname == absmodname: 

477 raise 

478 return AstroidManager().ast_from_module_name(modname, use_cache=use_cache) 

479 

480 def relative_to_absolute_name(self, modname: str, level: int | None) -> str: 

481 """Get the absolute module name for a relative import. 

482 

483 The relative import can be implicit or explicit. 

484 

485 :param modname: The module name to convert. 

486 

487 :param level: The level of relative import. 

488 

489 :returns: The absolute module name. 

490 

491 :raises TooManyLevelsError: When the relative import refers to a 

492 module too far above this one. 

493 """ 

494 # XXX this returns non sens when called on an absolute import 

495 # like 'pylint.checkers.astroid.utils' 

496 # XXX doesn't return absolute name if self.name isn't absolute name 

497 if self.absolute_import_activated() and level is None: 

498 return modname 

499 if level: 

500 if self.package: 

501 level = level - 1 

502 package_name = self.name.rsplit(".", level)[0] 

503 elif ( 

504 self.path 

505 and not os.path.exists(os.path.dirname(self.path[0]) + "/__init__.py") 

506 and os.path.exists( 

507 os.path.dirname(self.path[0]) + "/" + modname.split(".")[0] 

508 ) 

509 ): 

510 level = level - 1 

511 package_name = "" 

512 else: 

513 package_name = self.name.rsplit(".", level)[0] 

514 if level and self.name.count(".") < level: 

515 raise TooManyLevelsError(level=level, name=self.name) 

516 

517 elif self.package: 

518 package_name = self.name 

519 else: 

520 package_name = self.name.rsplit(".", 1)[0] 

521 

522 if package_name: 

523 if not modname: 

524 return package_name 

525 return f"{package_name}.{modname}" 

526 return modname 

527 

528 def wildcard_import_names(self): 

529 """The list of imported names when this module is 'wildcard imported'. 

530 

531 It doesn't include the '__builtins__' name which is added by the 

532 current CPython implementation of wildcard imports. 

533 

534 :returns: The list of imported names. 

535 :rtype: list(str) 

536 """ 

537 # We separate the different steps of lookup in try/excepts 

538 # to avoid catching too many Exceptions 

539 default = [name for name in self.keys() if not name.startswith("_")] 

540 try: 

541 all_values = self["__all__"] 

542 except KeyError: 

543 return default 

544 

545 try: 

546 explicit = next(all_values.assigned_stmts()) 

547 except (InferenceError, StopIteration): 

548 return default 

549 except AttributeError: 

550 # not an assignment node 

551 # XXX infer? 

552 return default 

553 

554 # Try our best to detect the exported name. 

555 inferred = [] 

556 try: 

557 explicit = next(explicit.infer()) 

558 except (InferenceError, StopIteration): 

559 return default 

560 if not isinstance(explicit, (node_classes.Tuple, node_classes.List)): 

561 return default 

562 

563 def str_const(node) -> bool: 

564 return isinstance(node, node_classes.Const) and isinstance(node.value, str) 

565 

566 for node in explicit.elts: 

567 if str_const(node): 

568 inferred.append(node.value) 

569 else: 

570 try: 

571 inferred_node = next(node.infer()) 

572 except (InferenceError, StopIteration): 

573 continue 

574 if str_const(inferred_node): 

575 inferred.append(inferred_node.value) 

576 return inferred 

577 

578 def public_names(self): 

579 """The list of the names that are publicly available in this module. 

580 

581 :returns: The list of public names. 

582 :rtype: list(str) 

583 """ 

584 return [name for name in self.keys() if not name.startswith("_")] 

585 

586 def bool_value(self, context: InferenceContext | None = None) -> bool: 

587 """Determine the boolean value of this node. 

588 

589 :returns: The boolean value of this node. 

590 For a :class:`Module` this is always ``True``. 

591 """ 

592 return True 

593 

594 def get_children(self): 

595 yield from self.body 

596 

597 def frame(self: _T, *, future: Literal[None, True] = None) -> _T: 

598 """The node's frame node. 

599 

600 A frame node is a :class:`Module`, :class:`FunctionDef`, 

601 :class:`ClassDef` or :class:`Lambda`. 

602 

603 :returns: The node itself. 

604 """ 

605 return self 

606 

607 def _infer( 

608 self, context: InferenceContext | None = None, **kwargs: Any 

609 ) -> Generator[Module]: 

610 yield self 

611 

612 

613class __SyntheticRoot(Module): 

614 def __init__(self): 

615 super().__init__("__astroid_synthetic", pure_python=False) 

616 

617 

618SYNTHETIC_ROOT = __SyntheticRoot() 

619 

620 

621class GeneratorExp(ComprehensionScope): 

622 """Class representing an :class:`ast.GeneratorExp` node. 

623 

624 >>> import astroid 

625 >>> node = astroid.extract_node('(thing for thing in things if thing)') 

626 >>> node 

627 <GeneratorExp l.1 at 0x7f23b2e4e400> 

628 """ 

629 

630 _astroid_fields = ("elt", "generators") 

631 _other_other_fields = ("locals",) 

632 elt: NodeNG 

633 """The element that forms the output of the expression.""" 

634 

635 def __init__( 

636 self, 

637 lineno: int, 

638 col_offset: int, 

639 parent: NodeNG, 

640 *, 

641 end_lineno: int | None, 

642 end_col_offset: int | None, 

643 ) -> None: 

644 self.locals = {} 

645 """A map of the name of a local variable to the node defining the local.""" 

646 

647 self.generators: list[nodes.Comprehension] = [] 

648 """The generators that are looped through.""" 

649 

650 super().__init__( 

651 lineno=lineno, 

652 col_offset=col_offset, 

653 end_lineno=end_lineno, 

654 end_col_offset=end_col_offset, 

655 parent=parent, 

656 ) 

657 

658 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]) -> None: 

659 self.elt = elt 

660 self.generators = generators 

661 

662 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: 

663 """Determine the boolean value of this node. 

664 

665 :returns: The boolean value of this node. 

666 For a :class:`GeneratorExp` this is always ``True``. 

667 """ 

668 return True 

669 

670 def get_children(self): 

671 yield self.elt 

672 

673 yield from self.generators 

674 

675 

676class DictComp(ComprehensionScope): 

677 """Class representing an :class:`ast.DictComp` node. 

678 

679 >>> import astroid 

680 >>> node = astroid.extract_node('{k:v for k, v in things if k > v}') 

681 >>> node 

682 <DictComp l.1 at 0x7f23b2e41d68> 

683 """ 

684 

685 _astroid_fields = ("key", "value", "generators") 

686 _other_other_fields = ("locals",) 

687 key: NodeNG 

688 """What produces the keys.""" 

689 

690 value: NodeNG 

691 """What produces the values.""" 

692 

693 def __init__( 

694 self, 

695 lineno: int, 

696 col_offset: int, 

697 parent: NodeNG, 

698 *, 

699 end_lineno: int | None, 

700 end_col_offset: int | None, 

701 ) -> None: 

702 self.locals = {} 

703 """A map of the name of a local variable to the node defining the local.""" 

704 

705 super().__init__( 

706 lineno=lineno, 

707 col_offset=col_offset, 

708 end_lineno=end_lineno, 

709 end_col_offset=end_col_offset, 

710 parent=parent, 

711 ) 

712 

713 def postinit( 

714 self, key: NodeNG, value: NodeNG, generators: list[nodes.Comprehension] 

715 ) -> None: 

716 self.key = key 

717 self.value = value 

718 self.generators = generators 

719 

720 def bool_value(self, context: InferenceContext | None = None): 

721 """Determine the boolean value of this node. 

722 

723 :returns: The boolean value of this node. 

724 For a :class:`DictComp` this is always :class:`Uninferable`. 

725 :rtype: Uninferable 

726 """ 

727 return util.Uninferable 

728 

729 def get_children(self): 

730 yield self.key 

731 yield self.value 

732 

733 yield from self.generators 

734 

735 

736class SetComp(ComprehensionScope): 

737 """Class representing an :class:`ast.SetComp` node. 

738 

739 >>> import astroid 

740 >>> node = astroid.extract_node('{thing for thing in things if thing}') 

741 >>> node 

742 <SetComp l.1 at 0x7f23b2e41898> 

743 """ 

744 

745 _astroid_fields = ("elt", "generators") 

746 _other_other_fields = ("locals",) 

747 elt: NodeNG 

748 """The element that forms the output of the expression.""" 

749 

750 def __init__( 

751 self, 

752 lineno: int, 

753 col_offset: int, 

754 parent: NodeNG, 

755 *, 

756 end_lineno: int | None, 

757 end_col_offset: int | None, 

758 ) -> None: 

759 self.locals = {} 

760 """A map of the name of a local variable to the node defining the local.""" 

761 

762 self.generators: list[nodes.Comprehension] = [] 

763 """The generators that are looped through.""" 

764 

765 super().__init__( 

766 lineno=lineno, 

767 col_offset=col_offset, 

768 end_lineno=end_lineno, 

769 end_col_offset=end_col_offset, 

770 parent=parent, 

771 ) 

772 

773 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]) -> None: 

774 self.elt = elt 

775 self.generators = generators 

776 

777 def bool_value(self, context: InferenceContext | None = None): 

778 """Determine the boolean value of this node. 

779 

780 :returns: The boolean value of this node. 

781 For a :class:`SetComp` this is always :class:`Uninferable`. 

782 :rtype: Uninferable 

783 """ 

784 return util.Uninferable 

785 

786 def get_children(self): 

787 yield self.elt 

788 

789 yield from self.generators 

790 

791 

792class ListComp(ComprehensionScope): 

793 """Class representing an :class:`ast.ListComp` node. 

794 

795 >>> import astroid 

796 >>> node = astroid.extract_node('[thing for thing in things if thing]') 

797 >>> node 

798 <ListComp l.1 at 0x7f23b2e418d0> 

799 """ 

800 

801 _astroid_fields = ("elt", "generators") 

802 _other_other_fields = ("locals",) 

803 

804 elt: NodeNG 

805 """The element that forms the output of the expression.""" 

806 

807 def __init__( 

808 self, 

809 lineno: int, 

810 col_offset: int, 

811 parent: NodeNG, 

812 *, 

813 end_lineno: int | None, 

814 end_col_offset: int | None, 

815 ) -> None: 

816 self.locals = {} 

817 """A map of the name of a local variable to the node defining it.""" 

818 

819 self.generators: list[nodes.Comprehension] = [] 

820 """The generators that are looped through.""" 

821 

822 super().__init__( 

823 lineno=lineno, 

824 col_offset=col_offset, 

825 end_lineno=end_lineno, 

826 end_col_offset=end_col_offset, 

827 parent=parent, 

828 ) 

829 

830 def postinit(self, elt: NodeNG, generators: list[nodes.Comprehension]): 

831 self.elt = elt 

832 self.generators = generators 

833 

834 def bool_value(self, context: InferenceContext | None = None): 

835 """Determine the boolean value of this node. 

836 

837 :returns: The boolean value of this node. 

838 For a :class:`ListComp` this is always :class:`Uninferable`. 

839 :rtype: Uninferable 

840 """ 

841 return util.Uninferable 

842 

843 def get_children(self): 

844 yield self.elt 

845 

846 yield from self.generators 

847 

848 

849def _infer_decorator_callchain(node): 

850 """Detect decorator call chaining and see if the end result is a 

851 static or a classmethod. 

852 """ 

853 if not isinstance(node, FunctionDef): 

854 return None 

855 if not node.parent: 

856 return None 

857 try: 

858 result = next(node.infer_call_result(node.parent), None) 

859 except InferenceError: 

860 return None 

861 if isinstance(result, bases.Instance): 

862 result = result._proxied 

863 if isinstance(result, ClassDef): 

864 if result.is_subtype_of("builtins.classmethod"): 

865 return "classmethod" 

866 if result.is_subtype_of("builtins.staticmethod"): 

867 return "staticmethod" 

868 if isinstance(result, FunctionDef): 

869 if not result.decorators: 

870 return None 

871 # Determine if this function is decorated with one of the builtin descriptors we want. 

872 for decorator in result.decorators.nodes: 

873 if isinstance(decorator, node_classes.Name): 

874 if decorator.name in BUILTIN_DESCRIPTORS: 

875 return decorator.name 

876 if ( 

877 isinstance(decorator, node_classes.Attribute) 

878 and isinstance(decorator.expr, node_classes.Name) 

879 and decorator.expr.name == "builtins" 

880 and decorator.attrname in BUILTIN_DESCRIPTORS 

881 ): 

882 return decorator.attrname 

883 return None 

884 

885 

886class Lambda(_base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG): 

887 """Class representing an :class:`ast.Lambda` node. 

888 

889 >>> import astroid 

890 >>> node = astroid.extract_node('lambda arg: arg + 1') 

891 >>> node 

892 <Lambda.<lambda> l.1 at 0x7f23b2e41518> 

893 """ 

894 

895 _astroid_fields: ClassVar[tuple[str, ...]] = ("args", "body") 

896 _other_other_fields: ClassVar[tuple[str, ...]] = ("locals",) 

897 name = "<lambda>" 

898 is_lambda = True 

899 special_attributes = FunctionModel() 

900 """The names of special attributes that this function has.""" 

901 

902 args: Arguments 

903 """The arguments that the function takes.""" 

904 

905 body: NodeNG 

906 """The contents of the function body.""" 

907 

908 def implicit_parameters(self) -> Literal[0]: 

909 return 0 

910 

911 @property 

912 def type(self) -> Literal["method", "function"]: 

913 """Whether this is a method or function. 

914 

915 :returns: 'method' if this is a method, 'function' otherwise. 

916 """ 

917 if self.args.arguments and self.args.arguments[0].name == "self": 

918 if self.parent and isinstance(self.parent.scope(), ClassDef): 

919 return "method" 

920 return "function" 

921 

922 def __init__( 

923 self, 

924 lineno: int, 

925 col_offset: int, 

926 parent: NodeNG, 

927 *, 

928 end_lineno: int | None, 

929 end_col_offset: int | None, 

930 ): 

931 self.locals = {} 

932 """A map of the name of a local variable to the node defining it.""" 

933 

934 self.instance_attrs: dict[str, list[NodeNG]] = {} 

935 

936 super().__init__( 

937 lineno=lineno, 

938 col_offset=col_offset, 

939 end_lineno=end_lineno, 

940 end_col_offset=end_col_offset, 

941 parent=parent, 

942 ) 

943 

944 def postinit(self, args: Arguments, body: NodeNG) -> None: 

945 self.args = args 

946 self.body = body 

947 

948 def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]: 

949 """Get the name of the type that this node represents. 

950 

951 :returns: The name of the type. 

952 """ 

953 if "method" in self.type: 

954 return "builtins.instancemethod" 

955 return "builtins.function" 

956 

957 def display_type(self) -> str: 

958 """A human readable type of this node. 

959 

960 :returns: The type of this node. 

961 :rtype: str 

962 """ 

963 if "method" in self.type: 

964 return "Method" 

965 return "Function" 

966 

967 def callable(self) -> Literal[True]: 

968 """Whether this node defines something that is callable. 

969 

970 :returns: Whether this defines something that is callable 

971 For a :class:`Lambda` this is always ``True``. 

972 """ 

973 return True 

974 

975 def argnames(self) -> list[str]: 

976 """Get the names of each of the arguments, including that 

977 of the collections of variable-length arguments ("args", "kwargs", 

978 etc.), as well as positional-only and keyword-only arguments. 

979 

980 :returns: The names of the arguments. 

981 :rtype: list(str) 

982 """ 

983 if self.args.arguments: # maybe None with builtin functions 

984 names = [elt.name for elt in self.args.arguments] 

985 else: 

986 names = [] 

987 

988 return names 

989 

990 def infer_call_result( 

991 self, 

992 caller: SuccessfulInferenceResult | None, 

993 context: InferenceContext | None = None, 

994 ) -> Iterator[InferenceResult]: 

995 """Infer what the function returns when called.""" 

996 return self.body.infer(context) 

997 

998 def scope_lookup( 

999 self, node: LookupMixIn, name: str, offset: int = 0 

1000 ) -> tuple[LocalsDictNodeNG, list[NodeNG]]: 

1001 """Lookup where the given names is assigned. 

1002 

1003 :param node: The node to look for assignments up to. 

1004 Any assignments after the given node are ignored. 

1005 

1006 :param name: The name to find assignments for. 

1007 

1008 :param offset: The line offset to filter statements up to. 

1009 

1010 :returns: This scope node and the list of assignments associated to the 

1011 given name according to the scope where it has been found (locals, 

1012 globals or builtin). 

1013 """ 

1014 if (self.args.defaults and node in self.args.defaults) or ( 

1015 self.args.kw_defaults and node in self.args.kw_defaults 

1016 ): 

1017 if not self.parent: 

1018 raise ParentMissingError(target=self) 

1019 frame = self.parent.frame() 

1020 # line offset to avoid that def func(f=func) resolve the default 

1021 # value to the defined function 

1022 offset = -1 

1023 else: 

1024 # check this is not used in function decorators 

1025 frame = self 

1026 return frame._scope_lookup(node, name, offset) 

1027 

1028 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: 

1029 """Determine the boolean value of this node. 

1030 

1031 :returns: The boolean value of this node. 

1032 For a :class:`Lambda` this is always ``True``. 

1033 """ 

1034 return True 

1035 

1036 def get_children(self): 

1037 yield self.args 

1038 yield self.body 

1039 

1040 def frame(self: _T, *, future: Literal[None, True] = None) -> _T: 

1041 """The node's frame node. 

1042 

1043 A frame node is a :class:`Module`, :class:`FunctionDef`, 

1044 :class:`ClassDef` or :class:`Lambda`. 

1045 

1046 :returns: The node itself. 

1047 """ 

1048 return self 

1049 

1050 def getattr( 

1051 self, name: str, context: InferenceContext | None = None 

1052 ) -> list[NodeNG]: 

1053 if not name: 

1054 raise AttributeInferenceError(target=self, attribute=name, context=context) 

1055 

1056 found_attrs = [] 

1057 if name in self.instance_attrs: 

1058 found_attrs = self.instance_attrs[name] 

1059 if name in self.special_attributes: 

1060 found_attrs.append(self.special_attributes.lookup(name)) 

1061 if found_attrs: 

1062 return found_attrs 

1063 raise AttributeInferenceError(target=self, attribute=name) 

1064 

1065 def _infer( 

1066 self, context: InferenceContext | None = None, **kwargs: Any 

1067 ) -> Generator[Lambda]: 

1068 yield self 

1069 

1070 def _get_yield_nodes_skip_functions(self): 

1071 """A Lambda node can contain a Yield node in the body.""" 

1072 yield from self.body._get_yield_nodes_skip_functions() 

1073 

1074 

1075class FunctionDef( 

1076 _base_nodes.MultiLineBlockNode, 

1077 _base_nodes.FilterStmtsBaseNode, 

1078 _base_nodes.Statement, 

1079 LocalsDictNodeNG, 

1080): 

1081 """Class representing an :class:`ast.FunctionDef`. 

1082 

1083 >>> import astroid 

1084 >>> node = astroid.extract_node(''' 

1085 ... def my_func(arg): 

1086 ... return arg + 1 

1087 ... ''') 

1088 >>> node 

1089 <FunctionDef.my_func l.2 at 0x7f23b2e71e10> 

1090 """ 

1091 

1092 _astroid_fields = ( 

1093 "decorators", 

1094 "args", 

1095 "returns", 

1096 "type_params", 

1097 "doc_node", 

1098 "body", 

1099 ) 

1100 _multi_line_block_fields = ("body",) 

1101 returns = None 

1102 

1103 decorators: node_classes.Decorators | None 

1104 """The decorators that are applied to this method or function.""" 

1105 

1106 doc_node: Const | None 

1107 """The doc node associated with this node.""" 

1108 

1109 args: Arguments 

1110 """The arguments that the function takes.""" 

1111 

1112 is_function = True 

1113 """Whether this node indicates a function. 

1114 

1115 For a :class:`FunctionDef` this is always ``True``. 

1116 

1117 :type: bool 

1118 """ 

1119 type_annotation = None 

1120 """If present, this will contain the type annotation passed by a type comment 

1121 

1122 :type: NodeNG or None 

1123 """ 

1124 type_comment_args = None 

1125 """ 

1126 If present, this will contain the type annotation for arguments 

1127 passed by a type comment 

1128 """ 

1129 type_comment_returns = None 

1130 """If present, this will contain the return type annotation, passed by a type comment""" 

1131 # attributes below are set by the builder module or by raw factories 

1132 _other_fields = ("name", "position") 

1133 _other_other_fields = ( 

1134 "locals", 

1135 "_type", 

1136 "type_comment_returns", 

1137 "type_comment_args", 

1138 ) 

1139 _type = None 

1140 

1141 name = "<functiondef>" 

1142 

1143 special_attributes = FunctionModel() 

1144 """The names of special attributes that this function has.""" 

1145 

1146 def __init__( 

1147 self, 

1148 name: str, 

1149 lineno: int, 

1150 col_offset: int, 

1151 parent: NodeNG, 

1152 *, 

1153 end_lineno: int | None, 

1154 end_col_offset: int | None, 

1155 ) -> None: 

1156 self.name = name 

1157 """The name of the function.""" 

1158 

1159 self.locals = {} 

1160 """A map of the name of a local variable to the node defining it.""" 

1161 

1162 self.body: list[NodeNG] = [] 

1163 """The contents of the function body.""" 

1164 

1165 self.type_params: list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] = ( 

1166 [] 

1167 ) 

1168 """PEP 695 (Python 3.12+) type params, e.g. first 'T' in def func[T]() -> T: ...""" 

1169 

1170 self.instance_attrs: dict[str, list[NodeNG]] = {} 

1171 

1172 super().__init__( 

1173 lineno=lineno, 

1174 col_offset=col_offset, 

1175 end_lineno=end_lineno, 

1176 end_col_offset=end_col_offset, 

1177 parent=parent, 

1178 ) 

1179 

1180 def postinit( 

1181 self, 

1182 args: Arguments, 

1183 body: list[NodeNG], 

1184 decorators: node_classes.Decorators | None = None, 

1185 returns=None, 

1186 type_comment_returns=None, 

1187 type_comment_args=None, 

1188 *, 

1189 position: Position | None = None, 

1190 doc_node: Const | None = None, 

1191 type_params: ( 

1192 list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] | None 

1193 ) = None, 

1194 ): 

1195 """Do some setup after initialisation. 

1196 

1197 :param args: The arguments that the function takes. 

1198 

1199 :param body: The contents of the function body. 

1200 

1201 :param decorators: The decorators that are applied to this 

1202 method or function. 

1203 :params type_comment_returns: 

1204 The return type annotation passed via a type comment. 

1205 :params type_comment_args: 

1206 The args type annotation passed via a type comment. 

1207 :params position: 

1208 Position of function keyword(s) and name. 

1209 :param doc_node: 

1210 The doc node associated with this node. 

1211 :param type_params: 

1212 The type_params associated with this node. 

1213 """ 

1214 self.args = args 

1215 self.body = body 

1216 self.decorators = decorators 

1217 self.returns = returns 

1218 self.type_comment_returns = type_comment_returns 

1219 self.type_comment_args = type_comment_args 

1220 self.position = position 

1221 self.doc_node = doc_node 

1222 self.type_params = type_params or [] 

1223 

1224 @cached_property 

1225 def extra_decorators(self) -> list[node_classes.Call]: 

1226 """The extra decorators that this function can have. 

1227 

1228 Additional decorators are considered when they are used as 

1229 assignments, as in ``method = staticmethod(method)``. 

1230 The property will return all the callables that are used for 

1231 decoration. 

1232 """ 

1233 if not self.parent or not isinstance(frame := self.parent.frame(), ClassDef): 

1234 return [] 

1235 

1236 decorators: list[node_classes.Call] = [] 

1237 for assign in frame._assign_nodes_in_scope: 

1238 if isinstance(assign.value, node_classes.Call) and isinstance( 

1239 assign.value.func, node_classes.Name 

1240 ): 

1241 for assign_node in assign.targets: 

1242 if not isinstance(assign_node, node_classes.AssignName): 

1243 # Support only `name = callable(name)` 

1244 continue 

1245 

1246 if assign_node.name != self.name: 

1247 # Interested only in the assignment nodes that 

1248 # decorates the current method. 

1249 continue 

1250 try: 

1251 meth = frame[self.name] 

1252 except KeyError: 

1253 continue 

1254 else: 

1255 # Must be a function and in the same frame as the 

1256 # original method. 

1257 if ( 

1258 isinstance(meth, FunctionDef) 

1259 and assign_node.frame() == frame 

1260 ): 

1261 decorators.append(assign.value) 

1262 return decorators 

1263 

1264 def pytype(self) -> Literal["builtins.instancemethod", "builtins.function"]: 

1265 """Get the name of the type that this node represents. 

1266 

1267 :returns: The name of the type. 

1268 """ 

1269 if "method" in self.type: 

1270 return "builtins.instancemethod" 

1271 return "builtins.function" 

1272 

1273 def display_type(self) -> str: 

1274 """A human readable type of this node. 

1275 

1276 :returns: The type of this node. 

1277 :rtype: str 

1278 """ 

1279 if "method" in self.type: 

1280 return "Method" 

1281 return "Function" 

1282 

1283 def callable(self) -> Literal[True]: 

1284 return True 

1285 

1286 def argnames(self) -> list[str]: 

1287 """Get the names of each of the arguments, including that 

1288 of the collections of variable-length arguments ("args", "kwargs", 

1289 etc.), as well as positional-only and keyword-only arguments. 

1290 

1291 :returns: The names of the arguments. 

1292 :rtype: list(str) 

1293 """ 

1294 if self.args.arguments: # maybe None with builtin functions 

1295 names = [elt.name for elt in self.args.arguments] 

1296 else: 

1297 names = [] 

1298 

1299 return names 

1300 

1301 def getattr( 

1302 self, name: str, context: InferenceContext | None = None 

1303 ) -> list[NodeNG]: 

1304 if not name: 

1305 raise AttributeInferenceError(target=self, attribute=name, context=context) 

1306 

1307 found_attrs = [] 

1308 if name in self.instance_attrs: 

1309 found_attrs = self.instance_attrs[name] 

1310 if name in self.special_attributes: 

1311 found_attrs.append(self.special_attributes.lookup(name)) 

1312 if found_attrs: 

1313 return found_attrs 

1314 raise AttributeInferenceError(target=self, attribute=name) 

1315 

1316 @cached_property 

1317 def type(self) -> str: # pylint: disable=too-many-return-statements # noqa: C901 

1318 """The function type for this node. 

1319 

1320 Possible values are: method, function, staticmethod, classmethod. 

1321 """ 

1322 for decorator in self.extra_decorators: 

1323 if decorator.func.name in BUILTIN_DESCRIPTORS: 

1324 return decorator.func.name 

1325 

1326 if not self.parent: 

1327 raise ParentMissingError(target=self) 

1328 

1329 frame = self.parent.frame() 

1330 type_name = "function" 

1331 if isinstance(frame, ClassDef): 

1332 if self.name == "__new__": 

1333 return "classmethod" 

1334 if self.name == "__init_subclass__": 

1335 return "classmethod" 

1336 if self.name == "__class_getitem__": 

1337 return "classmethod" 

1338 

1339 type_name = "method" 

1340 

1341 if not self.decorators: 

1342 return type_name 

1343 

1344 for node in self.decorators.nodes: 

1345 if isinstance(node, node_classes.Name): 

1346 if node.name in BUILTIN_DESCRIPTORS: 

1347 return node.name 

1348 if ( 

1349 isinstance(node, node_classes.Attribute) 

1350 and isinstance(node.expr, node_classes.Name) 

1351 and node.expr.name == "builtins" 

1352 and node.attrname in BUILTIN_DESCRIPTORS 

1353 ): 

1354 return node.attrname 

1355 

1356 if isinstance(node, node_classes.Call): 

1357 # Handle the following case: 

1358 # @some_decorator(arg1, arg2) 

1359 # def func(...) 

1360 # 

1361 try: 

1362 current = next(node.func.infer()) 

1363 except (InferenceError, StopIteration): 

1364 continue 

1365 _type = _infer_decorator_callchain(current) 

1366 if _type is not None: 

1367 return _type 

1368 

1369 try: 

1370 for inferred in node.infer(): 

1371 # Check to see if this returns a static or a class method. 

1372 _type = _infer_decorator_callchain(inferred) 

1373 if _type is not None: 

1374 return _type 

1375 

1376 if not isinstance(inferred, ClassDef): 

1377 continue 

1378 for ancestor in inferred.ancestors(): 

1379 if not isinstance(ancestor, ClassDef): 

1380 continue 

1381 if ancestor.is_subtype_of("builtins.classmethod"): 

1382 return "classmethod" 

1383 if ancestor.is_subtype_of("builtins.staticmethod"): 

1384 return "staticmethod" 

1385 except InferenceError: 

1386 pass 

1387 return type_name 

1388 

1389 @cached_property 

1390 def fromlineno(self) -> int: 

1391 """The first line that this node appears on in the source code. 

1392 

1393 Can also return 0 if the line can not be determined. 

1394 """ 

1395 # lineno is the line number of the first decorator, we want the def 

1396 # statement lineno. Similar to 'ClassDef.fromlineno' 

1397 lineno = self.lineno or 0 

1398 if self.decorators is not None: 

1399 lineno += sum( 

1400 node.tolineno - (node.lineno or 0) + 1 for node in self.decorators.nodes 

1401 ) 

1402 

1403 return lineno or 0 

1404 

1405 @cached_property 

1406 def blockstart_tolineno(self): 

1407 """The line on which the beginning of this block ends. 

1408 

1409 :type: int 

1410 """ 

1411 return self.args.tolineno 

1412 

1413 def implicit_parameters(self) -> Literal[0, 1]: 

1414 return 1 if self.is_bound() else 0 

1415 

1416 def block_range(self, lineno: int) -> tuple[int, int]: 

1417 """Get a range from the given line number to where this node ends. 

1418 

1419 :param lineno: Unused. 

1420 

1421 :returns: The range of line numbers that this node belongs to, 

1422 """ 

1423 return self.fromlineno, self.tolineno 

1424 

1425 def igetattr( 

1426 self, name: str, context: InferenceContext | None = None 

1427 ) -> Iterator[InferenceResult]: 

1428 """Inferred getattr, which returns an iterator of inferred statements.""" 

1429 try: 

1430 return bases._infer_stmts(self.getattr(name, context), context, frame=self) 

1431 except AttributeInferenceError as error: 

1432 raise InferenceError( 

1433 str(error), target=self, attribute=name, context=context 

1434 ) from error 

1435 

1436 def is_method(self) -> bool: 

1437 """Check if this function node represents a method. 

1438 

1439 :returns: Whether this is a method. 

1440 """ 

1441 # check we are defined in a ClassDef, because this is usually expected 

1442 # (e.g. pylint...) when is_method() return True 

1443 return ( 

1444 self.type != "function" 

1445 and self.parent is not None 

1446 and isinstance(self.parent.frame(), ClassDef) 

1447 ) 

1448 

1449 def decoratornames(self, context: InferenceContext | None = None) -> set[str]: 

1450 """Get the qualified names of each of the decorators on this function. 

1451 

1452 :param context: 

1453 An inference context that can be passed to inference functions 

1454 :returns: The names of the decorators. 

1455 """ 

1456 result = set() 

1457 decoratornodes = [] 

1458 if self.decorators is not None: 

1459 decoratornodes += self.decorators.nodes 

1460 decoratornodes += self.extra_decorators 

1461 for decnode in decoratornodes: 

1462 try: 

1463 for infnode in decnode.infer(context=context): 

1464 result.add(infnode.qname()) 

1465 except InferenceError: 

1466 continue 

1467 return result 

1468 

1469 def is_bound(self) -> bool: 

1470 """Check if the function is bound to an instance or class. 

1471 

1472 :returns: Whether the function is bound to an instance or class. 

1473 """ 

1474 return self.type in {"method", "classmethod"} 

1475 

1476 def is_abstract(self, pass_is_abstract=True, any_raise_is_abstract=False) -> bool: 

1477 """Check if the method is abstract. 

1478 

1479 A method is considered abstract if any of the following is true: 

1480 * The only statement is 'raise NotImplementedError' 

1481 * The only statement is 'raise <SomeException>' and any_raise_is_abstract is True 

1482 * The only statement is 'pass' and pass_is_abstract is True 

1483 * The method is annotated with abc.astractproperty/abc.abstractmethod 

1484 

1485 :returns: Whether the method is abstract. 

1486 """ 

1487 if self.decorators: 

1488 for node in self.decorators.nodes: 

1489 try: 

1490 inferred = next(node.infer()) 

1491 except (InferenceError, StopIteration): 

1492 continue 

1493 if inferred and inferred.qname() in { 

1494 "abc.abstractproperty", 

1495 "abc.abstractmethod", 

1496 }: 

1497 return True 

1498 

1499 for child_node in self.body: 

1500 if isinstance(child_node, node_classes.Raise): 

1501 if any_raise_is_abstract: 

1502 return True 

1503 if child_node.raises_not_implemented(): 

1504 return True 

1505 return pass_is_abstract and isinstance(child_node, node_classes.Pass) 

1506 # empty function is the same as function with a single "pass" statement 

1507 if pass_is_abstract: 

1508 return True 

1509 

1510 return False 

1511 

1512 def is_generator(self) -> bool: 

1513 """Check if this is a generator function. 

1514 

1515 :returns: Whether this is a generator function. 

1516 """ 

1517 yields_without_lambdas = set(self._get_yield_nodes_skip_lambdas()) 

1518 yields_without_functions = set(self._get_yield_nodes_skip_functions()) 

1519 # Want an intersecting member that is neither in a lambda nor a function 

1520 return bool(yields_without_lambdas & yields_without_functions) 

1521 

1522 def _infer( 

1523 self, context: InferenceContext | None = None, **kwargs: Any 

1524 ) -> Generator[objects.Property | FunctionDef, None, InferenceErrorInfo]: 

1525 from astroid import objects # pylint: disable=import-outside-toplevel 

1526 

1527 if not self.decorators or not bases._is_property(self): 

1528 yield self 

1529 return InferenceErrorInfo(node=self, context=context) 

1530 

1531 if not self.parent: 

1532 raise ParentMissingError(target=self) 

1533 prop_func = objects.Property( 

1534 function=self, 

1535 name=self.name, 

1536 lineno=self.lineno, 

1537 parent=self.parent, 

1538 col_offset=self.col_offset, 

1539 ) 

1540 prop_func.postinit(body=[], args=self.args, doc_node=self.doc_node) 

1541 yield prop_func 

1542 return InferenceErrorInfo(node=self, context=context) 

1543 

1544 def infer_yield_result(self, context: InferenceContext | None = None): 

1545 """Infer what the function yields when called 

1546 

1547 :returns: What the function yields 

1548 :rtype: iterable(NodeNG or Uninferable) or None 

1549 """ 

1550 for yield_ in self.nodes_of_class(node_classes.Yield): 

1551 if yield_.value is None: 

1552 yield node_classes.Const(None, parent=yield_, lineno=yield_.lineno) 

1553 elif yield_.scope() == self: 

1554 yield from yield_.value.infer(context=context) 

1555 

1556 def infer_call_result( 

1557 self, 

1558 caller: SuccessfulInferenceResult | None, 

1559 context: InferenceContext | None = None, 

1560 ) -> Iterator[InferenceResult]: 

1561 """Infer what the function returns when called.""" 

1562 if context is None: 

1563 context = InferenceContext() 

1564 if self.is_generator(): 

1565 if isinstance(self, AsyncFunctionDef): 

1566 generator_cls: type[bases.Generator] = bases.AsyncGenerator 

1567 else: 

1568 generator_cls = bases.Generator 

1569 result = generator_cls(self, generator_initial_context=context) 

1570 yield result 

1571 return 

1572 # This is really a gigantic hack to work around metaclass generators 

1573 # that return transient class-generating functions. Pylint's AST structure 

1574 # cannot handle a base class object that is only used for calling __new__, 

1575 # but does not contribute to the inheritance structure itself. We inject 

1576 # a fake class into the hierarchy here for several well-known metaclass 

1577 # generators, and filter it out later. 

1578 if ( 

1579 self.name == "with_metaclass" 

1580 and caller is not None 

1581 and self.args.args 

1582 and len(self.args.args) == 1 

1583 and self.args.vararg is not None 

1584 ): 

1585 if isinstance(caller.args, node_classes.Arguments): 

1586 assert caller.args.args is not None 

1587 metaclass = next(caller.args.args[0].infer(context), None) 

1588 elif isinstance(caller.args, list): 

1589 metaclass = next(caller.args[0].infer(context), None) 

1590 else: 

1591 raise TypeError( # pragma: no cover 

1592 f"caller.args was neither Arguments nor list; got {type(caller.args)}" 

1593 ) 

1594 if isinstance(metaclass, ClassDef): 

1595 class_bases = [_infer_last(x, context) for x in caller.args[1:]] 

1596 new_class = ClassDef( 

1597 name="temporary_class", 

1598 lineno=0, 

1599 col_offset=0, 

1600 end_lineno=0, 

1601 end_col_offset=0, 

1602 parent=SYNTHETIC_ROOT, 

1603 ) 

1604 new_class.hide = True 

1605 new_class.postinit( 

1606 bases=[ 

1607 base 

1608 for base in class_bases 

1609 if not isinstance(base, util.UninferableBase) 

1610 ], 

1611 body=[], 

1612 decorators=None, 

1613 metaclass=metaclass, 

1614 ) 

1615 yield new_class 

1616 return 

1617 returns = self._get_return_nodes_skip_functions() 

1618 

1619 first_return = next(returns, None) 

1620 if not first_return: 

1621 if self.body: 

1622 if self.is_abstract(pass_is_abstract=True, any_raise_is_abstract=True): 

1623 yield util.Uninferable 

1624 else: 

1625 yield node_classes.Const(None) 

1626 return 

1627 

1628 raise InferenceError("The function does not have any return statements") 

1629 

1630 for returnnode in itertools.chain((first_return,), returns): 

1631 if returnnode.value is None: 

1632 yield node_classes.Const(None) 

1633 else: 

1634 try: 

1635 yield from returnnode.value.infer(context) 

1636 except InferenceError: 

1637 yield util.Uninferable 

1638 

1639 def bool_value(self, context: InferenceContext | None = None) -> bool: 

1640 """Determine the boolean value of this node. 

1641 

1642 :returns: The boolean value of this node. 

1643 For a :class:`FunctionDef` this is always ``True``. 

1644 """ 

1645 return True 

1646 

1647 def get_children(self): 

1648 if self.decorators is not None: 

1649 yield self.decorators 

1650 

1651 yield self.args 

1652 

1653 if self.returns is not None: 

1654 yield self.returns 

1655 yield from self.type_params 

1656 

1657 yield from self.body 

1658 

1659 def scope_lookup( 

1660 self, node: LookupMixIn, name: str, offset: int = 0 

1661 ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]: 

1662 """Lookup where the given name is assigned.""" 

1663 if name == "__class__": 

1664 # __class__ is an implicit closure reference created by the compiler 

1665 # if any methods in a class body refer to either __class__ or super. 

1666 # In our case, we want to be able to look it up in the current scope 

1667 # when `__class__` is being used. 

1668 if self.parent and isinstance(frame := self.parent.frame(), ClassDef): 

1669 return self, [frame] 

1670 

1671 if (self.args.defaults and node in self.args.defaults) or ( 

1672 self.args.kw_defaults and node in self.args.kw_defaults 

1673 ): 

1674 if not self.parent: 

1675 raise ParentMissingError(target=self) 

1676 frame = self.parent.frame() 

1677 # line offset to avoid that def func(f=func) resolve the default 

1678 # value to the defined function 

1679 offset = -1 

1680 else: 

1681 # check this is not used in function decorators 

1682 frame = self 

1683 return frame._scope_lookup(node, name, offset) 

1684 

1685 def frame(self: _T, *, future: Literal[None, True] = None) -> _T: 

1686 """The node's frame node. 

1687 

1688 A frame node is a :class:`Module`, :class:`FunctionDef`, 

1689 :class:`ClassDef` or :class:`Lambda`. 

1690 

1691 :returns: The node itself. 

1692 """ 

1693 return self 

1694 

1695 

1696class AsyncFunctionDef(FunctionDef): 

1697 """Class representing an :class:`ast.FunctionDef` node. 

1698 

1699 A :class:`AsyncFunctionDef` is an asynchronous function 

1700 created with the `async` keyword. 

1701 

1702 >>> import astroid 

1703 >>> node = astroid.extract_node(''' 

1704 async def func(things): 

1705 async for thing in things: 

1706 print(thing) 

1707 ''') 

1708 >>> node 

1709 <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8> 

1710 >>> node.body[0] 

1711 <AsyncFor l.3 at 0x7f23b2e417b8> 

1712 """ 

1713 

1714 

1715def _is_metaclass( 

1716 klass: ClassDef, 

1717 seen: set[str] | None = None, 

1718 context: InferenceContext | None = None, 

1719) -> bool: 

1720 """Return if the given class can be 

1721 used as a metaclass. 

1722 """ 

1723 if klass.name == "type": 

1724 return True 

1725 if seen is None: 

1726 seen = set() 

1727 for base in klass.bases: 

1728 try: 

1729 for baseobj in base.infer(context=context): 

1730 baseobj_name = baseobj.qname() 

1731 if baseobj_name in seen: 

1732 continue 

1733 

1734 seen.add(baseobj_name) 

1735 if isinstance(baseobj, bases.Instance): 

1736 # not abstract 

1737 return False 

1738 if baseobj is klass: 

1739 continue 

1740 if not isinstance(baseobj, ClassDef): 

1741 continue 

1742 if baseobj._type == "metaclass": 

1743 return True 

1744 if _is_metaclass(baseobj, seen, context=context): 

1745 return True 

1746 except InferenceError: 

1747 continue 

1748 return False 

1749 

1750 

1751def _class_type( 

1752 klass: ClassDef, 

1753 ancestors: set[str] | None = None, 

1754 context: InferenceContext | None = None, 

1755) -> Literal["class", "exception", "metaclass"]: 

1756 """return a ClassDef node type to differ metaclass and exception 

1757 from 'regular' classes 

1758 """ 

1759 # XXX we have to store ancestors in case we have an ancestor loop 

1760 if klass._type is not None: 

1761 return klass._type 

1762 if _is_metaclass(klass, context=context): 

1763 klass._type = "metaclass" 

1764 elif klass.name.endswith("Exception"): 

1765 klass._type = "exception" 

1766 else: 

1767 if ancestors is None: 

1768 ancestors = set() 

1769 klass_name = klass.qname() 

1770 if klass_name in ancestors: 

1771 # XXX we are in loop ancestors, and have found no type 

1772 klass._type = "class" 

1773 return "class" 

1774 ancestors.add(klass_name) 

1775 for base in klass.ancestors(recurs=False): 

1776 name = _class_type(base, ancestors) 

1777 if name != "class": 

1778 if name == "metaclass" and klass._type != "metaclass": 

1779 # don't propagate it if the current class 

1780 # can't be a metaclass 

1781 continue 

1782 klass._type = base.type 

1783 break 

1784 if klass._type is None: 

1785 klass._type = "class" 

1786 return klass._type 

1787 

1788 

1789def get_wrapping_class(node): 

1790 """Get the class that wraps the given node. 

1791 

1792 We consider that a class wraps a node if the class 

1793 is a parent for the said node. 

1794 

1795 :returns: The class that wraps the given node 

1796 :rtype: ClassDef or None 

1797 """ 

1798 

1799 klass = node.frame() 

1800 while klass is not None and not isinstance(klass, ClassDef): 

1801 if klass.parent is None: 

1802 klass = None 

1803 else: 

1804 klass = klass.parent.frame() 

1805 return klass 

1806 

1807 

1808class ClassDef( 

1809 _base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG, _base_nodes.Statement 

1810): 

1811 """Class representing an :class:`ast.ClassDef` node. 

1812 

1813 >>> import astroid 

1814 >>> node = astroid.extract_node(''' 

1815 class Thing: 

1816 def my_meth(self, arg): 

1817 return arg + self.offset 

1818 ''') 

1819 >>> node 

1820 <ClassDef.Thing l.2 at 0x7f23b2e9e748> 

1821 """ 

1822 

1823 # some of the attributes below are set by the builder module or 

1824 # by a raw factories 

1825 

1826 # a dictionary of class instances attributes 

1827 _astroid_fields = ( 

1828 "decorators", 

1829 "bases", 

1830 "keywords", 

1831 "doc_node", 

1832 "body", 

1833 "type_params", 

1834 ) # name 

1835 

1836 decorators = None 

1837 """The decorators that are applied to this class. 

1838 

1839 :type: Decorators or None 

1840 """ 

1841 special_attributes = ClassModel() 

1842 """The names of special attributes that this class has. 

1843 

1844 :type: objectmodel.ClassModel 

1845 """ 

1846 

1847 _type: Literal["class", "exception", "metaclass"] | None = None 

1848 _metaclass: NodeNG | None = None 

1849 _metaclass_hack = False 

1850 hide = False 

1851 type = property( 

1852 _class_type, 

1853 doc=( 

1854 "The class type for this node.\n\n" 

1855 "Possible values are: class, metaclass, exception.\n\n" 

1856 ":type: str" 

1857 ), 

1858 ) 

1859 _other_fields = ("name", "is_dataclass", "position") 

1860 _other_other_fields = "locals" 

1861 

1862 def __init__( 

1863 self, 

1864 name: str, 

1865 lineno: int, 

1866 col_offset: int, 

1867 parent: NodeNG, 

1868 *, 

1869 end_lineno: int | None, 

1870 end_col_offset: int | None, 

1871 ) -> None: 

1872 self.instance_attrs: dict[str, NodeNG] = {} 

1873 self.locals = {} 

1874 """A map of the name of a local variable to the node defining it.""" 

1875 

1876 self.keywords: list[node_classes.Keyword] = [] 

1877 """The keywords given to the class definition. 

1878 

1879 This is usually for :pep:`3115` style metaclass declaration. 

1880 """ 

1881 

1882 self.bases: list[SuccessfulInferenceResult] = [] 

1883 """What the class inherits from.""" 

1884 

1885 self.body: list[NodeNG] = [] 

1886 """The contents of the class body.""" 

1887 

1888 self.name = name 

1889 """The name of the class.""" 

1890 

1891 self.decorators = None 

1892 """The decorators that are applied to this class.""" 

1893 

1894 self.doc_node: Const | None = None 

1895 """The doc node associated with this node.""" 

1896 

1897 self.is_dataclass: bool = False 

1898 """Whether this class is a dataclass.""" 

1899 

1900 self.type_params: list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] = ( 

1901 [] 

1902 ) 

1903 """PEP 695 (Python 3.12+) type params, e.g. class MyClass[T]: ...""" 

1904 

1905 super().__init__( 

1906 lineno=lineno, 

1907 col_offset=col_offset, 

1908 end_lineno=end_lineno, 

1909 end_col_offset=end_col_offset, 

1910 parent=parent, 

1911 ) 

1912 for local_name, node in self.implicit_locals(): 

1913 self.add_local_node(node, local_name) 

1914 

1915 infer_binary_op: ClassVar[InferBinaryOp[ClassDef]] = ( 

1916 protocols.instance_class_infer_binary_op 

1917 ) 

1918 

1919 def implicit_parameters(self) -> Literal[1]: 

1920 return 1 

1921 

1922 def implicit_locals(self): 

1923 """Get implicitly defined class definition locals. 

1924 

1925 :returns: the the name and Const pair for each local 

1926 :rtype: tuple(tuple(str, node_classes.Const), ...) 

1927 """ 

1928 locals_ = (("__module__", self.special_attributes.attr___module__),) 

1929 # __qualname__ is defined in PEP3155 

1930 locals_ += ( 

1931 ("__qualname__", self.special_attributes.attr___qualname__), 

1932 ("__annotations__", self.special_attributes.attr___annotations__), 

1933 ) 

1934 return locals_ 

1935 

1936 # pylint: disable=redefined-outer-name 

1937 def postinit( 

1938 self, 

1939 bases: list[SuccessfulInferenceResult], 

1940 body: list[NodeNG], 

1941 decorators: node_classes.Decorators | None, 

1942 newstyle: bool | None = None, 

1943 metaclass: NodeNG | None = None, 

1944 keywords: list[node_classes.Keyword] | None = None, 

1945 *, 

1946 position: Position | None = None, 

1947 doc_node: Const | None = None, 

1948 type_params: ( 

1949 list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] | None 

1950 ) = None, 

1951 ) -> None: 

1952 if keywords is not None: 

1953 self.keywords = keywords 

1954 self.bases = bases 

1955 self.body = body 

1956 self.decorators = decorators 

1957 self._metaclass = metaclass 

1958 self.position = position 

1959 self.doc_node = doc_node 

1960 self.type_params = type_params or [] 

1961 

1962 @cached_property 

1963 def blockstart_tolineno(self): 

1964 """The line on which the beginning of this block ends. 

1965 

1966 :type: int 

1967 """ 

1968 if self.bases: 

1969 return self.bases[-1].tolineno 

1970 

1971 return self.fromlineno 

1972 

1973 def block_range(self, lineno: int) -> tuple[int, int]: 

1974 """Get a range from the given line number to where this node ends. 

1975 

1976 :param lineno: Unused. 

1977 

1978 :returns: The range of line numbers that this node belongs to, 

1979 """ 

1980 return self.fromlineno, self.tolineno 

1981 

1982 def pytype(self) -> Literal["builtins.type"]: 

1983 """Get the name of the type that this node represents. 

1984 

1985 :returns: The name of the type. 

1986 """ 

1987 return "builtins.type" 

1988 

1989 def display_type(self) -> str: 

1990 """A human readable type of this node. 

1991 

1992 :returns: The type of this node. 

1993 :rtype: str 

1994 """ 

1995 return "Class" 

1996 

1997 def callable(self) -> bool: 

1998 """Whether this node defines something that is callable. 

1999 

2000 :returns: Whether this defines something that is callable. 

2001 For a :class:`ClassDef` this is always ``True``. 

2002 """ 

2003 return True 

2004 

2005 def is_subtype_of(self, type_name, context: InferenceContext | None = None) -> bool: 

2006 """Whether this class is a subtype of the given type. 

2007 

2008 :param type_name: The name of the type of check against. 

2009 :type type_name: str 

2010 

2011 :returns: Whether this class is a subtype of the given type. 

2012 """ 

2013 if self.qname() == type_name: 

2014 return True 

2015 

2016 return any(anc.qname() == type_name for anc in self.ancestors(context=context)) 

2017 

2018 def _infer_type_call(self, caller, context): 

2019 try: 

2020 name_node = next(caller.args[0].infer(context)) 

2021 except StopIteration as e: 

2022 raise InferenceError(node=caller.args[0], context=context) from e 

2023 if isinstance(name_node, node_classes.Const) and isinstance( 

2024 name_node.value, str 

2025 ): 

2026 name = name_node.value 

2027 else: 

2028 return util.Uninferable 

2029 

2030 result = ClassDef( 

2031 name, 

2032 lineno=0, 

2033 col_offset=0, 

2034 end_lineno=0, 

2035 end_col_offset=0, 

2036 parent=caller.parent, 

2037 ) 

2038 

2039 # Get the bases of the class. 

2040 try: 

2041 class_bases = next(caller.args[1].infer(context)) 

2042 except StopIteration as e: 

2043 raise InferenceError(node=caller.args[1], context=context) from e 

2044 if isinstance(class_bases, (node_classes.Tuple, node_classes.List)): 

2045 bases = [] 

2046 for base in class_bases.itered(): 

2047 inferred = next(base.infer(context=context), None) 

2048 if inferred: 

2049 bases.append( 

2050 node_classes.EvaluatedObject(original=base, value=inferred) 

2051 ) 

2052 result.bases = bases 

2053 else: 

2054 # There is currently no AST node that can represent an 'unknown' 

2055 # node (Uninferable is not an AST node), therefore we simply return Uninferable here 

2056 # although we know at least the name of the class. 

2057 return util.Uninferable 

2058 

2059 # Get the members of the class 

2060 try: 

2061 members = next(caller.args[2].infer(context)) 

2062 except (InferenceError, StopIteration): 

2063 members = None 

2064 

2065 if members and isinstance(members, node_classes.Dict): 

2066 for attr, value in members.items: 

2067 if isinstance(attr, node_classes.Const) and isinstance(attr.value, str): 

2068 result.locals[attr.value] = [value] 

2069 

2070 return result 

2071 

2072 def infer_call_result( 

2073 self, 

2074 caller: SuccessfulInferenceResult | None, 

2075 context: InferenceContext | None = None, 

2076 ) -> Iterator[InferenceResult]: 

2077 """infer what a class is returning when called""" 

2078 if self.is_subtype_of("builtins.type", context) and len(caller.args) == 3: 

2079 result = self._infer_type_call(caller, context) 

2080 yield result 

2081 return 

2082 

2083 dunder_call = None 

2084 try: 

2085 metaclass = self.metaclass(context=context) 

2086 if metaclass is not None: 

2087 # Only get __call__ if it's defined locally for the metaclass. 

2088 # Otherwise we will find ObjectModel.__call__ which will 

2089 # return an instance of the metaclass. Instantiating the class is 

2090 # handled later. 

2091 if "__call__" in metaclass.locals: 

2092 dunder_call = next(metaclass.igetattr("__call__", context)) 

2093 except (AttributeInferenceError, StopIteration): 

2094 pass 

2095 

2096 if dunder_call and dunder_call.qname() != "builtins.type.__call__": 

2097 # Call type.__call__ if not set metaclass 

2098 # (since type is the default metaclass) 

2099 context = bind_context_to_node(context, self) 

2100 context.callcontext.callee = dunder_call 

2101 yield from dunder_call.infer_call_result(caller, context) 

2102 else: 

2103 yield self.instantiate_class() 

2104 

2105 def scope_lookup( 

2106 self, node: LookupMixIn, name: str, offset: int = 0 

2107 ) -> tuple[LocalsDictNodeNG, list[nodes.NodeNG]]: 

2108 """Lookup where the given name is assigned. 

2109 

2110 :param node: The node to look for assignments up to. 

2111 Any assignments after the given node are ignored. 

2112 

2113 :param name: The name to find assignments for. 

2114 

2115 :param offset: The line offset to filter statements up to. 

2116 

2117 :returns: This scope node and the list of assignments associated to the 

2118 given name according to the scope where it has been found (locals, 

2119 globals or builtin). 

2120 """ 

2121 # If the name looks like a builtin name, just try to look 

2122 # into the upper scope of this class. We might have a 

2123 # decorator that it's poorly named after a builtin object 

2124 # inside this class. 

2125 lookup_upper_frame = ( 

2126 isinstance(node.parent, node_classes.Decorators) 

2127 and name in AstroidManager().builtins_module 

2128 ) 

2129 if ( 

2130 any( 

2131 node == base or (base.parent_of(node) and not self.type_params) 

2132 for base in self.bases 

2133 ) 

2134 or lookup_upper_frame 

2135 ): 

2136 # Handle the case where we have either a name 

2137 # in the bases of a class, which exists before 

2138 # the actual definition or the case where we have 

2139 # a Getattr node, with that name. 

2140 # 

2141 # name = ... 

2142 # class A(name): 

2143 # def name(self): ... 

2144 # 

2145 # import name 

2146 # class A(name.Name): 

2147 # def name(self): ... 

2148 if not self.parent: 

2149 raise ParentMissingError(target=self) 

2150 frame = self.parent.frame() 

2151 # line offset to avoid that class A(A) resolve the ancestor to 

2152 # the defined class 

2153 offset = -1 

2154 else: 

2155 frame = self 

2156 return frame._scope_lookup(node, name, offset) 

2157 

2158 @property 

2159 def basenames(self): 

2160 """The names of the parent classes 

2161 

2162 Names are given in the order they appear in the class definition. 

2163 

2164 :type: list(str) 

2165 """ 

2166 return [bnode.as_string() for bnode in self.bases] 

2167 

2168 def ancestors( 

2169 self, recurs: bool = True, context: InferenceContext | None = None 

2170 ) -> Generator[ClassDef]: 

2171 """Iterate over the base classes in prefixed depth first order. 

2172 

2173 :param recurs: Whether to recurse or return direct ancestors only. 

2174 

2175 :returns: The base classes 

2176 """ 

2177 # FIXME: should be possible to choose the resolution order 

2178 # FIXME: inference make infinite loops possible here 

2179 yielded = {self} 

2180 if context is None: 

2181 context = InferenceContext() 

2182 if not self.bases and self.qname() != "builtins.object": 

2183 # This should always be a ClassDef (which we don't assert for) 

2184 yield builtin_lookup("object")[1][0] # type: ignore[misc] 

2185 return 

2186 

2187 for stmt in self.bases: 

2188 with context.restore_path(): 

2189 try: 

2190 for baseobj in stmt.infer(context): 

2191 if not isinstance(baseobj, ClassDef): 

2192 if isinstance(baseobj, bases.Instance): 

2193 baseobj = baseobj._proxied 

2194 else: 

2195 continue 

2196 if not baseobj.hide: 

2197 if baseobj in yielded: 

2198 continue 

2199 yielded.add(baseobj) 

2200 yield baseobj 

2201 if not recurs: 

2202 continue 

2203 for grandpa in baseobj.ancestors(recurs=True, context=context): 

2204 if grandpa is self: 

2205 # This class is the ancestor of itself. 

2206 break 

2207 if grandpa in yielded: 

2208 continue 

2209 yielded.add(grandpa) 

2210 yield grandpa 

2211 except InferenceError: 

2212 continue 

2213 

2214 def local_attr_ancestors(self, name, context: InferenceContext | None = None): 

2215 """Iterate over the parents that define the given name. 

2216 

2217 :param name: The name to find definitions for. 

2218 :type name: str 

2219 

2220 :returns: The parents that define the given name. 

2221 :rtype: iterable(NodeNG) 

2222 """ 

2223 # Look up in the mro if we can. This will result in the 

2224 # attribute being looked up just as Python does it. 

2225 try: 

2226 ancestors: Iterable[ClassDef] = self.mro(context)[1:] 

2227 except MroError: 

2228 # Fallback to use ancestors, we can't determine 

2229 # a sane MRO. 

2230 ancestors = self.ancestors(context=context) 

2231 for astroid in ancestors: 

2232 if name in astroid: 

2233 yield astroid 

2234 

2235 def instance_attr_ancestors(self, name, context: InferenceContext | None = None): 

2236 """Iterate over the parents that define the given name as an attribute. 

2237 

2238 :param name: The name to find definitions for. 

2239 :type name: str 

2240 

2241 :returns: The parents that define the given name as 

2242 an instance attribute. 

2243 :rtype: iterable(NodeNG) 

2244 """ 

2245 for astroid in self.ancestors(context=context): 

2246 if name in astroid.instance_attrs: 

2247 yield astroid 

2248 

2249 def has_base(self, node) -> bool: 

2250 """Whether this class directly inherits from the given node. 

2251 

2252 :param node: The node to check for. 

2253 :type node: NodeNG 

2254 

2255 :returns: Whether this class directly inherits from the given node. 

2256 """ 

2257 return node in self.bases 

2258 

2259 def local_attr(self, name, context: InferenceContext | None = None): 

2260 """Get the list of assign nodes associated to the given name. 

2261 

2262 Assignments are looked for in both this class and in parents. 

2263 

2264 :returns: The list of assignments to the given name. 

2265 :rtype: list(NodeNG) 

2266 

2267 :raises AttributeInferenceError: If no attribute with this name 

2268 can be found in this class or parent classes. 

2269 """ 

2270 result = [] 

2271 if name in self.locals: 

2272 result = self.locals[name] 

2273 else: 

2274 class_node = next(self.local_attr_ancestors(name, context), None) 

2275 if class_node: 

2276 result = class_node.locals[name] 

2277 result = [n for n in result if not isinstance(n, node_classes.DelAttr)] 

2278 if result: 

2279 return result 

2280 raise AttributeInferenceError(target=self, attribute=name, context=context) 

2281 

2282 def instance_attr(self, name, context: InferenceContext | None = None): 

2283 """Get the list of nodes associated to the given attribute name. 

2284 

2285 Assignments are looked for in both this class and in parents. 

2286 

2287 :returns: The list of assignments to the given name. 

2288 :rtype: list(NodeNG) 

2289 

2290 :raises AttributeInferenceError: If no attribute with this name 

2291 can be found in this class or parent classes. 

2292 """ 

2293 # Return a copy, so we don't modify self.instance_attrs, 

2294 # which could lead to infinite loop. 

2295 values = list(self.instance_attrs.get(name, [])) 

2296 # get all values from parents 

2297 for class_node in self.instance_attr_ancestors(name, context): 

2298 values += class_node.instance_attrs[name] 

2299 values = [n for n in values if not isinstance(n, node_classes.DelAttr)] 

2300 if values: 

2301 return values 

2302 raise AttributeInferenceError(target=self, attribute=name, context=context) 

2303 

2304 def instantiate_class(self) -> bases.Instance: 

2305 """Get an :class:`Instance` of the :class:`ClassDef` node. 

2306 

2307 :returns: An :class:`Instance` of the :class:`ClassDef` node 

2308 """ 

2309 from astroid import objects # pylint: disable=import-outside-toplevel 

2310 

2311 try: 

2312 if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()): 

2313 # Subclasses of exceptions can be exception instances 

2314 return objects.ExceptionInstance(self) 

2315 except MroError: 

2316 pass 

2317 return bases.Instance(self) 

2318 

2319 def getattr( 

2320 self, 

2321 name: str, 

2322 context: InferenceContext | None = None, 

2323 class_context: bool = True, 

2324 ) -> list[InferenceResult]: 

2325 """Get an attribute from this class, using Python's attribute semantic. 

2326 

2327 This method doesn't look in the :attr:`instance_attrs` dictionary 

2328 since it is done by an :class:`Instance` proxy at inference time. 

2329 It may return an :class:`Uninferable` object if 

2330 the attribute has not been 

2331 found, but a ``__getattr__`` or ``__getattribute__`` method is defined. 

2332 If ``class_context`` is given, then it is considered that the 

2333 attribute is accessed from a class context, 

2334 e.g. ClassDef.attribute, otherwise it might have been accessed 

2335 from an instance as well. If ``class_context`` is used in that 

2336 case, then a lookup in the implicit metaclass and the explicit 

2337 metaclass will be done. 

2338 

2339 :param name: The attribute to look for. 

2340 

2341 :param class_context: Whether the attribute can be accessed statically. 

2342 

2343 :returns: The attribute. 

2344 

2345 :raises AttributeInferenceError: If the attribute cannot be inferred. 

2346 """ 

2347 if not name: 

2348 raise AttributeInferenceError(target=self, attribute=name, context=context) 

2349 

2350 # don't modify the list in self.locals! 

2351 values: list[InferenceResult] = list(self.locals.get(name, [])) 

2352 for classnode in self.ancestors(recurs=True, context=context): 

2353 values += classnode.locals.get(name, []) 

2354 

2355 if name in self.special_attributes and class_context and not values: 

2356 result = [self.special_attributes.lookup(name)] 

2357 return result 

2358 

2359 if class_context: 

2360 values += self._metaclass_lookup_attribute(name, context) 

2361 

2362 result: list[InferenceResult] = [] 

2363 for value in values: 

2364 if isinstance(value, node_classes.AssignName): 

2365 stmt = value.statement() 

2366 # Ignore AnnAssigns without value, which are not attributes in the purest sense. 

2367 if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None: 

2368 continue 

2369 result.append(value) 

2370 

2371 if not result: 

2372 raise AttributeInferenceError(target=self, attribute=name, context=context) 

2373 

2374 return result 

2375 

2376 @lru_cache(maxsize=1024) # noqa 

2377 def _metaclass_lookup_attribute(self, name, context): 

2378 """Search the given name in the implicit and the explicit metaclass.""" 

2379 attrs = set() 

2380 implicit_meta = self.implicit_metaclass() 

2381 context = copy_context(context) 

2382 metaclass = self.metaclass(context=context) 

2383 for cls in (implicit_meta, metaclass): 

2384 if cls and cls != self and isinstance(cls, ClassDef): 

2385 cls_attributes = self._get_attribute_from_metaclass(cls, name, context) 

2386 attrs.update(cls_attributes) 

2387 return attrs 

2388 

2389 def _get_attribute_from_metaclass(self, cls, name, context): 

2390 from astroid import objects # pylint: disable=import-outside-toplevel 

2391 

2392 try: 

2393 attrs = cls.getattr(name, context=context, class_context=True) 

2394 except AttributeInferenceError: 

2395 return 

2396 

2397 for attr in bases._infer_stmts(attrs, context, frame=cls): 

2398 if not isinstance(attr, FunctionDef): 

2399 yield attr 

2400 continue 

2401 

2402 if isinstance(attr, objects.Property): 

2403 yield attr 

2404 continue 

2405 if attr.type == "classmethod": 

2406 # If the method is a classmethod, then it will 

2407 # be bound to the metaclass, not to the class 

2408 # from where the attribute is retrieved. 

2409 # get_wrapping_class could return None, so just 

2410 # default to the current class. 

2411 frame = get_wrapping_class(attr) or self 

2412 yield bases.BoundMethod(attr, frame) 

2413 elif attr.type == "staticmethod": 

2414 yield attr 

2415 else: 

2416 yield bases.BoundMethod(attr, self) 

2417 

2418 def igetattr( 

2419 self, 

2420 name: str, 

2421 context: InferenceContext | None = None, 

2422 class_context: bool = True, 

2423 ) -> Iterator[InferenceResult]: 

2424 """Infer the possible values of the given variable. 

2425 

2426 :param name: The name of the variable to infer. 

2427 

2428 :returns: The inferred possible values. 

2429 """ 

2430 from astroid import objects # pylint: disable=import-outside-toplevel 

2431 

2432 # set lookup name since this is necessary to infer on import nodes for 

2433 # instance 

2434 context = copy_context(context) 

2435 context.lookupname = name 

2436 

2437 metaclass = self.metaclass(context=context) 

2438 try: 

2439 attributes = self.getattr(name, context, class_context=class_context) 

2440 # If we have more than one attribute, make sure that those starting from 

2441 # the second one are from the same scope. This is to account for modifications 

2442 # to the attribute happening *after* the attribute's definition (e.g. AugAssigns on lists) 

2443 if len(attributes) > 1: 

2444 first_attr, attributes = attributes[0], attributes[1:] 

2445 first_scope = first_attr.parent.scope() 

2446 attributes = [first_attr] + [ 

2447 attr 

2448 for attr in attributes 

2449 if attr.parent and attr.parent.scope() == first_scope 

2450 ] 

2451 functions = [attr for attr in attributes if isinstance(attr, FunctionDef)] 

2452 setter = None 

2453 for function in functions: 

2454 dec_names = function.decoratornames(context=context) 

2455 for dec_name in dec_names: 

2456 if dec_name is util.Uninferable: 

2457 continue 

2458 if dec_name.split(".")[-1] == "setter": 

2459 setter = function 

2460 if setter: 

2461 break 

2462 if functions: 

2463 # Prefer only the last function, unless a property is involved. 

2464 last_function = functions[-1] 

2465 attributes = [ 

2466 a 

2467 for a in attributes 

2468 if a not in functions or a is last_function or bases._is_property(a) 

2469 ] 

2470 

2471 for inferred in bases._infer_stmts(attributes, context, frame=self): 

2472 # yield Uninferable object instead of descriptors when necessary 

2473 if not isinstance(inferred, node_classes.Const) and isinstance( 

2474 inferred, bases.Instance 

2475 ): 

2476 try: 

2477 inferred._proxied.getattr("__get__", context) 

2478 except AttributeInferenceError: 

2479 yield inferred 

2480 else: 

2481 yield util.Uninferable 

2482 elif isinstance(inferred, objects.Property): 

2483 function = inferred.function 

2484 if not class_context: 

2485 if not context.callcontext and not setter: 

2486 context.callcontext = CallContext( 

2487 args=function.args.arguments, callee=function 

2488 ) 

2489 # Through an instance so we can solve the property 

2490 yield from function.infer_call_result( 

2491 caller=self, context=context 

2492 ) 

2493 # If we're in a class context, we need to determine if the property 

2494 # was defined in the metaclass (a derived class must be a subclass of 

2495 # the metaclass of all its bases), in which case we can resolve the 

2496 # property. If not, i.e. the property is defined in some base class 

2497 # instead, then we return the property object 

2498 elif metaclass and function.parent.scope() is metaclass: 

2499 # Resolve a property as long as it is not accessed through 

2500 # the class itself. 

2501 yield from function.infer_call_result( 

2502 caller=self, context=context 

2503 ) 

2504 else: 

2505 yield inferred 

2506 else: 

2507 yield function_to_method(inferred, self) 

2508 except AttributeInferenceError as error: 

2509 if not name.startswith("__") and self.has_dynamic_getattr(context): 

2510 # class handle some dynamic attributes, return a Uninferable object 

2511 yield util.Uninferable 

2512 else: 

2513 raise InferenceError( 

2514 str(error), target=self, attribute=name, context=context 

2515 ) from error 

2516 

2517 def has_dynamic_getattr(self, context: InferenceContext | None = None) -> bool: 

2518 """Check if the class has a custom __getattr__ or __getattribute__. 

2519 

2520 If any such method is found and it is not from 

2521 builtins, nor from an extension module, then the function 

2522 will return True. 

2523 

2524 :returns: Whether the class has a custom __getattr__ or __getattribute__. 

2525 """ 

2526 

2527 def _valid_getattr(node): 

2528 root = node.root() 

2529 return root.name != "builtins" and getattr(root, "pure_python", None) 

2530 

2531 try: 

2532 return _valid_getattr(self.getattr("__getattr__", context)[0]) 

2533 except AttributeInferenceError: 

2534 try: 

2535 getattribute = self.getattr("__getattribute__", context)[0] 

2536 return _valid_getattr(getattribute) 

2537 except AttributeInferenceError: 

2538 pass 

2539 return False 

2540 

2541 def getitem(self, index, context: InferenceContext | None = None): 

2542 """Return the inference of a subscript. 

2543 

2544 This is basically looking up the method in the metaclass and calling it. 

2545 

2546 :returns: The inferred value of a subscript to this class. 

2547 :rtype: NodeNG 

2548 

2549 :raises AstroidTypeError: If this class does not define a 

2550 ``__getitem__`` method. 

2551 """ 

2552 try: 

2553 methods = lookup(self, "__getitem__", context=context) 

2554 except AttributeInferenceError as exc: 

2555 if isinstance(self, ClassDef): 

2556 # subscripting a class definition may be 

2557 # achieved thanks to __class_getitem__ method 

2558 # which is a classmethod defined in the class 

2559 # that supports subscript and not in the metaclass 

2560 try: 

2561 methods = self.getattr("__class_getitem__") 

2562 # Here it is assumed that the __class_getitem__ node is 

2563 # a FunctionDef. One possible improvement would be to deal 

2564 # with more generic inference. 

2565 except AttributeInferenceError: 

2566 raise AstroidTypeError(node=self, context=context) from exc 

2567 else: 

2568 raise AstroidTypeError(node=self, context=context) from exc 

2569 

2570 method = methods[0] 

2571 

2572 # Create a new callcontext for providing index as an argument. 

2573 new_context = bind_context_to_node(context, self) 

2574 new_context.callcontext = CallContext(args=[index], callee=method) 

2575 

2576 try: 

2577 return next(method.infer_call_result(self, new_context), util.Uninferable) 

2578 except AttributeError: 

2579 # Starting with python3.9, builtin types list, dict etc... 

2580 # are subscriptable thanks to __class_getitem___ classmethod. 

2581 # However in such case the method is bound to an EmptyNode and 

2582 # EmptyNode doesn't have infer_call_result method yielding to 

2583 # AttributeError 

2584 if ( 

2585 isinstance(method, node_classes.EmptyNode) 

2586 and self.pytype() == "builtins.type" 

2587 ): 

2588 return self 

2589 raise 

2590 except InferenceError: 

2591 return util.Uninferable 

2592 

2593 def methods(self): 

2594 """Iterate over all of the method defined in this class and its parents. 

2595 

2596 :returns: The methods defined on the class. 

2597 :rtype: iterable(FunctionDef) 

2598 """ 

2599 done = {} 

2600 for astroid in itertools.chain(iter((self,)), self.ancestors()): 

2601 for meth in astroid.mymethods(): 

2602 if meth.name in done: 

2603 continue 

2604 done[meth.name] = None 

2605 yield meth 

2606 

2607 def mymethods(self): 

2608 """Iterate over all of the method defined in this class only. 

2609 

2610 :returns: The methods defined on the class. 

2611 :rtype: iterable(FunctionDef) 

2612 """ 

2613 for member in self.values(): 

2614 if isinstance(member, FunctionDef): 

2615 yield member 

2616 

2617 def implicit_metaclass(self): 

2618 """Get the implicit metaclass of the current class. 

2619 

2620 This will return an instance of builtins.type. 

2621 

2622 :returns: The metaclass. 

2623 :rtype: builtins.type 

2624 """ 

2625 return builtin_lookup("type")[1][0] 

2626 

2627 def declared_metaclass( 

2628 self, context: InferenceContext | None = None 

2629 ) -> SuccessfulInferenceResult | None: 

2630 """Return the explicit declared metaclass for the current class. 

2631 

2632 An explicit declared metaclass is defined 

2633 either by passing the ``metaclass`` keyword argument 

2634 in the class definition line (Python 3) or (Python 2) by 

2635 having a ``__metaclass__`` class attribute, or if there are 

2636 no explicit bases but there is a global ``__metaclass__`` variable. 

2637 

2638 :returns: The metaclass of this class, 

2639 or None if one could not be found. 

2640 """ 

2641 for base in self.bases: 

2642 try: 

2643 for baseobj in base.infer(context=context): 

2644 if isinstance(baseobj, ClassDef) and baseobj.hide: 

2645 self._metaclass = baseobj._metaclass 

2646 self._metaclass_hack = True 

2647 break 

2648 except InferenceError: 

2649 pass 

2650 

2651 if self._metaclass: 

2652 # Expects this from Py3k TreeRebuilder 

2653 try: 

2654 return next( 

2655 node 

2656 for node in self._metaclass.infer(context=context) 

2657 if not isinstance(node, util.UninferableBase) 

2658 ) 

2659 except (InferenceError, StopIteration): 

2660 return None 

2661 

2662 return None 

2663 

2664 def _find_metaclass( 

2665 self, seen: set[ClassDef] | None = None, context: InferenceContext | None = None 

2666 ) -> SuccessfulInferenceResult | None: 

2667 if seen is None: 

2668 seen = set() 

2669 seen.add(self) 

2670 

2671 klass = self.declared_metaclass(context=context) 

2672 if klass is None: 

2673 for parent in self.ancestors(context=context): 

2674 if parent not in seen: 

2675 klass = parent._find_metaclass(seen) 

2676 if klass is not None: 

2677 break 

2678 return klass 

2679 

2680 def metaclass( 

2681 self, context: InferenceContext | None = None 

2682 ) -> SuccessfulInferenceResult | None: 

2683 """Get the metaclass of this class. 

2684 

2685 If this class does not define explicitly a metaclass, 

2686 then the first defined metaclass in ancestors will be used 

2687 instead. 

2688 

2689 :returns: The metaclass of this class. 

2690 """ 

2691 return self._find_metaclass(context=context) 

2692 

2693 def has_metaclass_hack(self) -> bool: 

2694 return self._metaclass_hack 

2695 

2696 def _islots(self): 

2697 """Return an iterator with the inferred slots.""" 

2698 if "__slots__" not in self.locals: 

2699 return None 

2700 for slots in self.igetattr("__slots__"): 

2701 # check if __slots__ is a valid type 

2702 for meth in ITER_METHODS: 

2703 try: 

2704 slots.getattr(meth) 

2705 break 

2706 except AttributeInferenceError: 

2707 continue 

2708 else: 

2709 continue 

2710 

2711 if isinstance(slots, node_classes.Const): 

2712 # a string. Ignore the following checks, 

2713 # but yield the node, only if it has a value 

2714 if slots.value: 

2715 yield slots 

2716 continue 

2717 if not hasattr(slots, "itered"): 

2718 # we can't obtain the values, maybe a .deque? 

2719 continue 

2720 

2721 if isinstance(slots, node_classes.Dict): 

2722 values = [item[0] for item in slots.items] 

2723 else: 

2724 values = slots.itered() 

2725 if isinstance(values, util.UninferableBase): 

2726 continue 

2727 if not values: 

2728 # Stop the iteration, because the class 

2729 # has an empty list of slots. 

2730 return values 

2731 

2732 for elt in values: 

2733 try: 

2734 for inferred in elt.infer(): 

2735 if not isinstance( 

2736 inferred, node_classes.Const 

2737 ) or not isinstance(inferred.value, str): 

2738 continue 

2739 if not inferred.value: 

2740 continue 

2741 yield inferred 

2742 except InferenceError: 

2743 continue 

2744 

2745 return None 

2746 

2747 def _slots(self): 

2748 

2749 slots = self._islots() 

2750 try: 

2751 first = next(slots) 

2752 except StopIteration as exc: 

2753 # The class doesn't have a __slots__ definition or empty slots. 

2754 if exc.args and exc.args[0] not in ("", None): 

2755 return exc.args[0] 

2756 return None 

2757 return [first, *slots] 

2758 

2759 # Cached, because inferring them all the time is expensive 

2760 @cached_property 

2761 def _all_slots(self): 

2762 """Get all the slots for this node. 

2763 

2764 :returns: The names of slots for this class. 

2765 If the class doesn't define any slot, through the ``__slots__`` 

2766 variable, then this function will return a None. 

2767 Also, it will return None in the case the slots were not inferred. 

2768 :rtype: list(str) or None 

2769 """ 

2770 

2771 def grouped_slots( 

2772 mro: list[ClassDef], 

2773 ) -> Iterator[node_classes.NodeNG | None]: 

2774 for cls in mro: 

2775 # Not interested in object, since it can't have slots. 

2776 if cls.qname() == "builtins.object": 

2777 continue 

2778 try: 

2779 cls_slots = cls._slots() 

2780 except NotImplementedError: 

2781 continue 

2782 if cls_slots is not None: 

2783 yield from cls_slots 

2784 else: 

2785 yield None 

2786 

2787 try: 

2788 mro = self.mro() 

2789 except MroError as e: 

2790 raise NotImplementedError( 

2791 "Cannot get slots while parsing mro fails." 

2792 ) from e 

2793 

2794 slots = list(grouped_slots(mro)) 

2795 if not all(slot is not None for slot in slots): 

2796 return None 

2797 

2798 return sorted(set(slots), key=lambda item: item.value) 

2799 

2800 def slots(self): 

2801 return self._all_slots 

2802 

2803 def _inferred_bases(self, context: InferenceContext | None = None): 

2804 # Similar with .ancestors, but the difference is when one base is inferred, 

2805 # only the first object is wanted. That's because 

2806 # we aren't interested in superclasses, as in the following 

2807 # example: 

2808 # 

2809 # class SomeSuperClass(object): pass 

2810 # class SomeClass(SomeSuperClass): pass 

2811 # class Test(SomeClass): pass 

2812 # 

2813 # Inferring SomeClass from the Test's bases will give 

2814 # us both SomeClass and SomeSuperClass, but we are interested 

2815 # only in SomeClass. 

2816 

2817 if context is None: 

2818 context = InferenceContext() 

2819 if not self.bases and self.qname() != "builtins.object": 

2820 yield builtin_lookup("object")[1][0] 

2821 return 

2822 

2823 for stmt in self.bases: 

2824 try: 

2825 baseobj = _infer_last(stmt, context) 

2826 except InferenceError: 

2827 continue 

2828 if isinstance(baseobj, bases.Instance): 

2829 baseobj = baseobj._proxied 

2830 if not isinstance(baseobj, ClassDef): 

2831 continue 

2832 if not baseobj.hide: 

2833 yield baseobj 

2834 else: 

2835 yield from baseobj.bases 

2836 

2837 def _compute_mro(self, context: InferenceContext | None = None): 

2838 if self.qname() == "builtins.object": 

2839 return [self] 

2840 

2841 inferred_bases = list(self._inferred_bases(context=context)) 

2842 bases_mro = [] 

2843 for base in inferred_bases: 

2844 if base is self: 

2845 continue 

2846 

2847 mro = base._compute_mro(context=context) 

2848 bases_mro.append(mro) 

2849 

2850 unmerged_mro: list[list[ClassDef]] = [[self], *bases_mro, inferred_bases] 

2851 unmerged_mro = clean_duplicates_mro(unmerged_mro, self, context) 

2852 clean_typing_generic_mro(unmerged_mro) 

2853 return _c3_merge(unmerged_mro, self, context) 

2854 

2855 def mro(self, context: InferenceContext | None = None) -> list[ClassDef]: 

2856 """Get the method resolution order, using C3 linearization. 

2857 

2858 :returns: The list of ancestors, sorted by the mro. 

2859 :rtype: list(NodeNG) 

2860 :raises DuplicateBasesError: Duplicate bases in the same class base 

2861 :raises InconsistentMroError: A class' MRO is inconsistent 

2862 """ 

2863 return self._compute_mro(context=context) 

2864 

2865 def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: 

2866 """Determine the boolean value of this node. 

2867 

2868 :returns: The boolean value of this node. 

2869 For a :class:`ClassDef` this is always ``True``. 

2870 """ 

2871 return True 

2872 

2873 def get_children(self): 

2874 if self.decorators is not None: 

2875 yield self.decorators 

2876 

2877 yield from self.bases 

2878 if self.keywords is not None: 

2879 yield from self.keywords 

2880 yield from self.type_params 

2881 

2882 yield from self.body 

2883 

2884 @cached_property 

2885 def _assign_nodes_in_scope(self): 

2886 children_assign_nodes = ( 

2887 child_node._assign_nodes_in_scope for child_node in self.body 

2888 ) 

2889 return list(itertools.chain.from_iterable(children_assign_nodes)) 

2890 

2891 def frame(self: _T, *, future: Literal[None, True] = None) -> _T: 

2892 """The node's frame node. 

2893 

2894 A frame node is a :class:`Module`, :class:`FunctionDef`, 

2895 :class:`ClassDef` or :class:`Lambda`. 

2896 

2897 :returns: The node itself. 

2898 """ 

2899 return self 

2900 

2901 def _infer( 

2902 self, context: InferenceContext | None = None, **kwargs: Any 

2903 ) -> Generator[ClassDef]: 

2904 yield self