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, 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 0x...> 

193 >>> node.parent 

194 <Module l.0 at 0x...> 

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 special_attributes = ModuleModel() 

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

212 

213 # names of module attributes available through the global scope 

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

215 "__name__", 

216 "__doc__", 

217 "__file__", 

218 "__path__", 

219 "__package__", 

220 } 

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

222 

223 _other_fields = ( 

224 "name", 

225 "file", 

226 "path", 

227 "package", 

228 "pure_python", 

229 "future_imports", 

230 ) 

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

232 

233 def __init__( 

234 self, 

235 name: str, 

236 file: str | None = None, 

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

238 package: bool = False, 

239 pure_python: bool = True, 

240 ) -> None: 

241 self.name = name 

242 """The name of the module.""" 

243 

244 self.file = file 

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

246 

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

248 built-in module. 

249 """ 

250 

251 self.path = path 

252 

253 self.package = package 

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

255 

256 self.pure_python = pure_python 

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

258 

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

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

261 

262 self.locals = self.globals = {} 

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

264 

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

266 """The contents of the module.""" 

267 

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

269 """The imports from ``__future__``.""" 

270 

271 super().__init__( 

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

273 ) 

274 

275 # pylint: enable=redefined-builtin 

276 

277 def postinit( 

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

279 ): 

280 self.body = body 

281 self.doc_node = doc_node 

282 

283 def _get_stream(self): 

284 if self.file_bytes is not None: 

285 return io.BytesIO(self.file_bytes) 

286 if self.file is not None: 

287 # pylint: disable=consider-using-with 

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

289 return stream 

290 return None 

291 

292 def stream(self): 

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

294 

295 :type: io.IOBase or io.BytesIO or None 

296 """ 

297 return self._get_stream() 

298 

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

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

301 

302 :param lineno: Unused. 

303 

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

305 """ 

306 return self.fromlineno, self.tolineno 

307 

308 def scope_lookup( 

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

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

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

312 

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

314 Any assignments after the given node are ignored. 

315 

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

317 

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

319 

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

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

322 globals or builtin). 

323 """ 

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

325 try: 

326 return self, self.getattr(name) 

327 except AttributeInferenceError: 

328 return self, [] 

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

330 

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

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

333 

334 :returns: The name of the type. 

335 """ 

336 return "builtins.module" 

337 

338 def display_type(self) -> str: 

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

340 

341 :returns: The type of this node. 

342 :rtype: str 

343 """ 

344 return "Module" 

345 

346 def getattr( 

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

348 ): 

349 if not name: 

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

351 

352 result = [] 

353 name_in_locals = name in self.locals 

354 

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

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

357 if name == "__name__": 

358 main_const = node_classes.const_factory("__main__") 

359 main_const.parent = AstroidManager().builtins_module 

360 result.append(main_const) 

361 elif not ignore_locals and name_in_locals: 

362 result = self.locals[name] 

363 elif self.package: 

364 try: 

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

366 except (AstroidBuildingError, SyntaxError) as exc: 

367 raise AttributeInferenceError( 

368 target=self, attribute=name, context=context 

369 ) from exc 

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

371 if result: 

372 return result 

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

374 

375 def igetattr( 

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

377 ) -> Iterator[InferenceResult]: 

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

379 

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

381 

382 :returns: The inferred possible values. 

383 """ 

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

385 # instance 

386 context = copy_context(context) 

387 context.lookupname = name 

388 try: 

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

390 except AttributeInferenceError as error: 

391 raise InferenceError( 

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

393 ) from error 

394 

395 def fully_defined(self) -> bool: 

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

397 

398 If so, the module contains a complete representation, 

399 including the code. 

400 

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

402 """ 

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

404 

405 def statement(self) -> NoReturn: 

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

407 

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

409 """ 

410 raise StatementMissing(target=self) 

411 

412 def previous_sibling(self): 

413 """The previous sibling statement. 

414 

415 :returns: The previous sibling statement node. 

416 :rtype: NodeNG or None 

417 """ 

418 

419 def next_sibling(self): 

420 """The next sibling statement node. 

421 

422 :returns: The next sibling statement node. 

423 :rtype: NodeNG or None 

424 """ 

425 

426 _absolute_import_activated = True 

427 

428 def absolute_import_activated(self) -> bool: 

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

430 

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

432 """ 

433 return self._absolute_import_activated 

434 

435 def import_module( 

436 self, 

437 modname: str, 

438 relative_only: bool = False, 

439 level: int | None = None, 

440 use_cache: bool = True, 

441 ) -> Module: 

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

443 

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

445 

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

447 

448 :param level: The level of relative import. 

449 

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

451 

452 :returns: The imported module ast. 

453 """ 

454 if relative_only and level is None: 

455 level = 0 

456 absmodname = self.relative_to_absolute_name(modname, level) 

457 

458 try: 

459 return AstroidManager().ast_from_module_name( 

460 absmodname, use_cache=use_cache 

461 ) 

462 except AstroidBuildingError: 

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

464 # skip here 

465 if relative_only: 

466 raise 

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

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

469 if modname == absmodname: 

470 raise 

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

472 

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

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

475 

476 The relative import can be implicit or explicit. 

477 

478 :param modname: The module name to convert. 

479 

480 :param level: The level of relative import. 

481 

482 :returns: The absolute module name. 

483 

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

485 module too far above this one. 

486 """ 

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

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

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

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

491 return modname 

492 if level: 

493 if self.package: 

494 level = level - 1 

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

496 elif ( 

497 self.path 

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

499 and os.path.exists( 

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

501 ) 

502 ): 

503 level = level - 1 

504 package_name = "" 

505 else: 

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

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

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

509 

510 elif self.package: 

511 package_name = self.name 

512 else: 

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

514 

515 if package_name: 

516 if not modname: 

517 return package_name 

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

519 return modname 

520 

521 def wildcard_import_names(self): 

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

523 

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

525 current CPython implementation of wildcard imports. 

526 

527 :returns: The list of imported names. 

528 :rtype: list(str) 

529 """ 

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

531 # to avoid catching too many Exceptions 

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

533 try: 

534 all_values = self["__all__"] 

535 except KeyError: 

536 return default 

537 

538 try: 

539 explicit = next(all_values.assigned_stmts()) 

540 except (InferenceError, StopIteration): 

541 return default 

542 except AttributeError: 

543 # not an assignment node 

544 # XXX infer? 

545 return default 

546 

547 # Try our best to detect the exported name. 

548 inferred = [] 

549 try: 

550 explicit = next(explicit.infer()) 

551 except (InferenceError, StopIteration): 

552 return default 

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

554 return default 

555 

556 def str_const(node) -> bool: 

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

558 

559 for node in explicit.elts: 

560 if str_const(node): 

561 inferred.append(node.value) 

562 else: 

563 try: 

564 inferred_node = next(node.infer()) 

565 except (InferenceError, StopIteration): 

566 continue 

567 if str_const(inferred_node): 

568 inferred.append(inferred_node.value) 

569 return inferred 

570 

571 def public_names(self): 

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

573 

574 :returns: The list of public names. 

575 :rtype: list(str) 

576 """ 

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

578 

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

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

581 

582 :returns: The boolean value of this node. 

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

584 """ 

585 return True 

586 

587 def get_children(self): 

588 yield from self.body 

589 

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

591 """The node's frame node. 

592 

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

594 :class:`ClassDef` or :class:`Lambda`. 

595 

596 :returns: The node itself. 

597 """ 

598 return self 

599 

600 def _infer(self, context: InferenceContext | None = None) -> Generator[Module]: 

601 yield self 

602 

603 

604class __SyntheticRoot(Module): 

605 def __init__(self): 

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

607 

608 

609SYNTHETIC_ROOT = __SyntheticRoot() 

610 

611 

612class GeneratorExp(ComprehensionScope): 

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

614 

615 >>> import astroid 

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

617 >>> node 

618 <GeneratorExp l.1 at 0x...> 

619 """ 

620 

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

622 _other_other_fields = ("locals",) 

623 elt: NodeNG 

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

625 

626 def __init__( 

627 self, 

628 lineno: int, 

629 col_offset: int, 

630 parent: NodeNG, 

631 *, 

632 end_lineno: int | None, 

633 end_col_offset: int | None, 

634 ) -> None: 

635 self.locals = {} 

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

637 

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

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

640 

641 super().__init__( 

642 lineno=lineno, 

643 col_offset=col_offset, 

644 end_lineno=end_lineno, 

645 end_col_offset=end_col_offset, 

646 parent=parent, 

647 ) 

648 

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

650 self.elt = elt 

651 self.generators = generators 

652 

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

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

655 

656 :returns: The boolean value of this node. 

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

658 """ 

659 return True 

660 

661 def get_children(self): 

662 yield self.elt 

663 

664 yield from self.generators 

665 

666 

667class DictComp(ComprehensionScope): 

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

669 

670 >>> import astroid 

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

672 >>> node 

673 <DictComp l.1 at 0x...> 

674 """ 

675 

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

677 _other_other_fields = ("locals",) 

678 key: NodeNG 

679 """What produces the keys.""" 

680 

681 value: NodeNG 

682 """What produces the values.""" 

683 

684 def __init__( 

685 self, 

686 lineno: int, 

687 col_offset: int, 

688 parent: NodeNG, 

689 *, 

690 end_lineno: int | None, 

691 end_col_offset: int | None, 

692 ) -> None: 

693 self.locals = {} 

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

695 

696 super().__init__( 

697 lineno=lineno, 

698 col_offset=col_offset, 

699 end_lineno=end_lineno, 

700 end_col_offset=end_col_offset, 

701 parent=parent, 

702 ) 

703 

704 def postinit( 

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

706 ) -> None: 

707 self.key = key 

708 self.value = value 

709 self.generators = generators 

710 

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

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

713 

714 :returns: The boolean value of this node. 

715 For a :class:`DictComp` this is always :obj:`~astroid.Uninferable`. 

716 :rtype: Uninferable 

717 """ 

718 return util.Uninferable 

719 

720 def get_children(self): 

721 yield self.key 

722 yield self.value 

723 

724 yield from self.generators 

725 

726 

727class SetComp(ComprehensionScope): 

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

729 

730 >>> import astroid 

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

732 >>> node 

733 <SetComp l.1 at 0x...> 

734 """ 

735 

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

737 _other_other_fields = ("locals",) 

738 elt: NodeNG 

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

740 

741 def __init__( 

742 self, 

743 lineno: int, 

744 col_offset: int, 

745 parent: NodeNG, 

746 *, 

747 end_lineno: int | None, 

748 end_col_offset: int | None, 

749 ) -> None: 

750 self.locals = {} 

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

752 

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

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

755 

756 super().__init__( 

757 lineno=lineno, 

758 col_offset=col_offset, 

759 end_lineno=end_lineno, 

760 end_col_offset=end_col_offset, 

761 parent=parent, 

762 ) 

763 

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

765 self.elt = elt 

766 self.generators = generators 

767 

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

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

770 

771 :returns: The boolean value of this node. 

772 For a :class:`SetComp` this is always :obj:`~astroid.Uninferable`. 

773 :rtype: Uninferable 

774 """ 

775 return util.Uninferable 

776 

777 def get_children(self): 

778 yield self.elt 

779 

780 yield from self.generators 

781 

782 

783class ListComp(ComprehensionScope): 

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

785 

786 >>> import astroid 

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

788 >>> node 

789 <ListComp l.1 at 0x...> 

790 """ 

791 

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

793 _other_other_fields = ("locals",) 

794 

795 elt: NodeNG 

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

797 

798 def __init__( 

799 self, 

800 lineno: int, 

801 col_offset: int, 

802 parent: NodeNG, 

803 *, 

804 end_lineno: int | None, 

805 end_col_offset: int | None, 

806 ) -> None: 

807 self.locals = {} 

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

809 

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

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

812 

813 super().__init__( 

814 lineno=lineno, 

815 col_offset=col_offset, 

816 end_lineno=end_lineno, 

817 end_col_offset=end_col_offset, 

818 parent=parent, 

819 ) 

820 

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

822 self.elt = elt 

823 self.generators = generators 

824 

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

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

827 

828 :returns: The boolean value of this node. 

829 For a :class:`ListComp` this is always :obj:`~astroid.Uninferable`. 

830 :rtype: Uninferable 

831 """ 

832 return util.Uninferable 

833 

834 def get_children(self): 

835 yield self.elt 

836 

837 yield from self.generators 

838 

839 

840def _infer_decorator_callchain(node): 

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

842 static or a classmethod. 

843 """ 

844 if not isinstance(node, FunctionDef): 

845 return None 

846 if not node.parent: 

847 return None 

848 try: 

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

850 except InferenceError: 

851 return None 

852 if isinstance(result, bases.Instance): 

853 result = result._proxied 

854 if isinstance(result, ClassDef): 

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

856 return "classmethod" 

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

858 return "staticmethod" 

859 if isinstance(result, FunctionDef): 

860 if not result.decorators: 

861 return None 

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

863 for decorator in result.decorators.nodes: 

864 if isinstance(decorator, node_classes.Name): 

865 if decorator.name in BUILTIN_DESCRIPTORS: 

866 return decorator.name 

867 if ( 

868 isinstance(decorator, node_classes.Attribute) 

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

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

871 and decorator.attrname in BUILTIN_DESCRIPTORS 

872 ): 

873 return decorator.attrname 

874 return None 

875 

876 

877class Lambda(_base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG): 

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

879 

880 >>> import astroid 

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

882 >>> node 

883 <Lambda.<lambda> l.1 at 0x...> 

884 """ 

885 

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

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

888 name = "<lambda>" 

889 is_lambda = True 

890 special_attributes = FunctionModel() 

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

892 

893 args: Arguments 

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

895 

896 body: NodeNG 

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

898 

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

900 return 0 

901 

902 @property 

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

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

905 

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

907 """ 

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

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

910 return "method" 

911 return "function" 

912 

913 def __init__( 

914 self, 

915 lineno: int, 

916 col_offset: int, 

917 parent: NodeNG, 

918 *, 

919 end_lineno: int | None, 

920 end_col_offset: int | None, 

921 ): 

922 self.locals = {} 

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

924 

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

926 

927 super().__init__( 

928 lineno=lineno, 

929 col_offset=col_offset, 

930 end_lineno=end_lineno, 

931 end_col_offset=end_col_offset, 

932 parent=parent, 

933 ) 

934 

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

936 self.args = args 

937 self.body = body 

938 

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

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

941 

942 :returns: The name of the type. 

943 """ 

944 if "method" in self.type: 

945 return "builtins.instancemethod" 

946 return "builtins.function" 

947 

948 def display_type(self) -> str: 

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

950 

951 :returns: The type of this node. 

952 :rtype: str 

953 """ 

954 if "method" in self.type: 

955 return "Method" 

956 return "Function" 

957 

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

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

960 

961 :returns: Whether this defines something that is callable 

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

963 """ 

964 return True 

965 

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

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

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

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

970 

971 :returns: The names of the arguments. 

972 :rtype: list(str) 

973 """ 

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

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

976 else: 

977 names = [] 

978 

979 return names 

980 

981 def infer_call_result( 

982 self, 

983 caller: SuccessfulInferenceResult | None, 

984 context: InferenceContext | None = None, 

985 ) -> Iterator[InferenceResult]: 

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

987 return self.body.infer(context) 

988 

989 def scope_lookup( 

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

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

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

993 

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

995 Any assignments after the given node are ignored. 

996 

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

998 

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

1000 

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

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

1003 globals or builtin). 

1004 """ 

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

1006 self.args.kw_defaults and node in self.args.kw_defaults 

1007 ): 

1008 if not self.parent: 

1009 raise ParentMissingError(target=self) 

1010 frame = self.parent.frame() 

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

1012 # value to the defined function 

1013 offset = -1 

1014 else: 

1015 # check this is not used in function decorators 

1016 frame = self 

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

1018 

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

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

1021 

1022 :returns: The boolean value of this node. 

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

1024 """ 

1025 return True 

1026 

1027 def get_children(self): 

1028 yield self.args 

1029 yield self.body 

1030 

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

1032 """The node's frame node. 

1033 

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

1035 :class:`ClassDef` or :class:`Lambda`. 

1036 

1037 :returns: The node itself. 

1038 """ 

1039 return self 

1040 

1041 def getattr( 

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

1043 ) -> list[NodeNG]: 

1044 if not name: 

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

1046 

1047 found_attrs = [] 

1048 if name in self.instance_attrs: 

1049 found_attrs = self.instance_attrs[name] 

1050 if name in self.special_attributes: 

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

1052 if found_attrs: 

1053 return found_attrs 

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

1055 

1056 def _infer(self, context: InferenceContext | None = None) -> Generator[Lambda]: 

1057 yield self 

1058 

1059 def _get_yield_nodes_skip_functions(self): 

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

1061 yield from self.body._get_yield_nodes_skip_functions() 

1062 

1063 

1064class FunctionDef( 

1065 _base_nodes.MultiLineBlockNode, 

1066 _base_nodes.FilterStmtsBaseNode, 

1067 _base_nodes.Statement, 

1068 LocalsDictNodeNG, 

1069): 

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

1071 

1072 >>> import astroid 

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

1074 ... def my_func(arg): 

1075 ... return arg + 1 

1076 ... ''') 

1077 >>> node 

1078 <FunctionDef.my_func l.2 at 0x...> 

1079 """ 

1080 

1081 _astroid_fields = ( 

1082 "decorators", 

1083 "args", 

1084 "returns", 

1085 "type_params", 

1086 "doc_node", 

1087 "body", 

1088 ) 

1089 _multi_line_block_fields = ("body",) 

1090 returns = None 

1091 

1092 decorators: node_classes.Decorators | None 

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

1094 

1095 doc_node: Const | None 

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

1097 

1098 args: Arguments 

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

1100 

1101 is_function = True 

1102 """Whether this node indicates a function. 

1103 

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

1105 

1106 :type: bool 

1107 """ 

1108 type_annotation = None 

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

1110 

1111 :type: NodeNG or None 

1112 """ 

1113 type_comment_args = None 

1114 """ 

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

1116 passed by a type comment 

1117 """ 

1118 type_comment_returns = None 

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

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

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

1122 _other_other_fields = ( 

1123 "locals", 

1124 "_type", 

1125 "type_comment_returns", 

1126 "type_comment_args", 

1127 ) 

1128 _type = None 

1129 

1130 name = "<functiondef>" 

1131 

1132 special_attributes = FunctionModel() 

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

1134 

1135 def __init__( 

1136 self, 

1137 name: str, 

1138 lineno: int, 

1139 col_offset: int, 

1140 parent: NodeNG, 

1141 *, 

1142 end_lineno: int | None, 

1143 end_col_offset: int | None, 

1144 ) -> None: 

1145 self.name = name 

1146 """The name of the function.""" 

1147 

1148 self.locals = {} 

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

1150 

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

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

1153 

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

1155 [] 

1156 ) 

1157 """The type parameters introduced by :pep:`695`, new in Python 3.12. 

1158 

1159 For example, the ``T`` in ``def func[T]() -> T: ...``. 

1160 """ 

1161 

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

1163 

1164 super().__init__( 

1165 lineno=lineno, 

1166 col_offset=col_offset, 

1167 end_lineno=end_lineno, 

1168 end_col_offset=end_col_offset, 

1169 parent=parent, 

1170 ) 

1171 

1172 def postinit( 

1173 self, 

1174 args: Arguments, 

1175 body: list[NodeNG], 

1176 decorators: node_classes.Decorators | None = None, 

1177 returns=None, 

1178 type_comment_returns=None, 

1179 type_comment_args=None, 

1180 *, 

1181 position: Position | None = None, 

1182 doc_node: Const | None = None, 

1183 type_params: ( 

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

1185 ) = None, 

1186 ): 

1187 """Do some setup after initialisation. 

1188 

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

1190 

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

1192 

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

1194 method or function. 

1195 :params type_comment_returns: 

1196 The return type annotation passed via a type comment. 

1197 :params type_comment_args: 

1198 The args type annotation passed via a type comment. 

1199 :params position: 

1200 Position of function keyword(s) and name. 

1201 :param doc_node: 

1202 The doc node associated with this node. 

1203 :param type_params: 

1204 The type_params associated with this node. 

1205 """ 

1206 self.args = args 

1207 self.body = body 

1208 self.decorators = decorators 

1209 self.returns = returns 

1210 self.type_comment_returns = type_comment_returns 

1211 self.type_comment_args = type_comment_args 

1212 self.position = position 

1213 self.doc_node = doc_node 

1214 self.type_params = type_params or [] 

1215 

1216 @cached_property 

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

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

1219 

1220 Additional decorators are considered when they are used as 

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

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

1223 decoration. 

1224 """ 

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

1226 return [] 

1227 

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

1229 for assign in frame._assign_nodes_in_scope: 

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

1231 assign.value.func, node_classes.Name 

1232 ): 

1233 for assign_node in assign.targets: 

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

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

1236 continue 

1237 

1238 if assign_node.name != self.name: 

1239 # Interested only in the assignment nodes that 

1240 # decorates the current method. 

1241 continue 

1242 try: 

1243 meth = frame[self.name] 

1244 except KeyError: 

1245 continue 

1246 else: 

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

1248 # original method. 

1249 if ( 

1250 isinstance(meth, FunctionDef) 

1251 and assign_node.frame() == frame 

1252 ): 

1253 decorators.append(assign.value) 

1254 return decorators 

1255 

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

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

1258 

1259 :returns: The name of the type. 

1260 """ 

1261 if "method" in self.type: 

1262 return "builtins.instancemethod" 

1263 return "builtins.function" 

1264 

1265 def display_type(self) -> str: 

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

1267 

1268 :returns: The type of this node. 

1269 :rtype: str 

1270 """ 

1271 if "method" in self.type: 

1272 return "Method" 

1273 return "Function" 

1274 

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

1276 return True 

1277 

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

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

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

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

1282 

1283 :returns: The names of the arguments. 

1284 :rtype: list(str) 

1285 """ 

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

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

1288 else: 

1289 names = [] 

1290 

1291 return names 

1292 

1293 def getattr( 

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

1295 ) -> list[NodeNG]: 

1296 if not name: 

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

1298 

1299 found_attrs = [] 

1300 if name in self.instance_attrs: 

1301 found_attrs = self.instance_attrs[name] 

1302 if name in self.special_attributes: 

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

1304 if found_attrs: 

1305 return found_attrs 

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

1307 

1308 @cached_property 

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

1310 """The function type for this node. 

1311 

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

1313 """ 

1314 for decorator in self.extra_decorators: 

1315 if decorator.func.name in BUILTIN_DESCRIPTORS: 

1316 return decorator.func.name 

1317 

1318 if not self.parent: 

1319 raise ParentMissingError(target=self) 

1320 

1321 frame = self.parent.frame() 

1322 type_name = "function" 

1323 if isinstance(frame, ClassDef): 

1324 if self.name == "__new__": 

1325 return "classmethod" 

1326 if self.name == "__init_subclass__": 

1327 return "classmethod" 

1328 if self.name == "__class_getitem__": 

1329 return "classmethod" 

1330 

1331 type_name = "method" 

1332 

1333 if not self.decorators: 

1334 return type_name 

1335 

1336 for node in self.decorators.nodes: 

1337 if isinstance(node, node_classes.Name): 

1338 if node.name in BUILTIN_DESCRIPTORS: 

1339 return node.name 

1340 if ( 

1341 isinstance(node, node_classes.Attribute) 

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

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

1344 and node.attrname in BUILTIN_DESCRIPTORS 

1345 ): 

1346 return node.attrname 

1347 

1348 if isinstance(node, node_classes.Call): 

1349 # Handle the following case: 

1350 # @some_decorator(arg1, arg2) 

1351 # def func(...) 

1352 # 

1353 try: 

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

1355 except (InferenceError, StopIteration): 

1356 continue 

1357 _type = _infer_decorator_callchain(current) 

1358 if _type is not None: 

1359 return _type 

1360 

1361 try: 

1362 for inferred in node.infer(): 

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

1364 _type = _infer_decorator_callchain(inferred) 

1365 if _type is not None: 

1366 return _type 

1367 

1368 if not isinstance(inferred, ClassDef): 

1369 continue 

1370 for ancestor in inferred.ancestors(): 

1371 if not isinstance(ancestor, ClassDef): 

1372 continue 

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

1374 return "classmethod" 

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

1376 return "staticmethod" 

1377 except InferenceError: 

1378 pass 

1379 return type_name 

1380 

1381 @cached_property 

1382 def fromlineno(self) -> int: 

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

1384 

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

1386 """ 

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

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

1389 lineno = self.lineno or 0 

1390 if self.decorators is not None: 

1391 lineno += sum( 

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

1393 ) 

1394 

1395 return lineno or 0 

1396 

1397 @cached_property 

1398 def blockstart_tolineno(self): 

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

1400 

1401 :type: int 

1402 """ 

1403 if self.returns: 

1404 return self.returns.tolineno 

1405 return self.args.tolineno 

1406 

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

1408 return 1 if self.is_bound() else 0 

1409 

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

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

1412 

1413 :param lineno: Unused. 

1414 

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

1416 """ 

1417 return self.fromlineno, self.tolineno 

1418 

1419 def igetattr( 

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

1421 ) -> Iterator[InferenceResult]: 

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

1423 try: 

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

1425 except AttributeInferenceError as error: 

1426 raise InferenceError( 

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

1428 ) from error 

1429 

1430 def is_method(self) -> bool: 

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

1432 

1433 :returns: Whether this is a method. 

1434 """ 

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

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

1437 return ( 

1438 self.type != "function" 

1439 and self.parent is not None 

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

1441 ) 

1442 

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

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

1445 

1446 :param context: 

1447 An inference context that can be passed to inference functions 

1448 :returns: The names of the decorators. 

1449 """ 

1450 result = set() 

1451 decoratornodes = [] 

1452 if self.decorators is not None: 

1453 decoratornodes += self.decorators.nodes 

1454 decoratornodes += self.extra_decorators 

1455 for decnode in decoratornodes: 

1456 try: 

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

1458 result.add(infnode.qname()) 

1459 except InferenceError: 

1460 continue 

1461 return result 

1462 

1463 def is_bound(self) -> bool: 

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

1465 

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

1467 """ 

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

1469 

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

1471 """Check if the method is abstract. 

1472 

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

1474 * The only statement is 'raise NotImplementedError' 

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

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

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

1478 

1479 :returns: Whether the method is abstract. 

1480 """ 

1481 if self.decorators: 

1482 for node in self.decorators.nodes: 

1483 try: 

1484 inferred = next(node.infer()) 

1485 except (InferenceError, StopIteration): 

1486 continue 

1487 if inferred and inferred.qname() in { 

1488 "abc.abstractproperty", 

1489 "abc.abstractmethod", 

1490 }: 

1491 return True 

1492 

1493 for child_node in self.body: 

1494 if isinstance(child_node, node_classes.Raise): 

1495 if any_raise_is_abstract: 

1496 return True 

1497 if child_node.raises_not_implemented(): 

1498 return True 

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

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

1501 if pass_is_abstract: 

1502 return True 

1503 

1504 return False 

1505 

1506 def is_generator(self) -> bool: 

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

1508 

1509 :returns: Whether this is a generator function. 

1510 """ 

1511 yields_without_lambdas = set(self._get_yield_nodes_skip_lambdas()) 

1512 yields_without_functions = set(self._get_yield_nodes_skip_functions()) 

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

1514 return bool(yields_without_lambdas & yields_without_functions) 

1515 

1516 def _infer( 

1517 self, context: InferenceContext | None = None 

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

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

1520 

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

1522 yield self 

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

1524 

1525 if not self.parent: 

1526 raise ParentMissingError(target=self) 

1527 prop_func = objects.Property( 

1528 function=self, 

1529 name=self.name, 

1530 lineno=self.lineno, 

1531 parent=self.parent, 

1532 col_offset=self.col_offset, 

1533 ) 

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

1535 yield prop_func 

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

1537 

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

1539 """Infer what the function yields when called 

1540 

1541 :returns: What the function yields 

1542 :rtype: Iterator[NodeNG or Uninferable] or None 

1543 """ 

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

1545 if yield_.value is None: 

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

1547 elif yield_.scope() == self: 

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

1549 

1550 def infer_call_result( 

1551 self, 

1552 caller: SuccessfulInferenceResult | None, 

1553 context: InferenceContext | None = None, 

1554 ) -> Iterator[InferenceResult]: 

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

1556 if context is None: 

1557 context = InferenceContext() 

1558 if self.is_generator(): 

1559 if isinstance(self, AsyncFunctionDef): 

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

1561 else: 

1562 generator_cls = bases.Generator 

1563 result = generator_cls(self, generator_initial_context=context) 

1564 yield result 

1565 return 

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

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

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

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

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

1571 # generators, and filter it out later. 

1572 if ( 

1573 self.name == "with_metaclass" 

1574 and caller is not None 

1575 and self.args.args 

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

1577 and self.args.vararg is not None 

1578 ): 

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

1580 assert caller.args.args is not None 

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

1582 elif isinstance(caller.args, list): 

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

1584 else: 

1585 raise TypeError( # pragma: no cover 

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

1587 ) 

1588 if isinstance(metaclass, ClassDef): 

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

1590 new_class = ClassDef( 

1591 name="temporary_class", 

1592 lineno=0, 

1593 col_offset=0, 

1594 end_lineno=0, 

1595 end_col_offset=0, 

1596 parent=SYNTHETIC_ROOT, 

1597 ) 

1598 new_class.hide = True 

1599 new_class.postinit( 

1600 bases=[ 

1601 base 

1602 for base in class_bases 

1603 if not isinstance(base, util.UninferableBase) 

1604 ], 

1605 body=[], 

1606 decorators=None, 

1607 metaclass=metaclass, 

1608 ) 

1609 yield new_class 

1610 return 

1611 returns = self._get_return_nodes_skip_functions() 

1612 

1613 first_return = next(returns, None) 

1614 if not first_return: 

1615 if self.body: 

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

1617 yield util.Uninferable 

1618 else: 

1619 yield node_classes.Const(None) 

1620 return 

1621 

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

1623 if ( 

1624 len(self.body) == 0 

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

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

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

1628 ): 

1629 yield util.Uninferable 

1630 return 

1631 

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

1633 

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

1635 if returnnode.value is None: 

1636 yield node_classes.Const(None) 

1637 else: 

1638 try: 

1639 yield from returnnode.value.infer(context) 

1640 except InferenceError: 

1641 yield util.Uninferable 

1642 

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

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

1645 

1646 :returns: The boolean value of this node. 

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

1648 """ 

1649 return True 

1650 

1651 def get_children(self): 

1652 if self.decorators is not None: 

1653 yield self.decorators 

1654 

1655 yield self.args 

1656 

1657 if self.returns is not None: 

1658 yield self.returns 

1659 yield from self.type_params 

1660 

1661 yield from self.body 

1662 

1663 def scope_lookup( 

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

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

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

1667 if name == "__class__": 

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

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

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

1671 # when `__class__` is being used. 

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

1673 return self, [frame] 

1674 

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

1676 self.args.kw_defaults and node in self.args.kw_defaults 

1677 ): 

1678 if not self.parent: 

1679 raise ParentMissingError(target=self) 

1680 frame = self.parent.frame() 

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

1682 # value to the defined function 

1683 offset = -1 

1684 else: 

1685 # check this is not used in function decorators 

1686 frame = self 

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

1688 

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

1690 """The node's frame node. 

1691 

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

1693 :class:`ClassDef` or :class:`Lambda`. 

1694 

1695 :returns: The node itself. 

1696 """ 

1697 return self 

1698 

1699 

1700class AsyncFunctionDef(FunctionDef): 

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

1702 

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

1704 created with the `async` keyword. 

1705 

1706 >>> import astroid 

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

1708 ... async def func(things): 

1709 ... async for thing in things: 

1710 ... print(thing) 

1711 ... ''') 

1712 >>> node 

1713 <AsyncFunctionDef.func l.2 at 0x...> 

1714 >>> node.body[0] 

1715 <AsyncFor l.3 at 0x...> 

1716 """ 

1717 

1718 

1719def _is_metaclass( 

1720 klass: ClassDef, 

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

1722 context: InferenceContext | None = None, 

1723) -> bool: 

1724 """Return if the given class can be 

1725 used as a metaclass. 

1726 """ 

1727 if klass.name == "type": 

1728 return True 

1729 if seen is None: 

1730 seen = set() 

1731 for base in klass.bases: 

1732 try: 

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

1734 baseobj_name = baseobj.qname() 

1735 if baseobj_name in seen: 

1736 continue 

1737 

1738 seen.add(baseobj_name) 

1739 if isinstance(baseobj, bases.Instance): 

1740 # not abstract 

1741 return False 

1742 if baseobj is klass: 

1743 continue 

1744 if not isinstance(baseobj, ClassDef): 

1745 continue 

1746 if baseobj._type == "metaclass": 

1747 return True 

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

1749 return True 

1750 except InferenceError: 

1751 continue 

1752 return False 

1753 

1754 

1755def _class_type( 

1756 klass: ClassDef, 

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

1758 context: InferenceContext | None = None, 

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

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

1761 from 'regular' classes 

1762 """ 

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

1764 if klass._type is not None: 

1765 return klass._type 

1766 if _is_metaclass(klass, context=context): 

1767 klass._type = "metaclass" 

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

1769 klass._type = "exception" 

1770 else: 

1771 if ancestors is None: 

1772 ancestors = set() 

1773 klass_name = klass.qname() 

1774 if klass_name in ancestors: 

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

1776 klass._type = "class" 

1777 return "class" 

1778 ancestors.add(klass_name) 

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

1780 name = _class_type(base, ancestors) 

1781 if name != "class": 

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

1783 # don't propagate it if the current class 

1784 # can't be a metaclass 

1785 continue 

1786 klass._type = base.type 

1787 break 

1788 if klass._type is None: 

1789 klass._type = "class" 

1790 return klass._type 

1791 

1792 

1793def get_wrapping_class(node): 

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

1795 

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

1797 is a parent for the said node. 

1798 

1799 :returns: The class that wraps the given node 

1800 :rtype: ClassDef or None 

1801 """ 

1802 

1803 klass = node.frame() 

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

1805 if klass.parent is None: 

1806 klass = None 

1807 else: 

1808 klass = klass.parent.frame() 

1809 return klass 

1810 

1811 

1812class ClassDef( 

1813 _base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG, _base_nodes.Statement 

1814): 

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

1816 

1817 >>> import astroid 

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

1819 ... class Thing: 

1820 ... def my_meth(self, arg): 

1821 ... return arg + self.offset 

1822 ... ''') 

1823 >>> node 

1824 <ClassDef.Thing l.2 at 0x...> 

1825 """ 

1826 

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

1828 # by a raw factories 

1829 

1830 # a dictionary of class instances attributes 

1831 _astroid_fields = ( 

1832 "decorators", 

1833 "bases", 

1834 "keywords", 

1835 "doc_node", 

1836 "body", 

1837 "type_params", 

1838 ) # name 

1839 

1840 decorators = None 

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

1842 

1843 :type: Decorators or None 

1844 """ 

1845 special_attributes = ClassModel() 

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

1847 

1848 :type: objectmodel.ClassModel 

1849 """ 

1850 

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

1852 _metaclass: NodeNG | None = None 

1853 _metaclass_hack = False 

1854 hide = False 

1855 type = property( 

1856 _class_type, 

1857 doc=( 

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

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

1860 ":type: str" 

1861 ), 

1862 ) 

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

1864 _other_other_fields = "locals" 

1865 

1866 def __init__( 

1867 self, 

1868 name: str, 

1869 lineno: int, 

1870 col_offset: int, 

1871 parent: NodeNG, 

1872 *, 

1873 end_lineno: int | None, 

1874 end_col_offset: int | None, 

1875 ) -> None: 

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

1877 self.locals = {} 

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

1879 

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

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

1882 

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

1884 """ 

1885 

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

1887 """What the class inherits from.""" 

1888 

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

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

1891 

1892 self.name = name 

1893 """The name of the class.""" 

1894 

1895 self.decorators = None 

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

1897 

1898 self.doc_node: Const | None = None 

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

1900 

1901 self.is_dataclass: bool = False 

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

1903 

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

1905 [] 

1906 ) 

1907 """The type parameters introduced by :pep:`695`, new in Python 3.12. 

1908 

1909 For example, the ``T`` in ``class MyClass[T]: ...``. 

1910 """ 

1911 

1912 super().__init__( 

1913 lineno=lineno, 

1914 col_offset=col_offset, 

1915 end_lineno=end_lineno, 

1916 end_col_offset=end_col_offset, 

1917 parent=parent, 

1918 ) 

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

1920 self.add_local_node(node, local_name) 

1921 

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

1923 protocols.instance_class_infer_binary_op 

1924 ) 

1925 

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

1927 return 1 

1928 

1929 def implicit_locals(self): 

1930 """Get implicitly defined class definition locals. 

1931 

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

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

1934 """ 

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

1936 # __qualname__ is defined in PEP3155 

1937 locals_ += ( 

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

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

1940 ) 

1941 return locals_ 

1942 

1943 # pylint: disable=redefined-outer-name 

1944 def postinit( 

1945 self, 

1946 bases: list[SuccessfulInferenceResult], 

1947 body: list[NodeNG], 

1948 decorators: node_classes.Decorators | None, 

1949 newstyle: bool | None = None, 

1950 metaclass: NodeNG | None = None, 

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

1952 *, 

1953 position: Position | None = None, 

1954 doc_node: Const | None = None, 

1955 type_params: ( 

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

1957 ) = None, 

1958 ) -> None: 

1959 if keywords is not None: 

1960 self.keywords = keywords 

1961 self.bases = bases 

1962 self.body = body 

1963 self.decorators = decorators 

1964 self._metaclass = metaclass 

1965 self.position = position 

1966 self.doc_node = doc_node 

1967 self.type_params = type_params or [] 

1968 

1969 @cached_property 

1970 def blockstart_tolineno(self): 

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

1972 

1973 :type: int 

1974 """ 

1975 if self.bases: 

1976 return self.bases[-1].tolineno 

1977 

1978 return self.fromlineno 

1979 

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

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

1982 

1983 :param lineno: Unused. 

1984 

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

1986 """ 

1987 return self.fromlineno, self.tolineno 

1988 

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

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

1991 

1992 :returns: The name of the type. 

1993 """ 

1994 return "builtins.type" 

1995 

1996 def display_type(self) -> str: 

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

1998 

1999 :returns: The type of this node. 

2000 :rtype: str 

2001 """ 

2002 return "Class" 

2003 

2004 def callable(self) -> bool: 

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

2006 

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

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

2009 """ 

2010 return True 

2011 

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

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

2014 

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

2016 :type type_name: str 

2017 

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

2019 """ 

2020 if self.qname() == type_name: 

2021 return True 

2022 

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

2024 

2025 def _infer_type_call(self, caller, context): 

2026 try: 

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

2028 except StopIteration as e: 

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

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

2031 name_node.value, str 

2032 ): 

2033 name = name_node.value 

2034 else: 

2035 return util.Uninferable 

2036 

2037 result = ClassDef( 

2038 name, 

2039 lineno=0, 

2040 col_offset=0, 

2041 end_lineno=0, 

2042 end_col_offset=0, 

2043 parent=caller.parent, 

2044 ) 

2045 

2046 # Get the bases of the class. 

2047 try: 

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

2049 except StopIteration as e: 

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

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

2052 bases = [] 

2053 for base in class_bases.itered(): 

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

2055 if inferred: 

2056 bases.append( 

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

2058 ) 

2059 result.bases = bases 

2060 else: 

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

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

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

2064 return util.Uninferable 

2065 

2066 # Get the members of the class 

2067 try: 

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

2069 except (InferenceError, StopIteration): 

2070 members = None 

2071 

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

2073 for attr, value in members.items: 

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

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

2076 

2077 return result 

2078 

2079 def infer_call_result( 

2080 self, 

2081 caller: SuccessfulInferenceResult | None, 

2082 context: InferenceContext | None = None, 

2083 ) -> Iterator[InferenceResult]: 

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

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

2086 result = self._infer_type_call(caller, context) 

2087 yield result 

2088 return 

2089 

2090 dunder_call = None 

2091 try: 

2092 metaclass = self.metaclass(context=context) 

2093 if metaclass is not None: 

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

2095 # Otherwise we will find ObjectModel.__call__ which will 

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

2097 # handled later. 

2098 if "__call__" in metaclass.locals: 

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

2100 except (AttributeInferenceError, StopIteration): 

2101 pass 

2102 

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

2104 # Call type.__call__ if not set metaclass 

2105 # (since type is the default metaclass) 

2106 context = bind_context_to_node(context, self) 

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

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

2109 # when there is a call context to annotate. 

2110 if context.callcontext: 

2111 context.callcontext.callee = dunder_call 

2112 yield from dunder_call.infer_call_result(caller, context) 

2113 else: 

2114 yield self.instantiate_class() 

2115 

2116 def scope_lookup( 

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

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

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

2120 

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

2122 Any assignments after the given node are ignored. 

2123 

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

2125 

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

2127 

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

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

2130 globals or builtin). 

2131 """ 

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

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

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

2135 # inside this class. 

2136 lookup_upper_frame = ( 

2137 isinstance(node.parent, node_classes.Decorators) 

2138 and name in AstroidManager().builtins_module 

2139 ) 

2140 if ( 

2141 any( 

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

2143 for base in self.bases 

2144 ) 

2145 or lookup_upper_frame 

2146 ): 

2147 # Handle the case where we have either a name 

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

2149 # the actual definition or the case where we have 

2150 # a Getattr node, with that name. 

2151 # 

2152 # name = ... 

2153 # class A(name): 

2154 # def name(self): ... 

2155 # 

2156 # import name 

2157 # class A(name.Name): 

2158 # def name(self): ... 

2159 if not self.parent: 

2160 raise ParentMissingError(target=self) 

2161 frame = self.parent.frame() 

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

2163 # the defined class 

2164 offset = -1 

2165 else: 

2166 frame = self 

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

2168 

2169 @property 

2170 def basenames(self): 

2171 """The names of the parent classes 

2172 

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

2174 

2175 :type: list(str) 

2176 """ 

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

2178 

2179 def ancestors( 

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

2181 ) -> Generator[ClassDef]: 

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

2183 

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

2185 

2186 :returns: The base classes 

2187 """ 

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

2189 # FIXME: inference make infinite loops possible here 

2190 yielded = {self} 

2191 if context is None: 

2192 context = InferenceContext() 

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

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

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

2196 return 

2197 

2198 for stmt in self.bases: 

2199 with context.restore_path(): 

2200 try: 

2201 for baseobj in stmt.infer(context): 

2202 if not isinstance(baseobj, ClassDef): 

2203 if isinstance(baseobj, bases.Instance): 

2204 baseobj = baseobj._proxied 

2205 else: 

2206 continue 

2207 if not baseobj.hide: 

2208 if baseobj in yielded: 

2209 continue 

2210 yielded.add(baseobj) 

2211 yield baseobj 

2212 if not recurs: 

2213 continue 

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

2215 if grandpa is self: 

2216 # This class is the ancestor of itself. 

2217 break 

2218 if grandpa in yielded: 

2219 continue 

2220 yielded.add(grandpa) 

2221 yield grandpa 

2222 except InferenceError: 

2223 continue 

2224 

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

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

2227 

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

2229 :type name: str 

2230 

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

2232 :rtype: Iterator[NodeNG] 

2233 """ 

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

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

2236 try: 

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

2238 except MroError: 

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

2240 # a sane MRO. 

2241 ancestors = self.ancestors(context=context) 

2242 for astroid in ancestors: 

2243 if name in astroid: 

2244 yield astroid 

2245 

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

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

2248 

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

2250 :type name: str 

2251 

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

2253 an instance attribute. 

2254 :rtype: Iterator[NodeNG] 

2255 """ 

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

2257 if name in astroid.instance_attrs: 

2258 yield astroid 

2259 

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

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

2262 

2263 :param node: The node to check for. 

2264 :type node: NodeNG 

2265 

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

2267 """ 

2268 return node in self.bases 

2269 

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

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

2272 

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

2274 

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

2276 :rtype: list(NodeNG) 

2277 

2278 :raises AttributeInferenceError: If no attribute with this name 

2279 can be found in this class or parent classes. 

2280 """ 

2281 result = [] 

2282 if name in self.locals: 

2283 result = self.locals[name] 

2284 else: 

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

2286 if class_node: 

2287 result = class_node.locals[name] 

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

2289 if result: 

2290 return result 

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

2292 

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

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

2295 

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

2297 

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

2299 :rtype: list(NodeNG) 

2300 

2301 :raises AttributeInferenceError: If no attribute with this name 

2302 can be found in this class or parent classes. 

2303 """ 

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

2305 # which could lead to infinite loop. 

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

2307 # get all values from parents 

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

2309 values += class_node.instance_attrs[name] 

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

2311 if values: 

2312 return values 

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

2314 

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

2316 """Get an :class:`~astroid.Instance` of the :class:`ClassDef` node. 

2317 

2318 :returns: An :class:`~astroid.Instance` of the :class:`ClassDef` node 

2319 """ 

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

2321 

2322 try: 

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

2324 # Subclasses of exceptions can be exception instances 

2325 return objects.ExceptionInstance(self) 

2326 except MroError: 

2327 pass 

2328 return bases.Instance(self) 

2329 

2330 def getattr( 

2331 self, 

2332 name: str, 

2333 context: InferenceContext | None = None, 

2334 class_context: bool = True, 

2335 ) -> list[InferenceResult]: 

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

2337 

2338 This method doesn't look in the ``instance_attrs`` dictionary 

2339 since it is done by an :class:`~astroid.Instance` proxy at inference time. 

2340 It may return an :obj:`~astroid.Uninferable` object if 

2341 the attribute has not been 

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

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

2344 attribute is accessed from a class context, 

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

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

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

2348 metaclass will be done. 

2349 

2350 :param name: The attribute to look for. 

2351 

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

2353 

2354 :returns: The attribute. 

2355 

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

2357 """ 

2358 if not name: 

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

2360 

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

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

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

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

2365 

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

2367 special_attr = self.special_attributes.lookup(name) 

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

2369 result = [special_attr] 

2370 return result 

2371 

2372 if class_context: 

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

2374 

2375 result: list[InferenceResult] = [] 

2376 for value in values: 

2377 if isinstance(value, node_classes.AssignName): 

2378 stmt = value.statement() 

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

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

2381 continue 

2382 result.append(value) 

2383 

2384 if not result: 

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

2386 

2387 return result 

2388 

2389 @lru_cache(maxsize=1024) # noqa 

2390 def _metaclass_lookup_attribute(self, name, context): 

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

2392 attrs = set() 

2393 implicit_meta = self.implicit_metaclass() 

2394 context = copy_context(context) 

2395 metaclass = self.metaclass(context=context) 

2396 for cls in (implicit_meta, metaclass): 

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

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

2399 attrs.update(cls_attributes) 

2400 return attrs 

2401 

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

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

2404 

2405 try: 

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

2407 except AttributeInferenceError: 

2408 return 

2409 

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

2411 if not isinstance(attr, FunctionDef): 

2412 yield attr 

2413 continue 

2414 

2415 if isinstance(attr, objects.Property): 

2416 yield attr 

2417 continue 

2418 if attr.type == "classmethod": 

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

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

2421 # from where the attribute is retrieved. 

2422 # get_wrapping_class could return None, so just 

2423 # default to the current class. 

2424 frame = get_wrapping_class(attr) or self 

2425 yield bases.BoundMethod(attr, frame) 

2426 elif attr.type == "staticmethod": 

2427 yield attr 

2428 else: 

2429 yield bases.BoundMethod(attr, self) 

2430 

2431 def igetattr( 

2432 self, 

2433 name: str, 

2434 context: InferenceContext | None = None, 

2435 class_context: bool = True, 

2436 ) -> Iterator[InferenceResult]: 

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

2438 

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

2440 

2441 :returns: The inferred possible values. 

2442 """ 

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

2444 

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

2446 # instance 

2447 context = copy_context(context) 

2448 context.lookupname = name 

2449 

2450 metaclass = self.metaclass(context=context) 

2451 try: 

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

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

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

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

2456 if len(attributes) > 1: 

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

2458 first_scope = first_attr.parent.scope() 

2459 attributes = [first_attr] + [ 

2460 attr 

2461 for attr in attributes 

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

2463 ] 

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

2465 setter = None 

2466 for function in functions: 

2467 dec_names = function.decoratornames(context=context) 

2468 for dec_name in dec_names: 

2469 if dec_name is util.Uninferable: 

2470 continue 

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

2472 setter = function 

2473 if setter: 

2474 break 

2475 if functions: 

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

2477 last_function = functions[-1] 

2478 attributes = [ 

2479 a 

2480 for a in attributes 

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

2482 ] 

2483 

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

2485 # yield Uninferable object instead of descriptors when necessary 

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

2487 inferred, bases.Instance 

2488 ): 

2489 try: 

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

2491 except AttributeInferenceError: 

2492 yield inferred 

2493 else: 

2494 yield util.Uninferable 

2495 elif isinstance(inferred, objects.Property): 

2496 function = inferred.function 

2497 if not class_context: 

2498 if not context.callcontext and not setter: 

2499 context.callcontext = CallContext( 

2500 args=function.args.arguments, callee=function 

2501 ) 

2502 # Through an instance so we can solve the property 

2503 yield from function.infer_call_result( 

2504 caller=self, context=context 

2505 ) 

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

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

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

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

2510 # instead, then we return the property object 

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

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

2513 # the class itself. 

2514 yield from function.infer_call_result( 

2515 caller=self, context=context 

2516 ) 

2517 else: 

2518 yield inferred 

2519 else: 

2520 yield function_to_method(inferred, self) 

2521 except AttributeInferenceError as error: 

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

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

2524 yield util.Uninferable 

2525 else: 

2526 raise InferenceError( 

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

2528 ) from error 

2529 

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

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

2532 

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

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

2535 will return True. 

2536 

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

2538 """ 

2539 

2540 def _valid_getattr(node): 

2541 root = node.root() 

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

2543 

2544 try: 

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

2546 except AttributeInferenceError: 

2547 try: 

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

2549 return _valid_getattr(getattribute) 

2550 except AttributeInferenceError: 

2551 pass 

2552 return False 

2553 

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

2555 """Return the inference of a subscript. 

2556 

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

2558 

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

2560 :rtype: NodeNG 

2561 

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

2563 ``__getitem__`` method. 

2564 """ 

2565 try: 

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

2567 except AttributeInferenceError as exc: 

2568 if isinstance(self, ClassDef): 

2569 # subscripting a class definition may be 

2570 # achieved thanks to __class_getitem__ method 

2571 # which is a classmethod defined in the class 

2572 # that supports subscript and not in the metaclass 

2573 try: 

2574 methods = self.getattr("__class_getitem__") 

2575 # Here it is assumed that the __class_getitem__ node is 

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

2577 # with more generic inference. 

2578 except AttributeInferenceError: 

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

2580 else: 

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

2582 

2583 method = methods[0] 

2584 

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

2586 new_context = bind_context_to_node(context, self) 

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

2588 

2589 try: 

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

2591 except AttributeError as exc: 

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

2593 # are subscriptable thanks to __class_getitem___ classmethod. 

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

2595 # EmptyNode doesn't have infer_call_result method yielding to 

2596 # AttributeError 

2597 if ( 

2598 isinstance(method, node_classes.EmptyNode) 

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

2600 ): 

2601 return self 

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

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

2604 # ``infer_call_result`` method. 

2605 if not method.callable(): 

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

2607 raise 

2608 except InferenceError: 

2609 return util.Uninferable 

2610 

2611 def methods(self): 

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

2613 

2614 :returns: The methods defined on the class. 

2615 :rtype: Iterator[FunctionDef] 

2616 """ 

2617 done = {} 

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

2619 for meth in astroid.mymethods(): 

2620 if meth.name in done: 

2621 continue 

2622 done[meth.name] = None 

2623 yield meth 

2624 

2625 def mymethods(self): 

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

2627 

2628 :returns: The methods defined on the class. 

2629 :rtype: Iterator[FunctionDef] 

2630 """ 

2631 for member in self.values(): 

2632 if isinstance(member, FunctionDef): 

2633 yield member 

2634 

2635 def implicit_metaclass(self): 

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

2637 

2638 This will return an instance of builtins.type. 

2639 

2640 :returns: The metaclass. 

2641 :rtype: type 

2642 """ 

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

2644 

2645 def declared_metaclass( 

2646 self, context: InferenceContext | None = None 

2647 ) -> SuccessfulInferenceResult | None: 

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

2649 

2650 An explicit declared metaclass is defined by passing the 

2651 ``metaclass`` keyword argument in the class definition line. 

2652 

2653 :returns: The metaclass of this class, 

2654 or None if one could not be found. 

2655 """ 

2656 for base in self.bases: 

2657 try: 

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

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

2660 self._metaclass = baseobj._metaclass 

2661 self._metaclass_hack = True 

2662 break 

2663 except InferenceError: 

2664 pass 

2665 

2666 if self._metaclass: 

2667 try: 

2668 return next( 

2669 node 

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

2671 if not isinstance(node, util.UninferableBase) 

2672 ) 

2673 except (InferenceError, StopIteration): 

2674 return None 

2675 

2676 return None 

2677 

2678 def _find_metaclass( 

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

2680 ) -> SuccessfulInferenceResult | None: 

2681 if seen is None: 

2682 seen = set() 

2683 seen.add(self) 

2684 

2685 klass = self.declared_metaclass(context=context) 

2686 if klass is None: 

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

2688 if parent not in seen: 

2689 klass = parent._find_metaclass(seen) 

2690 if klass is not None: 

2691 break 

2692 return klass 

2693 

2694 def metaclass( 

2695 self, context: InferenceContext | None = None 

2696 ) -> SuccessfulInferenceResult | None: 

2697 """Get the metaclass of this class. 

2698 

2699 If this class does not define explicitly a metaclass, 

2700 then the first defined metaclass in ancestors will be used 

2701 instead. 

2702 

2703 :returns: The metaclass of this class. 

2704 """ 

2705 return self._find_metaclass(context=context) 

2706 

2707 def has_metaclass_hack(self) -> bool: 

2708 return self._metaclass_hack 

2709 

2710 def _islots(self): 

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

2712 if "__slots__" not in self.locals: 

2713 return None 

2714 try: 

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

2716 except InferenceError: 

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

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

2719 return None 

2720 for slots in slots_attributes: 

2721 # check if __slots__ is a valid type 

2722 for meth in ITER_METHODS: 

2723 try: 

2724 slots.getattr(meth) 

2725 break 

2726 except AttributeInferenceError: 

2727 continue 

2728 else: 

2729 continue 

2730 

2731 if isinstance(slots, node_classes.Const): 

2732 # a string. Ignore the following checks, 

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

2734 if slots.value: 

2735 yield slots 

2736 continue 

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

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

2739 continue 

2740 

2741 if isinstance(slots, node_classes.Dict): 

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

2743 else: 

2744 values = slots.itered() 

2745 if isinstance(values, util.UninferableBase): 

2746 continue 

2747 if not values: 

2748 # Stop the iteration, because the class 

2749 # has an empty list of slots. 

2750 return values 

2751 

2752 for elt in values: 

2753 try: 

2754 for inferred in elt.infer(): 

2755 if not ( 

2756 isinstance(inferred, node_classes.Const) 

2757 and isinstance(inferred.value, str) 

2758 ): 

2759 continue 

2760 if not inferred.value: 

2761 continue 

2762 yield inferred 

2763 except InferenceError: 

2764 continue 

2765 

2766 return None 

2767 

2768 def _slots(self): 

2769 

2770 slots = self._islots() 

2771 try: 

2772 first = next(slots) 

2773 except StopIteration as exc: 

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

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

2776 return exc.args[0] 

2777 return None 

2778 return [first, *slots] 

2779 

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

2781 @cached_property 

2782 def _all_slots(self): 

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

2784 

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

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

2787 variable, then this function will return a None. 

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

2789 :rtype: list(str) or None 

2790 """ 

2791 

2792 def grouped_slots( 

2793 mro: list[ClassDef], 

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

2795 for cls in mro: 

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

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

2798 continue 

2799 try: 

2800 cls_slots = cls._slots() 

2801 except NotImplementedError: 

2802 continue 

2803 if cls_slots is not None: 

2804 yield from cls_slots 

2805 else: 

2806 yield None 

2807 

2808 try: 

2809 mro = self.mro() 

2810 except MroError as e: 

2811 raise NotImplementedError( 

2812 "Cannot get slots while parsing mro fails." 

2813 ) from e 

2814 

2815 slots = list(grouped_slots(mro)) 

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

2817 return None 

2818 

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

2820 

2821 def slots(self): 

2822 return self._all_slots 

2823 

2824 def _inferred_bases( 

2825 self, 

2826 context: InferenceContext | None = None, 

2827 *, 

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

2829 ): 

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

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

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

2833 # example: 

2834 # 

2835 # class SomeSuperClass(object): pass 

2836 # class SomeClass(SomeSuperClass): pass 

2837 # class Test(SomeClass): pass 

2838 # 

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

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

2841 # only in SomeClass. 

2842 

2843 if context is None: 

2844 context = InferenceContext() 

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

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

2847 return 

2848 

2849 for stmt in self.bases: 

2850 try: 

2851 baseobj = _infer_last(stmt, context) 

2852 except InferenceError: 

2853 continue 

2854 if isinstance(baseobj, bases.Instance): 

2855 baseobj = baseobj._proxied 

2856 if not isinstance(baseobj, ClassDef): 

2857 continue 

2858 if baseobj is self or baseobj in base_classes: 

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

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

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

2862 baseobj = self._resolve_circular_base(stmt, context) 

2863 if baseobj is None: 

2864 continue 

2865 if not baseobj.hide: 

2866 yield baseobj 

2867 else: 

2868 yield from baseobj.bases 

2869 

2870 def _resolve_circular_base( 

2871 self, 

2872 stmt: nodes.NodeNG, 

2873 context: InferenceContext | None, 

2874 ) -> ClassDef | None: 

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

2876 

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

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

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

2880 non-circular ClassDef. 

2881 """ 

2882 inf_context = copy_context(context) 

2883 try: 

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

2885 if isinstance(inferred, bases.Instance): 

2886 inferred = inferred._proxied 

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

2888 return inferred 

2889 except InferenceError: 

2890 pass 

2891 return None 

2892 

2893 def _compute_mro( 

2894 self, 

2895 context: InferenceContext, 

2896 *, 

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

2898 ): 

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

2900 return [self] 

2901 

2902 inferred_bases = list( 

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

2904 ) 

2905 bases_mro = [] 

2906 base_chain |= {self} 

2907 for base in inferred_bases: 

2908 if base in base_chain: 

2909 continue 

2910 

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

2912 bases_mro.append(mro) 

2913 

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

2915 unmerged_mro = clean_duplicates_mro(unmerged_mro, self, context) 

2916 clean_typing_generic_mro(unmerged_mro) 

2917 return _c3_merge(unmerged_mro, self, context) 

2918 

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

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

2921 

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

2923 :rtype: list(NodeNG) 

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

2925 :raises InconsistentMroError: A class' MRO is inconsistent 

2926 """ 

2927 return self._compute_mro(context=context) 

2928 

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

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

2931 

2932 :returns: The boolean value of this node. 

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

2934 """ 

2935 return True 

2936 

2937 def get_children(self): 

2938 if self.decorators is not None: 

2939 yield self.decorators 

2940 

2941 yield from self.bases 

2942 if self.keywords is not None: 

2943 yield from self.keywords 

2944 yield from self.type_params 

2945 

2946 yield from self.body 

2947 

2948 @cached_property 

2949 def _assign_nodes_in_scope(self): 

2950 children_assign_nodes = ( 

2951 child_node._assign_nodes_in_scope for child_node in self.body 

2952 ) 

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

2954 

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

2956 """The node's frame node. 

2957 

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

2959 :class:`ClassDef` or :class:`Lambda`. 

2960 

2961 :returns: The node itself. 

2962 """ 

2963 return self 

2964 

2965 def _infer(self, context: InferenceContext | None = None) -> Generator[ClassDef]: 

2966 yield self