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

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

1303 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 sys 

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

18from functools import cached_property, lru_cache 

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

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 sys.version_info >= (3, 11): 

55 from typing import Self 

56else: 

57 from typing_extensions import Self 

58 

59if TYPE_CHECKING: 

60 from astroid import nodes, objects 

61 from astroid.nodes import Arguments, Const, NodeNG 

62 from astroid.nodes._base_nodes import LookupMixIn 

63 

64 

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

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

67BUILTIN_DESCRIPTORS = frozenset( 

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

69) 

70 

71 

72def _c3_merge(sequences, cls, context): 

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

74 

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

76 

77 """ 

78 result = [] 

79 while True: 

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

81 if not sequences: 

82 return result 

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

84 candidate = s1[0] 

85 for s2 in sequences: 

86 if candidate in s2[1:]: 

87 candidate = None 

88 break # reject the current head, it appears later 

89 else: 

90 break 

91 if not candidate: 

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

93 # candidates for the next mro sequence. 

94 raise InconsistentMroError( 

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

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

97 mros=sequences, 

98 cls=cls, 

99 context=context, 

100 ) 

101 

102 result.append(candidate) 

103 # remove the chosen candidate 

104 for seq in sequences: 

105 if seq[0] == candidate: 

106 del seq[0] 

107 return None 

108 

109 

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

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

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

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

114 sequences if multiple are present. 

115 

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

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

118 as well as its entry the bases_mro. 

119 

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

121 """ 

122 bases_mro = sequences[1:-1] 

123 inferred_bases = sequences[-1] 

124 # Check if Generic is part of inferred_bases 

125 for i, base in enumerate(inferred_bases): 

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

127 position_in_inferred_bases = i 

128 break 

129 else: 

130 return 

131 # Check if also part of bases_mro 

132 # Ignore entry for typing.Generic 

133 for i, seq in enumerate(bases_mro): 

134 if i == position_in_inferred_bases: 

135 continue 

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

137 break 

138 else: 

139 return 

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

141 # and the corresponding one from bases_mro 

142 inferred_bases.pop(position_in_inferred_bases) 

143 bases_mro.pop(position_in_inferred_bases) 

144 

145 

146def clean_duplicates_mro( 

147 sequences: list[list[ClassDef]], 

148 cls: ClassDef, 

149 context: InferenceContext | None, 

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

151 for sequence in sequences: 

152 seen = set() 

153 for node in sequence: 

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

155 if lineno_and_qname in seen: 

156 raise DuplicateBasesError( 

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

158 mros=sequences, 

159 cls=cls, 

160 context=context, 

161 ) 

162 seen.add(lineno_and_qname) 

163 return sequences 

164 

165 

166def function_to_method(n, klass): 

167 if isinstance(n, FunctionDef): 

168 if n.type == "classmethod": 

169 return bases.BoundMethod(n, klass) 

170 if n.type == "property": 

171 return n 

172 if n.type != "staticmethod": 

173 return bases.UnboundMethod(n) 

174 return n 

175 

176 

177def _infer_last( 

178 arg: SuccessfulInferenceResult, context: InferenceContext 

179) -> InferenceResult: 

180 res = util.Uninferable 

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

182 res = b 

183 return res 

184 

185 

186class Module(LocalsDictNodeNG): 

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

188 

189 >>> import astroid 

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

191 >>> node 

192 <Import l.1 at 0x7f23b2e4e5c0> 

193 >>> node.parent 

194 <Module l.0 at 0x7f23b2e4eda0> 

195 """ 

196 

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

198 

199 doc_node: Const | None 

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

201 

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

203 

204 file_bytes: str | bytes | None = None 

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

206 

207 file_encoding: str | None = None 

208 """The encoding of the source file. 

209 

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

211 Python 2 only. 

212 """ 

213 

214 special_attributes = ModuleModel() 

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

216 

217 # names of module attributes available through the global scope 

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

219 "__name__", 

220 "__doc__", 

221 "__file__", 

222 "__path__", 

223 "__package__", 

224 } 

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

226 

227 _other_fields = ( 

228 "name", 

229 "file", 

230 "path", 

231 "package", 

232 "pure_python", 

233 "future_imports", 

234 ) 

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

236 

237 def __init__( 

238 self, 

239 name: str, 

240 file: str | None = None, 

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

242 package: bool = False, 

243 pure_python: bool = True, 

244 ) -> None: 

245 self.name = name 

246 """The name of the module.""" 

247 

248 self.file = file 

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

250 

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

252 built-in module. 

253 """ 

254 

255 self.path = path 

256 

257 self.package = package 

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

259 

260 self.pure_python = pure_python 

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

262 

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

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

265 

266 self.locals = self.globals = {} 

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

268 

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

270 """The contents of the module.""" 

271 

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

273 """The imports from ``__future__``.""" 

274 

275 super().__init__( 

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

277 ) 

278 

279 # pylint: enable=redefined-builtin 

280 

281 def postinit( 

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

283 ): 

284 self.body = body 

285 self.doc_node = doc_node 

286 

287 def _get_stream(self): 

288 if self.file_bytes is not None: 

289 return io.BytesIO(self.file_bytes) 

290 if self.file is not None: 

291 # pylint: disable=consider-using-with 

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

293 return stream 

294 return None 

295 

296 def stream(self): 

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

298 

299 :type: file or io.BytesIO or None 

300 """ 

301 return self._get_stream() 

302 

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

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

305 

306 :param lineno: Unused. 

307 

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

309 """ 

310 return self.fromlineno, self.tolineno 

311 

312 def scope_lookup( 

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

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

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

316 

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

318 Any assignments after the given node are ignored. 

319 

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

321 

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

323 

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

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

326 globals or builtin). 

327 """ 

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

329 try: 

330 return self, self.getattr(name) 

331 except AttributeInferenceError: 

332 return self, [] 

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

334 

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

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

337 

338 :returns: The name of the type. 

339 """ 

340 return "builtins.module" 

341 

342 def display_type(self) -> str: 

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

344 

345 :returns: The type of this node. 

346 :rtype: str 

347 """ 

348 return "Module" 

349 

350 def getattr( 

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

352 ): 

353 if not name: 

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

355 

356 result = [] 

357 name_in_locals = name in self.locals 

358 

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

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

361 if name == "__name__": 

362 main_const = node_classes.const_factory("__main__") 

363 main_const.parent = AstroidManager().builtins_module 

364 result.append(main_const) 

365 elif not ignore_locals and name_in_locals: 

366 result = self.locals[name] 

367 elif self.package: 

368 try: 

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

370 except (AstroidBuildingError, SyntaxError) as exc: 

371 raise AttributeInferenceError( 

372 target=self, attribute=name, context=context 

373 ) from exc 

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

375 if result: 

376 return result 

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

378 

379 def igetattr( 

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

381 ) -> Iterator[InferenceResult]: 

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

383 

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

385 

386 :returns: The inferred possible values. 

387 """ 

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

389 # instance 

390 context = copy_context(context) 

391 context.lookupname = name 

392 try: 

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

394 except AttributeInferenceError as error: 

395 raise InferenceError( 

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

397 ) from error 

398 

399 def fully_defined(self) -> bool: 

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

401 

402 If so, the module contains a complete representation, 

403 including the code. 

404 

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

406 """ 

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

408 

409 def statement(self) -> NoReturn: 

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

411 

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

413 """ 

414 raise StatementMissing(target=self) 

415 

416 def previous_sibling(self): 

417 """The previous sibling statement. 

418 

419 :returns: The previous sibling statement node. 

420 :rtype: NodeNG or None 

421 """ 

422 

423 def next_sibling(self): 

424 """The next sibling statement node. 

425 

426 :returns: The next sibling statement node. 

427 :rtype: NodeNG or None 

428 """ 

429 

430 _absolute_import_activated = True 

431 

432 def absolute_import_activated(self) -> bool: 

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

434 

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

436 """ 

437 return self._absolute_import_activated 

438 

439 def import_module( 

440 self, 

441 modname: str, 

442 relative_only: bool = False, 

443 level: int | None = None, 

444 use_cache: bool = True, 

445 ) -> Module: 

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

447 

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

449 

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

451 

452 :param level: The level of relative import. 

453 

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

455 

456 :returns: The imported module ast. 

457 """ 

458 if relative_only and level is None: 

459 level = 0 

460 absmodname = self.relative_to_absolute_name(modname, level) 

461 

462 try: 

463 return AstroidManager().ast_from_module_name( 

464 absmodname, use_cache=use_cache 

465 ) 

466 except AstroidBuildingError: 

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

468 # skip here 

469 if relative_only: 

470 raise 

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

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

473 if modname == absmodname: 

474 raise 

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

476 

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

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

479 

480 The relative import can be implicit or explicit. 

481 

482 :param modname: The module name to convert. 

483 

484 :param level: The level of relative import. 

485 

486 :returns: The absolute module name. 

487 

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

489 module too far above this one. 

490 """ 

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

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

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

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

495 return modname 

496 if level: 

497 if self.package: 

498 level = level - 1 

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

500 elif ( 

501 self.path 

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

503 and os.path.exists( 

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

505 ) 

506 ): 

507 level = level - 1 

508 package_name = "" 

509 else: 

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

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

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

513 

514 elif self.package: 

515 package_name = self.name 

516 else: 

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

518 

519 if package_name: 

520 if not modname: 

521 return package_name 

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

523 return modname 

524 

525 def wildcard_import_names(self): 

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

527 

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

529 current CPython implementation of wildcard imports. 

530 

531 :returns: The list of imported names. 

532 :rtype: list(str) 

533 """ 

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

535 # to avoid catching too many Exceptions 

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

537 try: 

538 all_values = self["__all__"] 

539 except KeyError: 

540 return default 

541 

542 try: 

543 explicit = next(all_values.assigned_stmts()) 

544 except (InferenceError, StopIteration): 

545 return default 

546 except AttributeError: 

547 # not an assignment node 

548 # XXX infer? 

549 return default 

550 

551 # Try our best to detect the exported name. 

552 inferred = [] 

553 try: 

554 explicit = next(explicit.infer()) 

555 except (InferenceError, StopIteration): 

556 return default 

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

558 return default 

559 

560 def str_const(node) -> bool: 

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

562 

563 for node in explicit.elts: 

564 if str_const(node): 

565 inferred.append(node.value) 

566 else: 

567 try: 

568 inferred_node = next(node.infer()) 

569 except (InferenceError, StopIteration): 

570 continue 

571 if str_const(inferred_node): 

572 inferred.append(inferred_node.value) 

573 return inferred 

574 

575 def public_names(self): 

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

577 

578 :returns: The list of public names. 

579 :rtype: list(str) 

580 """ 

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

582 

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

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

585 

586 :returns: The boolean value of this node. 

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

588 """ 

589 return True 

590 

591 def get_children(self): 

592 yield from self.body 

593 

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

595 """The node's frame node. 

596 

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

598 :class:`ClassDef` or :class:`Lambda`. 

599 

600 :returns: The node itself. 

601 """ 

602 return self 

603 

604 def _infer( 

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

606 ) -> Generator[Module]: 

607 yield self 

608 

609 

610class __SyntheticRoot(Module): 

611 def __init__(self): 

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

613 

614 

615SYNTHETIC_ROOT = __SyntheticRoot() 

616 

617 

618class GeneratorExp(ComprehensionScope): 

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

620 

621 >>> import astroid 

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

623 >>> node 

624 <GeneratorExp l.1 at 0x7f23b2e4e400> 

625 """ 

626 

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

628 _other_other_fields = ("locals",) 

629 elt: NodeNG 

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

631 

632 def __init__( 

633 self, 

634 lineno: int, 

635 col_offset: int, 

636 parent: NodeNG, 

637 *, 

638 end_lineno: int | None, 

639 end_col_offset: int | None, 

640 ) -> None: 

641 self.locals = {} 

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

643 

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

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

646 

647 super().__init__( 

648 lineno=lineno, 

649 col_offset=col_offset, 

650 end_lineno=end_lineno, 

651 end_col_offset=end_col_offset, 

652 parent=parent, 

653 ) 

654 

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

656 self.elt = elt 

657 self.generators = generators 

658 

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

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

661 

662 :returns: The boolean value of this node. 

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

664 """ 

665 return True 

666 

667 def get_children(self): 

668 yield self.elt 

669 

670 yield from self.generators 

671 

672 

673class DictComp(ComprehensionScope): 

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

675 

676 >>> import astroid 

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

678 >>> node 

679 <DictComp l.1 at 0x7f23b2e41d68> 

680 """ 

681 

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

683 _other_other_fields = ("locals",) 

684 key: NodeNG 

685 """What produces the keys.""" 

686 

687 value: NodeNG 

688 """What produces the values.""" 

689 

690 def __init__( 

691 self, 

692 lineno: int, 

693 col_offset: int, 

694 parent: NodeNG, 

695 *, 

696 end_lineno: int | None, 

697 end_col_offset: int | None, 

698 ) -> None: 

699 self.locals = {} 

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

701 

702 super().__init__( 

703 lineno=lineno, 

704 col_offset=col_offset, 

705 end_lineno=end_lineno, 

706 end_col_offset=end_col_offset, 

707 parent=parent, 

708 ) 

709 

710 def postinit( 

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

712 ) -> None: 

713 self.key = key 

714 self.value = value 

715 self.generators = generators 

716 

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

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

719 

720 :returns: The boolean value of this node. 

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

722 :rtype: Uninferable 

723 """ 

724 return util.Uninferable 

725 

726 def get_children(self): 

727 yield self.key 

728 yield self.value 

729 

730 yield from self.generators 

731 

732 

733class SetComp(ComprehensionScope): 

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

735 

736 >>> import astroid 

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

738 >>> node 

739 <SetComp l.1 at 0x7f23b2e41898> 

740 """ 

741 

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

743 _other_other_fields = ("locals",) 

744 elt: NodeNG 

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

746 

747 def __init__( 

748 self, 

749 lineno: int, 

750 col_offset: int, 

751 parent: NodeNG, 

752 *, 

753 end_lineno: int | None, 

754 end_col_offset: int | None, 

755 ) -> None: 

756 self.locals = {} 

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

758 

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

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

761 

762 super().__init__( 

763 lineno=lineno, 

764 col_offset=col_offset, 

765 end_lineno=end_lineno, 

766 end_col_offset=end_col_offset, 

767 parent=parent, 

768 ) 

769 

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

771 self.elt = elt 

772 self.generators = generators 

773 

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

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

776 

777 :returns: The boolean value of this node. 

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

779 :rtype: Uninferable 

780 """ 

781 return util.Uninferable 

782 

783 def get_children(self): 

784 yield self.elt 

785 

786 yield from self.generators 

787 

788 

789class ListComp(ComprehensionScope): 

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

791 

792 >>> import astroid 

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

794 >>> node 

795 <ListComp l.1 at 0x7f23b2e418d0> 

796 """ 

797 

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

799 _other_other_fields = ("locals",) 

800 

801 elt: NodeNG 

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

803 

804 def __init__( 

805 self, 

806 lineno: int, 

807 col_offset: int, 

808 parent: NodeNG, 

809 *, 

810 end_lineno: int | None, 

811 end_col_offset: int | None, 

812 ) -> None: 

813 self.locals = {} 

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

815 

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

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

818 

819 super().__init__( 

820 lineno=lineno, 

821 col_offset=col_offset, 

822 end_lineno=end_lineno, 

823 end_col_offset=end_col_offset, 

824 parent=parent, 

825 ) 

826 

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

828 self.elt = elt 

829 self.generators = generators 

830 

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

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

833 

834 :returns: The boolean value of this node. 

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

836 :rtype: Uninferable 

837 """ 

838 return util.Uninferable 

839 

840 def get_children(self): 

841 yield self.elt 

842 

843 yield from self.generators 

844 

845 

846def _infer_decorator_callchain(node): 

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

848 static or a classmethod. 

849 """ 

850 if not isinstance(node, FunctionDef): 

851 return None 

852 if not node.parent: 

853 return None 

854 try: 

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

856 except InferenceError: 

857 return None 

858 if isinstance(result, bases.Instance): 

859 result = result._proxied 

860 if isinstance(result, ClassDef): 

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

862 return "classmethod" 

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

864 return "staticmethod" 

865 if isinstance(result, FunctionDef): 

866 if not result.decorators: 

867 return None 

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

869 for decorator in result.decorators.nodes: 

870 if isinstance(decorator, node_classes.Name): 

871 if decorator.name in BUILTIN_DESCRIPTORS: 

872 return decorator.name 

873 if ( 

874 isinstance(decorator, node_classes.Attribute) 

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

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

877 and decorator.attrname in BUILTIN_DESCRIPTORS 

878 ): 

879 return decorator.attrname 

880 return None 

881 

882 

883class Lambda(_base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG): 

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

885 

886 >>> import astroid 

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

888 >>> node 

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

890 """ 

891 

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

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

894 name = "<lambda>" 

895 is_lambda = True 

896 special_attributes = FunctionModel() 

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

898 

899 args: Arguments 

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

901 

902 body: NodeNG 

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

904 

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

906 return 0 

907 

908 @property 

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

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

911 

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

913 """ 

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

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

916 return "method" 

917 return "function" 

918 

919 def __init__( 

920 self, 

921 lineno: int, 

922 col_offset: int, 

923 parent: NodeNG, 

924 *, 

925 end_lineno: int | None, 

926 end_col_offset: int | None, 

927 ): 

928 self.locals = {} 

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

930 

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

932 

933 super().__init__( 

934 lineno=lineno, 

935 col_offset=col_offset, 

936 end_lineno=end_lineno, 

937 end_col_offset=end_col_offset, 

938 parent=parent, 

939 ) 

940 

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

942 self.args = args 

943 self.body = body 

944 

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

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

947 

948 :returns: The name of the type. 

949 """ 

950 if "method" in self.type: 

951 return "builtins.instancemethod" 

952 return "builtins.function" 

953 

954 def display_type(self) -> str: 

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

956 

957 :returns: The type of this node. 

958 :rtype: str 

959 """ 

960 if "method" in self.type: 

961 return "Method" 

962 return "Function" 

963 

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

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

966 

967 :returns: Whether this defines something that is callable 

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

969 """ 

970 return True 

971 

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

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

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

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

976 

977 :returns: The names of the arguments. 

978 :rtype: list(str) 

979 """ 

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

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

982 else: 

983 names = [] 

984 

985 return names 

986 

987 def infer_call_result( 

988 self, 

989 caller: SuccessfulInferenceResult | None, 

990 context: InferenceContext | None = None, 

991 ) -> Iterator[InferenceResult]: 

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

993 return self.body.infer(context) 

994 

995 def scope_lookup( 

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

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

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

999 

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

1001 Any assignments after the given node are ignored. 

1002 

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

1004 

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

1006 

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

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

1009 globals or builtin). 

1010 """ 

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

1012 self.args.kw_defaults and node in self.args.kw_defaults 

1013 ): 

1014 if not self.parent: 

1015 raise ParentMissingError(target=self) 

1016 frame = self.parent.frame() 

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

1018 # value to the defined function 

1019 offset = -1 

1020 else: 

1021 # check this is not used in function decorators 

1022 frame = self 

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

1024 

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

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

1027 

1028 :returns: The boolean value of this node. 

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

1030 """ 

1031 return True 

1032 

1033 def get_children(self): 

1034 yield self.args 

1035 yield self.body 

1036 

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

1038 """The node's frame node. 

1039 

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

1041 :class:`ClassDef` or :class:`Lambda`. 

1042 

1043 :returns: The node itself. 

1044 """ 

1045 return self 

1046 

1047 def getattr( 

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

1049 ) -> list[NodeNG]: 

1050 if not name: 

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

1052 

1053 found_attrs = [] 

1054 if name in self.instance_attrs: 

1055 found_attrs = self.instance_attrs[name] 

1056 if name in self.special_attributes: 

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

1058 if found_attrs: 

1059 return found_attrs 

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

1061 

1062 def _infer( 

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

1064 ) -> Generator[Lambda]: 

1065 yield self 

1066 

1067 def _get_yield_nodes_skip_functions(self): 

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

1069 yield from self.body._get_yield_nodes_skip_functions() 

1070 

1071 

1072class FunctionDef( 

1073 _base_nodes.MultiLineBlockNode, 

1074 _base_nodes.FilterStmtsBaseNode, 

1075 _base_nodes.Statement, 

1076 LocalsDictNodeNG, 

1077): 

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

1079 

1080 >>> import astroid 

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

1082 ... def my_func(arg): 

1083 ... return arg + 1 

1084 ... ''') 

1085 >>> node 

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

1087 """ 

1088 

1089 _astroid_fields = ( 

1090 "decorators", 

1091 "args", 

1092 "returns", 

1093 "type_params", 

1094 "doc_node", 

1095 "body", 

1096 ) 

1097 _multi_line_block_fields = ("body",) 

1098 returns = None 

1099 

1100 decorators: node_classes.Decorators | None 

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

1102 

1103 doc_node: Const | None 

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

1105 

1106 args: Arguments 

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

1108 

1109 is_function = True 

1110 """Whether this node indicates a function. 

1111 

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

1113 

1114 :type: bool 

1115 """ 

1116 type_annotation = None 

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

1118 

1119 :type: NodeNG or None 

1120 """ 

1121 type_comment_args = None 

1122 """ 

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

1124 passed by a type comment 

1125 """ 

1126 type_comment_returns = None 

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

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

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

1130 _other_other_fields = ( 

1131 "locals", 

1132 "_type", 

1133 "type_comment_returns", 

1134 "type_comment_args", 

1135 ) 

1136 _type = None 

1137 

1138 name = "<functiondef>" 

1139 

1140 special_attributes = FunctionModel() 

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

1142 

1143 def __init__( 

1144 self, 

1145 name: str, 

1146 lineno: int, 

1147 col_offset: int, 

1148 parent: NodeNG, 

1149 *, 

1150 end_lineno: int | None, 

1151 end_col_offset: int | None, 

1152 ) -> None: 

1153 self.name = name 

1154 """The name of the function.""" 

1155 

1156 self.locals = {} 

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

1158 

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

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

1161 

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

1163 [] 

1164 ) 

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

1166 

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

1168 

1169 super().__init__( 

1170 lineno=lineno, 

1171 col_offset=col_offset, 

1172 end_lineno=end_lineno, 

1173 end_col_offset=end_col_offset, 

1174 parent=parent, 

1175 ) 

1176 

1177 def postinit( 

1178 self, 

1179 args: Arguments, 

1180 body: list[NodeNG], 

1181 decorators: node_classes.Decorators | None = None, 

1182 returns=None, 

1183 type_comment_returns=None, 

1184 type_comment_args=None, 

1185 *, 

1186 position: Position | None = None, 

1187 doc_node: Const | None = None, 

1188 type_params: ( 

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

1190 ) = None, 

1191 ): 

1192 """Do some setup after initialisation. 

1193 

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

1195 

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

1197 

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

1199 method or function. 

1200 :params type_comment_returns: 

1201 The return type annotation passed via a type comment. 

1202 :params type_comment_args: 

1203 The args type annotation passed via a type comment. 

1204 :params position: 

1205 Position of function keyword(s) and name. 

1206 :param doc_node: 

1207 The doc node associated with this node. 

1208 :param type_params: 

1209 The type_params associated with this node. 

1210 """ 

1211 self.args = args 

1212 self.body = body 

1213 self.decorators = decorators 

1214 self.returns = returns 

1215 self.type_comment_returns = type_comment_returns 

1216 self.type_comment_args = type_comment_args 

1217 self.position = position 

1218 self.doc_node = doc_node 

1219 self.type_params = type_params or [] 

1220 

1221 @cached_property 

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

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

1224 

1225 Additional decorators are considered when they are used as 

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

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

1228 decoration. 

1229 """ 

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

1231 return [] 

1232 

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

1234 for assign in frame._assign_nodes_in_scope: 

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

1236 assign.value.func, node_classes.Name 

1237 ): 

1238 for assign_node in assign.targets: 

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

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

1241 continue 

1242 

1243 if assign_node.name != self.name: 

1244 # Interested only in the assignment nodes that 

1245 # decorates the current method. 

1246 continue 

1247 try: 

1248 meth = frame[self.name] 

1249 except KeyError: 

1250 continue 

1251 else: 

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

1253 # original method. 

1254 if ( 

1255 isinstance(meth, FunctionDef) 

1256 and assign_node.frame() == frame 

1257 ): 

1258 decorators.append(assign.value) 

1259 return decorators 

1260 

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

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

1263 

1264 :returns: The name of the type. 

1265 """ 

1266 if "method" in self.type: 

1267 return "builtins.instancemethod" 

1268 return "builtins.function" 

1269 

1270 def display_type(self) -> str: 

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

1272 

1273 :returns: The type of this node. 

1274 :rtype: str 

1275 """ 

1276 if "method" in self.type: 

1277 return "Method" 

1278 return "Function" 

1279 

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

1281 return True 

1282 

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

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

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

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

1287 

1288 :returns: The names of the arguments. 

1289 :rtype: list(str) 

1290 """ 

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

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

1293 else: 

1294 names = [] 

1295 

1296 return names 

1297 

1298 def getattr( 

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

1300 ) -> list[NodeNG]: 

1301 if not name: 

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

1303 

1304 found_attrs = [] 

1305 if name in self.instance_attrs: 

1306 found_attrs = self.instance_attrs[name] 

1307 if name in self.special_attributes: 

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

1309 if found_attrs: 

1310 return found_attrs 

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

1312 

1313 @cached_property 

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

1315 """The function type for this node. 

1316 

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

1318 """ 

1319 for decorator in self.extra_decorators: 

1320 if decorator.func.name in BUILTIN_DESCRIPTORS: 

1321 return decorator.func.name 

1322 

1323 if not self.parent: 

1324 raise ParentMissingError(target=self) 

1325 

1326 frame = self.parent.frame() 

1327 type_name = "function" 

1328 if isinstance(frame, ClassDef): 

1329 if self.name == "__new__": 

1330 return "classmethod" 

1331 if self.name == "__init_subclass__": 

1332 return "classmethod" 

1333 if self.name == "__class_getitem__": 

1334 return "classmethod" 

1335 

1336 type_name = "method" 

1337 

1338 if not self.decorators: 

1339 return type_name 

1340 

1341 for node in self.decorators.nodes: 

1342 if isinstance(node, node_classes.Name): 

1343 if node.name in BUILTIN_DESCRIPTORS: 

1344 return node.name 

1345 if ( 

1346 isinstance(node, node_classes.Attribute) 

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

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

1349 and node.attrname in BUILTIN_DESCRIPTORS 

1350 ): 

1351 return node.attrname 

1352 

1353 if isinstance(node, node_classes.Call): 

1354 # Handle the following case: 

1355 # @some_decorator(arg1, arg2) 

1356 # def func(...) 

1357 # 

1358 try: 

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

1360 except (InferenceError, StopIteration): 

1361 continue 

1362 _type = _infer_decorator_callchain(current) 

1363 if _type is not None: 

1364 return _type 

1365 

1366 try: 

1367 for inferred in node.infer(): 

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

1369 _type = _infer_decorator_callchain(inferred) 

1370 if _type is not None: 

1371 return _type 

1372 

1373 if not isinstance(inferred, ClassDef): 

1374 continue 

1375 for ancestor in inferred.ancestors(): 

1376 if not isinstance(ancestor, ClassDef): 

1377 continue 

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

1379 return "classmethod" 

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

1381 return "staticmethod" 

1382 except InferenceError: 

1383 pass 

1384 return type_name 

1385 

1386 @cached_property 

1387 def fromlineno(self) -> int: 

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

1389 

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

1391 """ 

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

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

1394 lineno = self.lineno or 0 

1395 if self.decorators is not None: 

1396 lineno += sum( 

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

1398 ) 

1399 

1400 return lineno or 0 

1401 

1402 @cached_property 

1403 def blockstart_tolineno(self): 

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

1405 

1406 :type: int 

1407 """ 

1408 if self.returns: 

1409 return self.returns.tolineno 

1410 return self.args.tolineno 

1411 

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

1413 return 1 if self.is_bound() else 0 

1414 

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

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

1417 

1418 :param lineno: Unused. 

1419 

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

1421 """ 

1422 return self.fromlineno, self.tolineno 

1423 

1424 def igetattr( 

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

1426 ) -> Iterator[InferenceResult]: 

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

1428 try: 

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

1430 except AttributeInferenceError as error: 

1431 raise InferenceError( 

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

1433 ) from error 

1434 

1435 def is_method(self) -> bool: 

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

1437 

1438 :returns: Whether this is a method. 

1439 """ 

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

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

1442 return ( 

1443 self.type != "function" 

1444 and self.parent is not None 

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

1446 ) 

1447 

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

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

1450 

1451 :param context: 

1452 An inference context that can be passed to inference functions 

1453 :returns: The names of the decorators. 

1454 """ 

1455 result = set() 

1456 decoratornodes = [] 

1457 if self.decorators is not None: 

1458 decoratornodes += self.decorators.nodes 

1459 decoratornodes += self.extra_decorators 

1460 for decnode in decoratornodes: 

1461 try: 

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

1463 result.add(infnode.qname()) 

1464 except InferenceError: 

1465 continue 

1466 return result 

1467 

1468 def is_bound(self) -> bool: 

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

1470 

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

1472 """ 

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

1474 

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

1476 """Check if the method is abstract. 

1477 

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

1479 * The only statement is 'raise NotImplementedError' 

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

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

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

1483 

1484 :returns: Whether the method is abstract. 

1485 """ 

1486 if self.decorators: 

1487 for node in self.decorators.nodes: 

1488 try: 

1489 inferred = next(node.infer()) 

1490 except (InferenceError, StopIteration): 

1491 continue 

1492 if inferred and inferred.qname() in { 

1493 "abc.abstractproperty", 

1494 "abc.abstractmethod", 

1495 }: 

1496 return True 

1497 

1498 for child_node in self.body: 

1499 if isinstance(child_node, node_classes.Raise): 

1500 if any_raise_is_abstract: 

1501 return True 

1502 if child_node.raises_not_implemented(): 

1503 return True 

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

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

1506 if pass_is_abstract: 

1507 return True 

1508 

1509 return False 

1510 

1511 def is_generator(self) -> bool: 

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

1513 

1514 :returns: Whether this is a generator function. 

1515 """ 

1516 yields_without_lambdas = set(self._get_yield_nodes_skip_lambdas()) 

1517 yields_without_functions = set(self._get_yield_nodes_skip_functions()) 

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

1519 return bool(yields_without_lambdas & yields_without_functions) 

1520 

1521 def _infer( 

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

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

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

1525 

1526 if not (self.decorators and bases._is_property(self)): 

1527 yield self 

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

1529 

1530 if not self.parent: 

1531 raise ParentMissingError(target=self) 

1532 prop_func = objects.Property( 

1533 function=self, 

1534 name=self.name, 

1535 lineno=self.lineno, 

1536 parent=self.parent, 

1537 col_offset=self.col_offset, 

1538 ) 

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

1540 yield prop_func 

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

1542 

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

1544 """Infer what the function yields when called 

1545 

1546 :returns: What the function yields 

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

1548 """ 

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

1550 if yield_.value is None: 

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

1552 elif yield_.scope() == self: 

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

1554 

1555 def infer_call_result( 

1556 self, 

1557 caller: SuccessfulInferenceResult | None, 

1558 context: InferenceContext | None = None, 

1559 ) -> Iterator[InferenceResult]: 

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

1561 if context is None: 

1562 context = InferenceContext() 

1563 if self.is_generator(): 

1564 if isinstance(self, AsyncFunctionDef): 

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

1566 else: 

1567 generator_cls = bases.Generator 

1568 result = generator_cls(self, generator_initial_context=context) 

1569 yield result 

1570 return 

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

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

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

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

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

1576 # generators, and filter it out later. 

1577 if ( 

1578 self.name == "with_metaclass" 

1579 and caller is not None 

1580 and self.args.args 

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

1582 and self.args.vararg is not None 

1583 ): 

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

1585 assert caller.args.args is not None 

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

1587 elif isinstance(caller.args, list): 

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

1589 else: 

1590 raise TypeError( # pragma: no cover 

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

1592 ) 

1593 if isinstance(metaclass, ClassDef): 

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

1595 new_class = ClassDef( 

1596 name="temporary_class", 

1597 lineno=0, 

1598 col_offset=0, 

1599 end_lineno=0, 

1600 end_col_offset=0, 

1601 parent=SYNTHETIC_ROOT, 

1602 ) 

1603 new_class.hide = True 

1604 new_class.postinit( 

1605 bases=[ 

1606 base 

1607 for base in class_bases 

1608 if not isinstance(base, util.UninferableBase) 

1609 ], 

1610 body=[], 

1611 decorators=None, 

1612 metaclass=metaclass, 

1613 ) 

1614 yield new_class 

1615 return 

1616 returns = self._get_return_nodes_skip_functions() 

1617 

1618 first_return = next(returns, None) 

1619 if not first_return: 

1620 if self.body: 

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

1622 yield util.Uninferable 

1623 else: 

1624 yield node_classes.Const(None) 

1625 return 

1626 

1627 # Builtin dunder methods have empty bodies, return Uninferable. 

1628 if ( 

1629 len(self.body) == 0 

1630 and self.name.startswith("__") 

1631 and self.name.endswith("__") 

1632 and self.root().qname() == "builtins" 

1633 ): 

1634 yield util.Uninferable 

1635 return 

1636 

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

1638 

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

1640 if returnnode.value is None: 

1641 yield node_classes.Const(None) 

1642 else: 

1643 try: 

1644 yield from returnnode.value.infer(context) 

1645 except InferenceError: 

1646 yield util.Uninferable 

1647 

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

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

1650 

1651 :returns: The boolean value of this node. 

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

1653 """ 

1654 return True 

1655 

1656 def get_children(self): 

1657 if self.decorators is not None: 

1658 yield self.decorators 

1659 

1660 yield self.args 

1661 

1662 if self.returns is not None: 

1663 yield self.returns 

1664 yield from self.type_params 

1665 

1666 yield from self.body 

1667 

1668 def scope_lookup( 

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

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

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

1672 if name == "__class__": 

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

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

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

1676 # when `__class__` is being used. 

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

1678 return self, [frame] 

1679 

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

1681 self.args.kw_defaults and node in self.args.kw_defaults 

1682 ): 

1683 if not self.parent: 

1684 raise ParentMissingError(target=self) 

1685 frame = self.parent.frame() 

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

1687 # value to the defined function 

1688 offset = -1 

1689 else: 

1690 # check this is not used in function decorators 

1691 frame = self 

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

1693 

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

1695 """The node's frame node. 

1696 

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

1698 :class:`ClassDef` or :class:`Lambda`. 

1699 

1700 :returns: The node itself. 

1701 """ 

1702 return self 

1703 

1704 

1705class AsyncFunctionDef(FunctionDef): 

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

1707 

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

1709 created with the `async` keyword. 

1710 

1711 >>> import astroid 

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

1713 async def func(things): 

1714 async for thing in things: 

1715 print(thing) 

1716 ''') 

1717 >>> node 

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

1719 >>> node.body[0] 

1720 <AsyncFor l.3 at 0x7f23b2e417b8> 

1721 """ 

1722 

1723 

1724def _is_metaclass( 

1725 klass: ClassDef, 

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

1727 context: InferenceContext | None = None, 

1728) -> bool: 

1729 """Return if the given class can be 

1730 used as a metaclass. 

1731 """ 

1732 if klass.name == "type": 

1733 return True 

1734 if seen is None: 

1735 seen = set() 

1736 for base in klass.bases: 

1737 try: 

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

1739 baseobj_name = baseobj.qname() 

1740 if baseobj_name in seen: 

1741 continue 

1742 

1743 seen.add(baseobj_name) 

1744 if isinstance(baseobj, bases.Instance): 

1745 # not abstract 

1746 return False 

1747 if baseobj is klass: 

1748 continue 

1749 if not isinstance(baseobj, ClassDef): 

1750 continue 

1751 if baseobj._type == "metaclass": 

1752 return True 

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

1754 return True 

1755 except InferenceError: 

1756 continue 

1757 return False 

1758 

1759 

1760def _class_type( 

1761 klass: ClassDef, 

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

1763 context: InferenceContext | None = None, 

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

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

1766 from 'regular' classes 

1767 """ 

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

1769 if klass._type is not None: 

1770 return klass._type 

1771 if _is_metaclass(klass, context=context): 

1772 klass._type = "metaclass" 

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

1774 klass._type = "exception" 

1775 else: 

1776 if ancestors is None: 

1777 ancestors = set() 

1778 klass_name = klass.qname() 

1779 if klass_name in ancestors: 

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

1781 klass._type = "class" 

1782 return "class" 

1783 ancestors.add(klass_name) 

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

1785 name = _class_type(base, ancestors) 

1786 if name != "class": 

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

1788 # don't propagate it if the current class 

1789 # can't be a metaclass 

1790 continue 

1791 klass._type = base.type 

1792 break 

1793 if klass._type is None: 

1794 klass._type = "class" 

1795 return klass._type 

1796 

1797 

1798def get_wrapping_class(node): 

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

1800 

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

1802 is a parent for the said node. 

1803 

1804 :returns: The class that wraps the given node 

1805 :rtype: ClassDef or None 

1806 """ 

1807 

1808 klass = node.frame() 

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

1810 if klass.parent is None: 

1811 klass = None 

1812 else: 

1813 klass = klass.parent.frame() 

1814 return klass 

1815 

1816 

1817class ClassDef( 

1818 _base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG, _base_nodes.Statement 

1819): 

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

1821 

1822 >>> import astroid 

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

1824 class Thing: 

1825 def my_meth(self, arg): 

1826 return arg + self.offset 

1827 ''') 

1828 >>> node 

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

1830 """ 

1831 

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

1833 # by a raw factories 

1834 

1835 # a dictionary of class instances attributes 

1836 _astroid_fields = ( 

1837 "decorators", 

1838 "bases", 

1839 "keywords", 

1840 "doc_node", 

1841 "body", 

1842 "type_params", 

1843 ) # name 

1844 

1845 decorators = None 

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

1847 

1848 :type: Decorators or None 

1849 """ 

1850 special_attributes = ClassModel() 

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

1852 

1853 :type: objectmodel.ClassModel 

1854 """ 

1855 

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

1857 _metaclass: NodeNG | None = None 

1858 _metaclass_hack = False 

1859 hide = False 

1860 type = property( 

1861 _class_type, 

1862 doc=( 

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

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

1865 ":type: str" 

1866 ), 

1867 ) 

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

1869 _other_other_fields = "locals" 

1870 

1871 def __init__( 

1872 self, 

1873 name: str, 

1874 lineno: int, 

1875 col_offset: int, 

1876 parent: NodeNG, 

1877 *, 

1878 end_lineno: int | None, 

1879 end_col_offset: int | None, 

1880 ) -> None: 

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

1882 self.locals = {} 

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

1884 

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

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

1887 

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

1889 """ 

1890 

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

1892 """What the class inherits from.""" 

1893 

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

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

1896 

1897 self.name = name 

1898 """The name of the class.""" 

1899 

1900 self.decorators = None 

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

1902 

1903 self.doc_node: Const | None = None 

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

1905 

1906 self.is_dataclass: bool = False 

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

1908 

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

1910 [] 

1911 ) 

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

1913 

1914 super().__init__( 

1915 lineno=lineno, 

1916 col_offset=col_offset, 

1917 end_lineno=end_lineno, 

1918 end_col_offset=end_col_offset, 

1919 parent=parent, 

1920 ) 

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

1922 self.add_local_node(node, local_name) 

1923 

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

1925 protocols.instance_class_infer_binary_op 

1926 ) 

1927 

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

1929 return 1 

1930 

1931 def implicit_locals(self): 

1932 """Get implicitly defined class definition locals. 

1933 

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

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

1936 """ 

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

1938 # __qualname__ is defined in PEP3155 

1939 locals_ += ( 

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

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

1942 ) 

1943 return locals_ 

1944 

1945 # pylint: disable=redefined-outer-name 

1946 def postinit( 

1947 self, 

1948 bases: list[SuccessfulInferenceResult], 

1949 body: list[NodeNG], 

1950 decorators: node_classes.Decorators | None, 

1951 newstyle: bool | None = None, 

1952 metaclass: NodeNG | None = None, 

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

1954 *, 

1955 position: Position | None = None, 

1956 doc_node: Const | None = None, 

1957 type_params: ( 

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

1959 ) = None, 

1960 ) -> None: 

1961 if keywords is not None: 

1962 self.keywords = keywords 

1963 self.bases = bases 

1964 self.body = body 

1965 self.decorators = decorators 

1966 self._metaclass = metaclass 

1967 self.position = position 

1968 self.doc_node = doc_node 

1969 self.type_params = type_params or [] 

1970 

1971 @cached_property 

1972 def blockstart_tolineno(self): 

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

1974 

1975 :type: int 

1976 """ 

1977 if self.bases: 

1978 return self.bases[-1].tolineno 

1979 

1980 return self.fromlineno 

1981 

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

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

1984 

1985 :param lineno: Unused. 

1986 

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

1988 """ 

1989 return self.fromlineno, self.tolineno 

1990 

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

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

1993 

1994 :returns: The name of the type. 

1995 """ 

1996 return "builtins.type" 

1997 

1998 def display_type(self) -> str: 

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

2000 

2001 :returns: The type of this node. 

2002 :rtype: str 

2003 """ 

2004 return "Class" 

2005 

2006 def callable(self) -> bool: 

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

2008 

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

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

2011 """ 

2012 return True 

2013 

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

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

2016 

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

2018 :type type_name: str 

2019 

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

2021 """ 

2022 if self.qname() == type_name: 

2023 return True 

2024 

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

2026 

2027 def _infer_type_call(self, caller, context): 

2028 try: 

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

2030 except StopIteration as e: 

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

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

2033 name_node.value, str 

2034 ): 

2035 name = name_node.value 

2036 else: 

2037 return util.Uninferable 

2038 

2039 result = ClassDef( 

2040 name, 

2041 lineno=0, 

2042 col_offset=0, 

2043 end_lineno=0, 

2044 end_col_offset=0, 

2045 parent=caller.parent, 

2046 ) 

2047 

2048 # Get the bases of the class. 

2049 try: 

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

2051 except StopIteration as e: 

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

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

2054 bases = [] 

2055 for base in class_bases.itered(): 

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

2057 if inferred: 

2058 bases.append( 

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

2060 ) 

2061 result.bases = bases 

2062 else: 

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

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

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

2066 return util.Uninferable 

2067 

2068 # Get the members of the class 

2069 try: 

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

2071 except (InferenceError, StopIteration): 

2072 members = None 

2073 

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

2075 for attr, value in members.items: 

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

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

2078 

2079 return result 

2080 

2081 def infer_call_result( 

2082 self, 

2083 caller: SuccessfulInferenceResult | None, 

2084 context: InferenceContext | None = None, 

2085 ) -> Iterator[InferenceResult]: 

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

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

2088 result = self._infer_type_call(caller, context) 

2089 yield result 

2090 return 

2091 

2092 dunder_call = None 

2093 try: 

2094 metaclass = self.metaclass(context=context) 

2095 if metaclass is not None: 

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

2097 # Otherwise we will find ObjectModel.__call__ which will 

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

2099 # handled later. 

2100 if "__call__" in metaclass.locals: 

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

2102 except (AttributeInferenceError, StopIteration): 

2103 pass 

2104 

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

2106 # Call type.__call__ if not set metaclass 

2107 # (since type is the default metaclass) 

2108 context = bind_context_to_node(context, self) 

2109 # ``infer_call_result`` may be called through the public API without 

2110 # a call context (it defaults to None); only annotate the callee 

2111 # when there is a call context to annotate. 

2112 if context.callcontext: 

2113 context.callcontext.callee = dunder_call 

2114 yield from dunder_call.infer_call_result(caller, context) 

2115 else: 

2116 yield self.instantiate_class() 

2117 

2118 def scope_lookup( 

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

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

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

2122 

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

2124 Any assignments after the given node are ignored. 

2125 

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

2127 

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

2129 

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

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

2132 globals or builtin). 

2133 """ 

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

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

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

2137 # inside this class. 

2138 lookup_upper_frame = ( 

2139 isinstance(node.parent, node_classes.Decorators) 

2140 and name in AstroidManager().builtins_module 

2141 ) 

2142 if ( 

2143 any( 

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

2145 for base in self.bases 

2146 ) 

2147 or lookup_upper_frame 

2148 ): 

2149 # Handle the case where we have either a name 

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

2151 # the actual definition or the case where we have 

2152 # a Getattr node, with that name. 

2153 # 

2154 # name = ... 

2155 # class A(name): 

2156 # def name(self): ... 

2157 # 

2158 # import name 

2159 # class A(name.Name): 

2160 # def name(self): ... 

2161 if not self.parent: 

2162 raise ParentMissingError(target=self) 

2163 frame = self.parent.frame() 

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

2165 # the defined class 

2166 offset = -1 

2167 else: 

2168 frame = self 

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

2170 

2171 @property 

2172 def basenames(self): 

2173 """The names of the parent classes 

2174 

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

2176 

2177 :type: list(str) 

2178 """ 

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

2180 

2181 def ancestors( 

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

2183 ) -> Generator[ClassDef]: 

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

2185 

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

2187 

2188 :returns: The base classes 

2189 """ 

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

2191 # FIXME: inference make infinite loops possible here 

2192 yielded = {self} 

2193 if context is None: 

2194 context = InferenceContext() 

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

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

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

2198 return 

2199 

2200 for stmt in self.bases: 

2201 with context.restore_path(): 

2202 try: 

2203 for baseobj in stmt.infer(context): 

2204 if not isinstance(baseobj, ClassDef): 

2205 if isinstance(baseobj, bases.Instance): 

2206 baseobj = baseobj._proxied 

2207 else: 

2208 continue 

2209 if not baseobj.hide: 

2210 if baseobj in yielded: 

2211 continue 

2212 yielded.add(baseobj) 

2213 yield baseobj 

2214 if not recurs: 

2215 continue 

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

2217 if grandpa is self: 

2218 # This class is the ancestor of itself. 

2219 break 

2220 if grandpa in yielded: 

2221 continue 

2222 yielded.add(grandpa) 

2223 yield grandpa 

2224 except InferenceError: 

2225 continue 

2226 

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

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

2229 

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

2231 :type name: str 

2232 

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

2234 :rtype: iterable(NodeNG) 

2235 """ 

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

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

2238 try: 

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

2240 except MroError: 

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

2242 # a sane MRO. 

2243 ancestors = self.ancestors(context=context) 

2244 for astroid in ancestors: 

2245 if name in astroid: 

2246 yield astroid 

2247 

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

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

2250 

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

2252 :type name: str 

2253 

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

2255 an instance attribute. 

2256 :rtype: iterable(NodeNG) 

2257 """ 

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

2259 if name in astroid.instance_attrs: 

2260 yield astroid 

2261 

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

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

2264 

2265 :param node: The node to check for. 

2266 :type node: NodeNG 

2267 

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

2269 """ 

2270 return node in self.bases 

2271 

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

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

2274 

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

2276 

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

2278 :rtype: list(NodeNG) 

2279 

2280 :raises AttributeInferenceError: If no attribute with this name 

2281 can be found in this class or parent classes. 

2282 """ 

2283 result = [] 

2284 if name in self.locals: 

2285 result = self.locals[name] 

2286 else: 

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

2288 if class_node: 

2289 result = class_node.locals[name] 

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

2291 if result: 

2292 return result 

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

2294 

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

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

2297 

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

2299 

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

2301 :rtype: list(NodeNG) 

2302 

2303 :raises AttributeInferenceError: If no attribute with this name 

2304 can be found in this class or parent classes. 

2305 """ 

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

2307 # which could lead to infinite loop. 

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

2309 # get all values from parents 

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

2311 values += class_node.instance_attrs[name] 

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

2313 if values: 

2314 return values 

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

2316 

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

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

2319 

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

2321 """ 

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

2323 

2324 try: 

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

2326 # Subclasses of exceptions can be exception instances 

2327 return objects.ExceptionInstance(self) 

2328 except MroError: 

2329 pass 

2330 return bases.Instance(self) 

2331 

2332 def getattr( 

2333 self, 

2334 name: str, 

2335 context: InferenceContext | None = None, 

2336 class_context: bool = True, 

2337 ) -> list[InferenceResult]: 

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

2339 

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

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

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

2343 the attribute has not been 

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

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

2346 attribute is accessed from a class context, 

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

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

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

2350 metaclass will be done. 

2351 

2352 :param name: The attribute to look for. 

2353 

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

2355 

2356 :returns: The attribute. 

2357 

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

2359 """ 

2360 if not name: 

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

2362 

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

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

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

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

2367 

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

2369 special_attr = self.special_attributes.lookup(name) 

2370 if not isinstance(special_attr, node_classes.Unknown): 

2371 result = [special_attr] 

2372 return result 

2373 

2374 if class_context: 

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

2376 

2377 result: list[InferenceResult] = [] 

2378 for value in values: 

2379 if isinstance(value, node_classes.AssignName): 

2380 stmt = value.statement() 

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

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

2383 continue 

2384 result.append(value) 

2385 

2386 if not result: 

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

2388 

2389 return result 

2390 

2391 @lru_cache(maxsize=1024) # noqa 

2392 def _metaclass_lookup_attribute(self, name, context): 

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

2394 attrs = set() 

2395 implicit_meta = self.implicit_metaclass() 

2396 context = copy_context(context) 

2397 metaclass = self.metaclass(context=context) 

2398 for cls in (implicit_meta, metaclass): 

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

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

2401 attrs.update(cls_attributes) 

2402 return attrs 

2403 

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

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

2406 

2407 try: 

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

2409 except AttributeInferenceError: 

2410 return 

2411 

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

2413 if not isinstance(attr, FunctionDef): 

2414 yield attr 

2415 continue 

2416 

2417 if isinstance(attr, objects.Property): 

2418 yield attr 

2419 continue 

2420 if attr.type == "classmethod": 

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

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

2423 # from where the attribute is retrieved. 

2424 # get_wrapping_class could return None, so just 

2425 # default to the current class. 

2426 frame = get_wrapping_class(attr) or self 

2427 yield bases.BoundMethod(attr, frame) 

2428 elif attr.type == "staticmethod": 

2429 yield attr 

2430 else: 

2431 yield bases.BoundMethod(attr, self) 

2432 

2433 def igetattr( 

2434 self, 

2435 name: str, 

2436 context: InferenceContext | None = None, 

2437 class_context: bool = True, 

2438 ) -> Iterator[InferenceResult]: 

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

2440 

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

2442 

2443 :returns: The inferred possible values. 

2444 """ 

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

2446 

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

2448 # instance 

2449 context = copy_context(context) 

2450 context.lookupname = name 

2451 

2452 metaclass = self.metaclass(context=context) 

2453 try: 

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

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

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

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

2458 if len(attributes) > 1: 

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

2460 first_scope = first_attr.parent.scope() 

2461 attributes = [first_attr] + [ 

2462 attr 

2463 for attr in attributes 

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

2465 ] 

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

2467 setter = None 

2468 for function in functions: 

2469 dec_names = function.decoratornames(context=context) 

2470 for dec_name in dec_names: 

2471 if dec_name is util.Uninferable: 

2472 continue 

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

2474 setter = function 

2475 if setter: 

2476 break 

2477 if functions: 

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

2479 last_function = functions[-1] 

2480 attributes = [ 

2481 a 

2482 for a in attributes 

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

2484 ] 

2485 

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

2487 # yield Uninferable object instead of descriptors when necessary 

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

2489 inferred, bases.Instance 

2490 ): 

2491 try: 

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

2493 except AttributeInferenceError: 

2494 yield inferred 

2495 else: 

2496 yield util.Uninferable 

2497 elif isinstance(inferred, objects.Property): 

2498 function = inferred.function 

2499 if not class_context: 

2500 if not context.callcontext and not setter: 

2501 context.callcontext = CallContext( 

2502 args=function.args.arguments, callee=function 

2503 ) 

2504 # Through an instance so we can solve the property 

2505 yield from function.infer_call_result( 

2506 caller=self, context=context 

2507 ) 

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

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

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

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

2512 # instead, then we return the property object 

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

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

2515 # the class itself. 

2516 yield from function.infer_call_result( 

2517 caller=self, context=context 

2518 ) 

2519 else: 

2520 yield inferred 

2521 else: 

2522 yield function_to_method(inferred, self) 

2523 except AttributeInferenceError as error: 

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

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

2526 yield util.Uninferable 

2527 else: 

2528 raise InferenceError( 

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

2530 ) from error 

2531 

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

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

2534 

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

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

2537 will return True. 

2538 

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

2540 """ 

2541 

2542 def _valid_getattr(node): 

2543 root = node.root() 

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

2545 

2546 try: 

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

2548 except AttributeInferenceError: 

2549 try: 

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

2551 return _valid_getattr(getattribute) 

2552 except AttributeInferenceError: 

2553 pass 

2554 return False 

2555 

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

2557 """Return the inference of a subscript. 

2558 

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

2560 

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

2562 :rtype: NodeNG 

2563 

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

2565 ``__getitem__`` method. 

2566 """ 

2567 try: 

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

2569 except AttributeInferenceError as exc: 

2570 if isinstance(self, ClassDef): 

2571 # subscripting a class definition may be 

2572 # achieved thanks to __class_getitem__ method 

2573 # which is a classmethod defined in the class 

2574 # that supports subscript and not in the metaclass 

2575 try: 

2576 methods = self.getattr("__class_getitem__") 

2577 # Here it is assumed that the __class_getitem__ node is 

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

2579 # with more generic inference. 

2580 except AttributeInferenceError: 

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

2582 else: 

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

2584 

2585 method = methods[0] 

2586 

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

2588 new_context = bind_context_to_node(context, self) 

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

2590 

2591 try: 

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

2593 except AttributeError as exc: 

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

2595 # are subscriptable thanks to __class_getitem___ classmethod. 

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

2597 # EmptyNode doesn't have infer_call_result method yielding to 

2598 # AttributeError 

2599 if ( 

2600 isinstance(method, node_classes.EmptyNode) 

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

2602 ): 

2603 return self 

2604 # ``__class_getitem__`` may resolve to a non-callable node (e.g. an 

2605 # ``AssignName`` or an ``Import``), which has no 

2606 # ``infer_call_result`` method. 

2607 if not method.callable(): 

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

2609 raise 

2610 except InferenceError: 

2611 return util.Uninferable 

2612 

2613 def methods(self): 

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

2615 

2616 :returns: The methods defined on the class. 

2617 :rtype: iterable(FunctionDef) 

2618 """ 

2619 done = {} 

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

2621 for meth in astroid.mymethods(): 

2622 if meth.name in done: 

2623 continue 

2624 done[meth.name] = None 

2625 yield meth 

2626 

2627 def mymethods(self): 

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

2629 

2630 :returns: The methods defined on the class. 

2631 :rtype: iterable(FunctionDef) 

2632 """ 

2633 for member in self.values(): 

2634 if isinstance(member, FunctionDef): 

2635 yield member 

2636 

2637 def implicit_metaclass(self): 

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

2639 

2640 This will return an instance of builtins.type. 

2641 

2642 :returns: The metaclass. 

2643 :rtype: builtins.type 

2644 """ 

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

2646 

2647 def declared_metaclass( 

2648 self, context: InferenceContext | None = None 

2649 ) -> SuccessfulInferenceResult | None: 

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

2651 

2652 An explicit declared metaclass is defined 

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

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

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

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

2657 

2658 :returns: The metaclass of this class, 

2659 or None if one could not be found. 

2660 """ 

2661 for base in self.bases: 

2662 try: 

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

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

2665 self._metaclass = baseobj._metaclass 

2666 self._metaclass_hack = True 

2667 break 

2668 except InferenceError: 

2669 pass 

2670 

2671 if self._metaclass: 

2672 # Expects this from Py3k TreeRebuilder 

2673 try: 

2674 return next( 

2675 node 

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

2677 if not isinstance(node, util.UninferableBase) 

2678 ) 

2679 except (InferenceError, StopIteration): 

2680 return None 

2681 

2682 return None 

2683 

2684 def _find_metaclass( 

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

2686 ) -> SuccessfulInferenceResult | None: 

2687 if seen is None: 

2688 seen = set() 

2689 seen.add(self) 

2690 

2691 klass = self.declared_metaclass(context=context) 

2692 if klass is None: 

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

2694 if parent not in seen: 

2695 klass = parent._find_metaclass(seen) 

2696 if klass is not None: 

2697 break 

2698 return klass 

2699 

2700 def metaclass( 

2701 self, context: InferenceContext | None = None 

2702 ) -> SuccessfulInferenceResult | None: 

2703 """Get the metaclass of this class. 

2704 

2705 If this class does not define explicitly a metaclass, 

2706 then the first defined metaclass in ancestors will be used 

2707 instead. 

2708 

2709 :returns: The metaclass of this class. 

2710 """ 

2711 return self._find_metaclass(context=context) 

2712 

2713 def has_metaclass_hack(self) -> bool: 

2714 return self._metaclass_hack 

2715 

2716 def _islots(self): 

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

2718 if "__slots__" not in self.locals: 

2719 return None 

2720 try: 

2721 slots_attributes = list(self.igetattr("__slots__")) 

2722 except InferenceError: 

2723 # ``__slots__`` is present in ``locals`` but cannot be inferred, 

2724 # e.g. an annotation-only ``__slots__: ...`` with no assigned value. 

2725 return None 

2726 for slots in slots_attributes: 

2727 # check if __slots__ is a valid type 

2728 for meth in ITER_METHODS: 

2729 try: 

2730 slots.getattr(meth) 

2731 break 

2732 except AttributeInferenceError: 

2733 continue 

2734 else: 

2735 continue 

2736 

2737 if isinstance(slots, node_classes.Const): 

2738 # a string. Ignore the following checks, 

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

2740 if slots.value: 

2741 yield slots 

2742 continue 

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

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

2745 continue 

2746 

2747 if isinstance(slots, node_classes.Dict): 

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

2749 else: 

2750 values = slots.itered() 

2751 if isinstance(values, util.UninferableBase): 

2752 continue 

2753 if not values: 

2754 # Stop the iteration, because the class 

2755 # has an empty list of slots. 

2756 return values 

2757 

2758 for elt in values: 

2759 try: 

2760 for inferred in elt.infer(): 

2761 if not ( 

2762 isinstance(inferred, node_classes.Const) 

2763 and isinstance(inferred.value, str) 

2764 ): 

2765 continue 

2766 if not inferred.value: 

2767 continue 

2768 yield inferred 

2769 except InferenceError: 

2770 continue 

2771 

2772 return None 

2773 

2774 def _slots(self): 

2775 

2776 slots = self._islots() 

2777 try: 

2778 first = next(slots) 

2779 except StopIteration as exc: 

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

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

2782 return exc.args[0] 

2783 return None 

2784 return [first, *slots] 

2785 

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

2787 @cached_property 

2788 def _all_slots(self): 

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

2790 

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

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

2793 variable, then this function will return a None. 

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

2795 :rtype: list(str) or None 

2796 """ 

2797 

2798 def grouped_slots( 

2799 mro: list[ClassDef], 

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

2801 for cls in mro: 

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

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

2804 continue 

2805 try: 

2806 cls_slots = cls._slots() 

2807 except NotImplementedError: 

2808 continue 

2809 if cls_slots is not None: 

2810 yield from cls_slots 

2811 else: 

2812 yield None 

2813 

2814 try: 

2815 mro = self.mro() 

2816 except MroError as e: 

2817 raise NotImplementedError( 

2818 "Cannot get slots while parsing mro fails." 

2819 ) from e 

2820 

2821 slots = list(grouped_slots(mro)) 

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

2823 return None 

2824 

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

2826 

2827 def slots(self): 

2828 return self._all_slots 

2829 

2830 def _inferred_bases( 

2831 self, 

2832 context: InferenceContext | None = None, 

2833 *, 

2834 base_classes: frozenset[ClassDef] = frozenset(), 

2835 ): 

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

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

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

2839 # example: 

2840 # 

2841 # class SomeSuperClass(object): pass 

2842 # class SomeClass(SomeSuperClass): pass 

2843 # class Test(SomeClass): pass 

2844 # 

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

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

2847 # only in SomeClass. 

2848 

2849 if context is None: 

2850 context = InferenceContext() 

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

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

2853 return 

2854 

2855 for stmt in self.bases: 

2856 try: 

2857 baseobj = _infer_last(stmt, context) 

2858 except InferenceError: 

2859 continue 

2860 if isinstance(baseobj, bases.Instance): 

2861 baseobj = baseobj._proxied 

2862 if not isinstance(baseobj, ClassDef): 

2863 continue 

2864 if baseobj is self or baseobj in base_classes: 

2865 # Circular base due to name rebinding (e.g. pdb.Pdb = CustomPdb 

2866 # where CustomPdb inherits from pdb.Pdb). Fall back to the 

2867 # first non-circular inferred value from the base expression. 

2868 baseobj = self._resolve_circular_base(stmt, context) 

2869 if baseobj is None: 

2870 continue 

2871 if not baseobj.hide: 

2872 yield baseobj 

2873 else: 

2874 yield from baseobj.bases 

2875 

2876 def _resolve_circular_base( 

2877 self, 

2878 stmt: nodes.NodeNG, 

2879 context: InferenceContext | None, 

2880 ) -> ClassDef | None: 

2881 """Resolve a circular base reference by finding the original class. 

2882 

2883 When a name is rebound to a subclass (e.g. ``pdb.Pdb = CustomPdb``), 

2884 ``_infer_last`` follows the rebinding and returns the subclass itself. 

2885 This method iterates through all inferred values to find the first 

2886 non-circular ClassDef. 

2887 """ 

2888 inf_context = copy_context(context) 

2889 try: 

2890 for inferred in stmt.infer(context=inf_context): 

2891 if isinstance(inferred, bases.Instance): 

2892 inferred = inferred._proxied 

2893 if isinstance(inferred, ClassDef) and inferred is not self: 

2894 return inferred 

2895 except InferenceError: 

2896 pass 

2897 return None 

2898 

2899 def _compute_mro( 

2900 self, 

2901 context: InferenceContext, 

2902 *, 

2903 base_chain: frozenset[ClassDef] = frozenset(), 

2904 ): 

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

2906 return [self] 

2907 

2908 inferred_bases = list( 

2909 self._inferred_bases(context=context, base_classes=base_chain) 

2910 ) 

2911 bases_mro = [] 

2912 base_chain |= {self} 

2913 for base in inferred_bases: 

2914 if base in base_chain: 

2915 continue 

2916 

2917 mro = base._compute_mro(context=context, base_chain=base_chain) 

2918 bases_mro.append(mro) 

2919 

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

2921 unmerged_mro = clean_duplicates_mro(unmerged_mro, self, context) 

2922 clean_typing_generic_mro(unmerged_mro) 

2923 return _c3_merge(unmerged_mro, self, context) 

2924 

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

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

2927 

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

2929 :rtype: list(NodeNG) 

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

2931 :raises InconsistentMroError: A class' MRO is inconsistent 

2932 """ 

2933 return self._compute_mro(context=context) 

2934 

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

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

2937 

2938 :returns: The boolean value of this node. 

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

2940 """ 

2941 return True 

2942 

2943 def get_children(self): 

2944 if self.decorators is not None: 

2945 yield self.decorators 

2946 

2947 yield from self.bases 

2948 if self.keywords is not None: 

2949 yield from self.keywords 

2950 yield from self.type_params 

2951 

2952 yield from self.body 

2953 

2954 @cached_property 

2955 def _assign_nodes_in_scope(self): 

2956 children_assign_nodes = ( 

2957 child_node._assign_nodes_in_scope for child_node in self.body 

2958 ) 

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

2960 

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

2962 """The node's frame node. 

2963 

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

2965 :class:`ClassDef` or :class:`Lambda`. 

2966 

2967 :returns: The node itself. 

2968 """ 

2969 return self 

2970 

2971 def _infer( 

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

2973 ) -> Generator[ClassDef]: 

2974 yield self